c语⾔中.c与.h⽂件详解
很多⼈对C语⾔中的 “⽂件包含”都不陌⽣了,⽂件包含处理在程序开发中会给我们的模块化程序设计带来很⼤的好处,通过⽂件包含的⽅法把程序中的各个功能模块联系起来是模块化程序设计中的⼀种⾮常有利的⼿段。
⽂件包含处理是指在⼀个源⽂件中,通过⽂件包含命令将另⼀个源⽂件的内容全部包含在此⽂件中。在源⽂件编译时,连同被包含进来的⽂件⼀同编译,⽣成⽬标⽬标⽂件。
很多⼈再初学时都会对这个很晕,怎么写⽂件件? 怎么包含才能避免重定义? 等等问题。。。
其实这个只要了解了⽂件包含的基本处理⽅法就可以对⽂件包含有⼀个很好的理解与应⽤了,下来我们⼀起来看⼀下:
⽂件包含的处理⽅法:
⾸先⼤家需要清楚:
(1) 处理时间:⽂件包含也是以”#”开头来写的(#include ), 那么它就是写给预处理器来看了, 也就是说⽂件包含是会在编译预处理阶段进⾏处理的。
(2) 处理⽅法:在预处理阶段,系统⾃动对#include命令进⾏处理,具体做法是:降包含⽂件的内容复制到包含语句(#include )处,得到新的⽂件,然后再对这个新的⽂件进⾏编译。
抓住这两点,那么这个东东就没有什么难的了。。。
⼀般情况下⽂件包含分为两种:包含.h⽂件 和 包含.c⽂件
1. 当然对于这两情况也都是按照上⾯说的⽅法来处理的。呵呵,这个肯定是没得说的.
1. 包含.c⽂件 和编译多⽂件程序 是不同的。
多⽂件程序: 是在源⽂件编译时把多个⽂件进⾏编译、连接在⼀起⽣成⼀个可执⾏⽂件。
包含.c⽂件: 按照我们上边的说法则是把多个⽂件合并为⼀个⽂件进⾏编译。
接下来通过例⼦看⼀下:
(1)包含.c⽂件:
1: //file1: main.c
2: #include
3: #include “fun.c”
4: int main()
5: {
6: int a=5,b=19;
7: c = a;
8: sun(a,b);
9: printf(“c=%d\n”,c);
10: return 0;
11: }
12: //end of file1
1: //file2: fun.c
2: int c=0;
3: void sun(int a, int b)
4: {
5: printf(“a+b=%d\n”,a+b);
6: c=0;
7: printf(“c=%d\n”,c);
8: }
9: //end of file2
10:
这个例⼦是采⽤ 包含.c⽂件 的⽅法实现的。
在编译时,直接去编译main.c⽂件,预处理器会先把fun.c⽂件中的内容复制到main.c中来,然后再对新的main.c进⾏编译。 编译命令:
gcc main.c -o main
可以看到,这⾥并没有对fun.c进⾏编译,但还是⽣成了最终的main可执⾏程序。
也可以通过命令来观察⼀下预处理的结果:
编译命令:
gcc -E main.c -o main.cpp
在main.cpp⽂件末尾可以看来下⾯⼀段代码:
1: //main.cpp⽂件中
2: 931 # 2 “main.c” 2
3: 932 # 1 “fun.c” 1
4: 933 //注意这⾥是fun.c⾥边的内容
5: 934 int c=0;
6: 935 void sun(int a, int b)
7: 936 {
8: 937 printf(“a+b=%d\n”,a+b);
9: 938 c=0;
10: 939 printf(“c=%d\n”,c);
11: 940 }
12: //这⾥是main函数
13: 941 # 3 “main.c” 2
14: 942 int main()
15: 943 {
16: 944 int a=5,b=19;
17: 945 c = a;
18: 946 printf(“c=%d\n”,c);
19: 947 sun(a,b);
20: 948 printf(“c=%d\n”,c);
21: 949 return 0;
22: 950 }
可见,其实就是将fun.c⽂件中的内容添加到了main函数之前,然后对新的⽂件进⾏编译,⽣成最终的可执⾏程序。
(2)编译多⽂件程序:
同样是上边的例⼦,把main.c中“ #include “fun.c” ”注释掉,加上⼀句:“extern int c;”因为 c 变量在另外⼀个⽂件(fun.c)中定义。
c语言和c++区别1: //file1: main.c
2: #include
3: //#include “fun.c” //注释掉
4: extern int c; //添加这⼀句
5: int main()
6: {
7: int a=5,b=19;
8: c = a;
9: sun(a,b);
10: printf(“c=%d\n”,c);
11: return 0;
12: }
13: //end of file1
14:
15:
16: //file2: fun.c
17: int c=0;
18: void sun(int a, int b)
19: {
20: printf(“a+b=%d\n”,a+b);
21: c=0;
22: printf(“c=%d\n”,c);
23: }
24: //end of file2
这次如果还是按照上⾯的⽅法只编译main.c的话就会出错,因为变量c和函数sun并没有在main.c中定义,所以编译时需要将fun.c⼀起编译:
编译命令:
gcc -c main.c -o main.o #编译main.c
gcc -c fun.c -o fun.o #编译fun.c
gcc main.o fun.o -o main #⽤main.o fun.o⽣成main
到这⾥⼤家应该已经理解包含.c⽂件和多⽂件程序的本质区别了~~~
好了,⼤家不防想想这两种⽅法的优缺点,这⾥就只写不⾜之处了:
1. 包含.c⽂件的⽅法: 容易产⽣”重定义”,⼤家想想如果⼀个⼯程中有多个⽂件都同时包含了某⼀个件,那么这个被包含⽂件的内容就会被复制到多个⽂件中去,也就相当于每个包含该⽂件的⽂件中都定义被包含⽂件中的变量和函数,这样在链接时就会产⽣”重定义”错误。
2. 多⽂件分开编译的⽅法: 这个⽐较好,不容易出现”重定义”之类的问题,这也是我们最常⽤的⼀种⽅法,但是并不是像上⾯这个例⼦中这样直接去⽤,⽽是使⽤”头⽂件”将各个.c⽂件联系起来。
上边这个例⼦⼤家会发现,在main.c中需要加上“extern int c;”这样⼀句声明,如果包含的⽂件较多?如果全局变量较多?…这个我们可以省掉吗?回答是肯定的!⽅法就是给它写上⼀个头⽂件。
接下来看⼀下使⽤头⽂件的来实现这个例⼦的⽅法:
1: //file1: main.c
2: #include
3: #include “fun.h” //fun.c修改为fun.h
4: //extern int c; //这⾏也不要了
5: int main()
6: {
7: int a=5,b=19;
8: c = a;
9: sun(a,b);
10: printf(“c=%d\n”,c);
11: return 0;
12: }
13: //end of file1
1:
2: //file2: fun.c
3: #include “fun.h”
4: int c=0; //变量c的定义
5: void sun(int a, int b) //函数sun()的定义
6: {
7: printf(“a+b=%d\n”,a+b);
8: c=0;
9: printf(“c=%d\n”,c);
10: }
11: //end of file2
1: //file3: fun.h
2: extern int c; //把c声明为外部可⽤的
3: void sun(int a, int b); //sun()函数的声明
4: //end of file3
这样再看⼀下,在要⽤到fun.c中定义的函数或变量的⽂件中只要包含fun.h⽂件就可以了,是不是这样呵呵,当然是了。。。
预处理时会把fun.h中的内容复制到包含它的⽂件中去,⽽复制的这些内容只是声名,不是定义,所以它被复制再多份也不会出现”重定义”的错误。。。
呵呵,对,就是这样,这就是头⽂件给我们再来的好处。
前⾯说了头⽂件的⽅法也是模块化程序设计中的⼀种⾮常有利的⼿段。
把同⼀类功能写到⼀个.c⽂件中,这样可以把他们划为⼀个模块,另外再对应的写上⼀个.h⽂件做它的声明。这样以后再使⽤这个模块时只需要把这两个⽂件添加进⼯程,同时在要使⽤模块内函数或变量的⽂件中包含.h⽂件就可以了。
举个很实际的例⼦,在单⽚机、ARM或其他嵌⼊式开发中,每⼀个平台可能本⾝都有多种不同的硬件模块,使⽤时需要去写相应的驱动程序,这样就可以把各个硬件模块的驱动程序作为⼀个模块(⽐如lcd驱动对对应lcd.c和lcd.h,IIC驱动对应I2C.c和I2C.h等),当具体使⽤到某个模块时,只需要在将对应的.c和.h⽂件添加进⼯程,并在⽂件中包含对就的.h⽂件即可。
所以关于头⽂件的写法个⼈总结以下⼏点:
(1) 对应的.c⽂件中写变量、函数的定义
(2) 对应的.h⽂件中写变量、函数的声明
(3) 如果有数据类型的定义 和 宏定义 ,请写的头⽂件(.h)中
(4) 头⽂件中⼀定加上#ifndef…#define….#endif之类的防⽌重包含的语句
(5) 模块的.c⽂件中别忘包含⾃⼰的.h⽂件
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论