在c/c++語言中的頭文件其實(shí)是為了搜尋對應(yīng)的類型和函數(shù)信息的
比如在頭文件中可以聲明一個函數(shù),但這個函數(shù)可能定義在任何地方
比如一個靜態(tài)庫或者動態(tài)導(dǎo)入庫lib中,或者可以直接以原代碼的方式寫在cpp/c文件中
頭文件提供的服務(wù)叫做類型映射(phototype)
函數(shù)在c/c++語言中也是一種類型
函數(shù)在聲明的時候其實(shí)僅僅是說明了對應(yīng)的函數(shù)調(diào)用協(xié)議,函數(shù)名稱和參數(shù)類型
這樣就可以明確的指導(dǎo)編譯器如何創(chuàng)建這個函數(shù)對應(yīng)的調(diào)用代碼
而尋找這個函數(shù)的工作交給了鏈接器
注意 編譯器在vc里邊叫做cl,鏈接器在vc里邊叫做link。
cl負(fù)責(zé)生成obj,每一個cpp/c文件會生成一個obj文件
這個obj里邊包含了直接由c/cpp源程序所生成的匯編代碼,這個c/cpp文件需要查找的符號(這個后面再說)
link其實(shí)和咱們上微機(jī)原理匯編課的時候用的Link很像
他負(fù)責(zé)將每一個obj中的符號查找表中的東西轉(zhuǎn)換為一個地址
這個地址就是最后編譯完成后的exe文件的函數(shù)對應(yīng)這個函數(shù)的入口地址。
符號,就是這個函數(shù)或者對象通過編譯器后所產(chǎn)生的名稱
在c語言中一個符號由這個函數(shù)或者這個對象的名稱和這個函數(shù)的調(diào)用協(xié)議組成
這也是為什么c語言不支持重載的原因
(還記得重載吧?重載就是參數(shù)類型或個數(shù)不同,
參數(shù)和個數(shù)都相同的叫做重寫,重寫只能用在類的函數(shù)的繼承中)
而c++會在每一個函數(shù)的前后添加一堆用于表示這個函數(shù)的調(diào)用方法所屬類型,名字空間和調(diào)用參數(shù)類型等的大量字符
用來區(qū)分每一個函數(shù)
比如一個函數(shù)
int Test(); 根據(jù)不同的命名方法可能被不同的命名
如果到定義了extern "C" 如下:
extern "C" int Test();
這個函數(shù)就被vc編譯器按c語言的方式命名為 _Test
其中的前劃線 _表示這個函數(shù)的調(diào)用協(xié)議為__cdecl
Test就是這個函數(shù)的名稱
如果使用vc編譯器直接編譯這個函數(shù)int Test();
他就被當(dāng)作int __cdecl Test(void); 編譯成 ?Test@@YAHXZ
其中的? 和 @@YAHXZ都是編譯器加上去的
? 和 @@YAH 是用來表示調(diào)用協(xié)議的
其中的H為返回值是int
X表示沒有參數(shù)。
Z是函數(shù)名稱結(jié)束修飾
調(diào)用協(xié)議
指函數(shù)的參數(shù)傳遞使用的方式
有__cdecl __fastcall __stdcall __thiscall 等
__cdecl 是c語言的調(diào)用方式
__fastcall 使用寄存器傳遞參數(shù)
__stdcall 使用棧傳遞參數(shù),并且其壓棧順序?yàn)閺挠业阶螅杀徽{(diào)用函數(shù)來清除棧
__thiscall 是類對象的方法調(diào)用方式,這種調(diào)用方式不能直接由程序指定
如果一個函數(shù)的是被實(shí)現(xiàn)在cpp或者c文件中的時候,就必須保證這個cpp或者c文件所產(chǎn)生的符號與這個函數(shù)的聲明所產(chǎn)生的
符號相同,否則鏈接器在鏈接的時候就會發(fā)生無法找到符號的錯誤
如何保證這個符號是相同的呢?
只要在cpp或者c文件中包含這個頭文件
或者如果能保證所生成的符號相同則根本就不用h文件也能工作
比如如下程序
// 這是main.cpp文件的東西
int Test();
int main()
{
Test();
}
// 這是test.cpp文件的東西
int Test()
{
return 1;
}
這里就完全沒有用頭文件
注意事項(xiàng):
1。如果是使用了c文件作為函數(shù)的載體而編譯器為微軟的vc,
需要在h文件中添加extern "C"通知編譯器使用c的命名規(guī)則
最好使用判斷
#ifdef __cplusplus
extern "C" {
#endif
...
函數(shù)聲明
...
#ifdef __cplusplus
}
#endif
這樣在c編譯器上也能使用這個頭文件了
2。c/cpp文件與頭文件名稱無關(guān)
3。如果一個頭文件中定義了一個結(jié)構(gòu)或者類,那么在h文件中最好使用防止多次包含的
#ifndef __XXX__H__
#define __XXX__H__
...
//類或結(jié)構(gòu)定義
class a
{
...
};
...
#endif
這里的XXX就是這個頭文件的名稱,這個名稱是可以隨便起的,只要不起重。
但c/cpp文件中不需要添加這些東西
或者如果使用了vc6作為編譯器它支持
#pragma once
在h文件的最前面寫上這句話就可以保證這個頭文件只會被處理一遍