呉云峰の部屋

          IntoTheWind,intoTheRain
          posts - 3, comments - 0, trackbacks - 0, articles - 5

          在c/c++語言中的頭文件其實是為了搜尋對應的類型和函數信息的
          比如在頭文件中可以聲明一個函數,但這個函數可能定義在任何地方
          比如一個靜態庫或者動態導入庫lib中,或者可以直接以原代碼的方式寫在cpp/c文件中
          頭文件提供的服務叫做類型映射(phototype)

          函數在c/c++語言中也是一種類型
          函數在聲明的時候其實僅僅是說明了對應的函數調用協議,函數名稱和參數類型
          這樣就可以明確的指導編譯器如何創建這個函數對應的調用代碼
          而尋找這個函數的工作交給了鏈接器
          注意 編譯器在vc里邊叫做cl,鏈接器在vc里邊叫做link。

          cl負責生成obj,每一個cpp/c文件會生成一個obj文件
          這個obj里邊包含了直接由c/cpp源程序所生成的匯編代碼,這個c/cpp文件需要查找的符號(這個后面再說)

          link其實和咱們上微機原理匯編課的時候用的Link很像
          他負責將每一個obj中的符號查找表中的東西轉換為一個地址
          這個地址就是最后編譯完成后的exe文件的函數對應這個函數的入口地址。

          符號,就是這個函數或者對象通過編譯器后所產生的名稱
          在c語言中一個符號由這個函數或者這個對象的名稱和這個函數的調用協議組成
          這也是為什么c語言不支持重載的原因
          (還記得重載吧?重載就是參數類型或個數不同,
          參數和個數都相同的叫做重寫,重寫只能用在類的函數的繼承中)
          而c++會在每一個函數的前后添加一堆用于表示這個函數的調用方法所屬類型,名字空間和調用參數類型等的大量字符
          用來區分每一個函數
          比如一個函數
          int Test(); 根據不同的命名方法可能被不同的命名
          如果到定義了extern "C" 如下:
          extern "C" int Test();
          這個函數就被vc編譯器按c語言的方式命名為 _Test
           其中的前劃線 _表示這個函數的調用協議為__cdecl
           Test就是這個函數的名稱
          如果使用vc編譯器直接編譯這個函數int Test();
          他就被當作int __cdecl Test(void); 編譯成  ?Test@@YAHXZ
           其中的? 和 @@YAHXZ都是編譯器加上去的
           ? 和 @@YAH 是用來表示調用協議的
           其中的H為返回值是int
           X表示沒有參數。
           Z是函數名稱結束修飾

          調用協議
          指函數的參數傳遞使用的方式
          有__cdecl __fastcall __stdcall __thiscall 等
          __cdecl 是c語言的調用方式
          __fastcall 使用寄存器傳遞參數
          __stdcall 使用棧傳遞參數,并且其壓棧順序為從右到左,由被調用函數來清除棧
          __thiscall 是類對象的方法調用方式,這種調用方式不能直接由程序指定

          如果一個函數的是被實現在cpp或者c文件中的時候,就必須保證這個cpp或者c文件所產生的符號與這個函數的聲明所產生的
          符號相同,否則鏈接器在鏈接的時候就會發生無法找到符號的錯誤

          如何保證這個符號是相同的呢?
          只要在cpp或者c文件中包含這個頭文件
          或者如果能保證所生成的符號相同則根本就不用h文件也能工作
          比如如下程序

          // 這是main.cpp文件的東西
          int Test();

          int main()
          {
           Test();
          }

          // 這是test.cpp文件的東西
          int Test()
          {
           return 1;
          }
          這里就完全沒有用頭文件

          注意事項:
          1。如果是使用了c文件作為函數的載體而編譯器為微軟的vc,
          需要在h文件中添加extern "C"通知編譯器使用c的命名規則
          最好使用判斷
          #ifdef __cplusplus
          extern "C" {
          #endif

          ...
          函數聲明
          ...

          #ifdef __cplusplus
          }
          #endif
          這樣在c編譯器上也能使用這個頭文件了
          2。c/cpp文件與頭文件名稱無關
          3。如果一個頭文件中定義了一個結構或者類,那么在h文件中最好使用防止多次包含的
          #ifndef __XXX__H__
          #define __XXX__H__

          ...
          //類或結構定義
          class a
          {
           ...
          };
          ...

          #endif
          這里的XXX就是這個頭文件的名稱,這個名稱是可以隨便起的,只要不起重。
          但c/cpp文件中不需要添加這些東西

          或者如果使用了vc6作為編譯器它支持
          #pragma once
          在h文件的最前面寫上這句話就可以保證這個頭文件只會被處理一遍


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 大悟县| 双辽市| 辽中县| 田林县| 收藏| 寻乌县| 诸暨市| 彰化市| 凌源市| 施秉县| 大埔区| 北宁市| 徐州市| 德化县| 嫩江县| 霍州市| 丽水市| 高密市| 上杭县| 江阴市| 通州区| 新兴县| 渑池县| 阿拉善左旗| 河源市| 朝阳县| 独山县| 益阳市| 虹口区| 德昌县| 军事| 类乌齐县| 成武县| 阿图什市| 沂源县| 汉阴县| 德钦县| 库尔勒市| 栾城县| 阜平县| 镶黄旗|