关于c语言与c++语言的头文件

ysbcg 2005-12-25 03:16:50
在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文件的最前面写上这句话就可以保证这个头文件只会被处理一遍
...全文
776 点赞 收藏 31
写回复
31 条回复
ysbcg 2005年12月28日
谢谢 Mortimer3309(Mortimer) () 信誉:100 的总结
回复 点赞
terryjwf 2005年12月26日
楼主你能活100
回复 点赞
__Zealot__ 2005年12月26日
学习心得——对C程序员而言:
1、 函数也是一种类型
2、 函数的声明是指定了函数的调用协议,函数名称,参数类型
3、 通过函数声明可以指导编译器如何创建函数对应的调用代码,而函数具体的执行代码 由链接器完成
4、 编译生成OBJ文件,包含的信息有:需要查找的符号表,汇编代码
链接将符号表中的“东西”转换成地址——应该是相对地址,用于程序的动态装入
5、 自定义的函数在经过编译以后生成的“符号”,有两部分:名称和调用协议

建议,楼主再把思路理理,完整的讲述下由源代码编辑到生成可执行文件的过程,再由此来给头文件作用定位

好帖,UP
回复 点赞
ruodeer 2005年12月26日
up
虽然以前看过一些
但是没有这么系统
回复 点赞
lumber 2005年12月26日
多谢楼主
回复 点赞
armman 2005年12月26日
先顶后学习!
回复 点赞
wxrwan 2005年12月26日
回复 点赞
ouyh12345 2005年12月26日
挺好的,顶
回复 点赞
dch4890164 2005年12月26日
佩服!!
真的很佩服!!
不仅仅是因为你的讲解!!
更佩服您的人格!!
现实当中您这样的人已经不多了!!!
回复 点赞
ovol 2005年12月26日

回复 点赞
sinopsw 2005年12月26日
准确地说,头文件只对编译器有意义。更准确地说是预编译器。
回复 点赞
minico 2005年12月26日
受教了
回复 点赞
ALittleTired 2005年12月26日
好帖,谢谢楼主!
回复 点赞
tubowangxy 2005年12月26日
cool!
学习了!
回复 点赞
dadi0189 2005年12月26日
把细节发出来供大家学习,精神可佳日后必得贵子!
回复 点赞
hu_vane 2005年12月26日
回复 点赞
Cicinho 2005年12月25日
呵呵,不错不错。这是我第一次看到比较详细的解释。
谢谢楼主了。
回复 点赞
cunsh 2005年12月25日
mark
收藏.
回复 点赞
hupingke 2005年12月25日
学习
顶!
回复 点赞
tashigan 2005年12月25日
顶!

c/cpp文件与头文件名称无关
所以一个c文件中的函数有时也可以拷到其它的c文件中去编译
回复 点赞
发动态
发帖子
C++ 语言
创建于2007-09-28

3.1w+

社区成员

24.8w+

社区内容

C++ 语言相关问题讨论,技术干货分享
社区公告
暂无公告