Posted on 2007-12-13 09:42
ゴ ウンホウ 阅读(175)
评论(0) 编辑 收藏
在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文件的最前面写上这句话就可以保证这个头文件只会被处理一遍