C程序中关于c和h的包含问题

发布者:光明2599最新更新时间:2015-05-04 来源: 51hei关键字:C程序  包含问题 手机看文章 扫描二维码
随时随地手机看文章
首先声明一下:下面文章都是网络搜索结果,本人浮躁的心情决对没有时间这样仔细地解释问题的,所以我要感谢一下原作者,辛苦了!由于学STM32的关系,需要重点理解“看似没有调用,实际上却包含了”的技巧,这个技巧本人在没有读过文章之前还确实不理解。

*********************************************************************************************************************************************************************
 
很多人对C语言中的 “文件包含”都不陌生了,文件包含处理在程序开发中会给我们的模块化程序设计带来很大的好处,通过文件包含的方法把程序中的各个功能模块联系起来是模块化程序设计中的一种非常有利的手段。

       文件包含处理是指在一个源文件中,通过文件包含命令将另一个源文件的内容全部包含在此文件中。在源文件编译时,连同被包含进来的文件一同编译,生成目标目标文件。

    很多人再初学时都会对这个很晕,怎么写文件件? 怎么包含才能避免重定义? 等等问题。。。
 
    其实这个只要了解了文件包含的基本处理方法就可以对文件包含有一个很好的理解与应用了,下来我们一起来看一下:
 
  文件包含的处理方法:

   首先大家需要清楚:

  (1) 处理时间:文件包含也是以"#"开头来写的(#include ), 那么它就是写给预处理器来看了, 也就是说文件包含是会在编译预处理阶段进行处理的。

  (2) 处理方法:在预处理阶段,系统自动对#include命令进行处理,具体做法是:降包含文件的内容复制到包含语句(#include )处,得到新的文件,然后再对这个新的文件进行编译。
 
  抓住这两点,那么这个东东就没有什么难的了。。。

  一般情况下文件包含分为两种:包含.h文件 和 包含.c文件
1. 当然对于这两情况也都是按照上面说的方法来处理的。呵呵,这个肯定是没得说的.

2. 包含.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 ",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 ",a+b); 6: c=0; 7: printf("c=%d ",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 ",a+b); 9:938 c=0; 10: 939 printf("c=%d ",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 ",c); 19: 947 sun(a,b); 20: 948 printf("c=%d ",c); 21: 949 return 0; 22: 950 }
可见,其实就是将fun.c文件中的内容添加到了main函数之前,然后对新的文件进行编译,生成最终的可执行程序。
 
(2)编译多文件程序:
同样是上边的例子,把main.c中“ #include "fun.c" ”注释掉,加上一句:“extern int c;”因为 c 变量在另外一个文件(fun.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 ",c); 11: return0; 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 ",a+b); 21: c=0; 22: printf("c=%d ",c); 23: } 24: //end of file2[page]
 
这次如果还是按照上面的方法只编译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 ",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 ",a+b);
8: c=0;
9: printf("c=%d ",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文件即可。
关键字:C程序  包含问题 引用地址:C程序中关于c和h的包含问题

上一篇:未来设计电子产品时会考虑的问题
下一篇:单片机防止电磁干扰的方法

推荐阅读最新更新时间:2024-03-16 14:00

labview 发布EXE中包含共享变量的相关问题解决
RT,在生成中报出如下错误。 解决办法:
[测试测量]
西门子PLC程序如何规范编写
一、程序结构统一 OB1:主程序; OB100:初始化程序(无需主程序调用); OB35:100ms(可修改)中断(无需主程序调用),可以调用PID模块; OB80、OB82、OB85、OB86、OB87、OB121、OB122:故障诊断模块(无需主程序调用、无需编程); FC1:系统模式; FC2:输入处理; FC3:输出处理; FC4:运行处理; FC5:停止处理; FC6:手自动切换; FC100:之后用来建立一些可以循环调用的子程序; FC105:系统自带,模拟量输入子程序(可以循环调用); FC106:系统自带,模拟量输出子程序(可以循环调用); modbus通讯(CP341):FB7:P_RCV_RK,FB8:P_SN
[嵌入式]
PLC程序需满足哪些设计要求?
一、完美的PLC程序需满足的设计要求 一套完整的PLC程序,并不仅仅是使系统能够运行起来这么简单,它也需要完整的注释、精良的架构、良好的可扩展性、完备的报警保护系统、运行前的模拟系统。 1. 简单性 使PLC程序尽可能简单。简单的含义就是尽可能的使用标准化的程序框架,尽可能使用简单的指令。 要想程序简单,从大的方面讲,要优化程序结构,用流程控制指令简化程序,从小的方面讲还要用功能强的指令取代功能单一的指令,以及注意指令的安排顺序等。 2. 可读性 要求所设计的程序可读性要好。这不仅便于程序设计者加深对程序的理解,便于调试,而且,还要便于别人读懂你的程序,便于使用者维护。必要时,也可使程序推广。 要使程序可读性好,所设计的程序就
[嵌入式]
PL<font color='red'>C程序</font>需满足哪些设计要求?
图解在keil中的c程序加入汇编语句
  在C51编程中,有时控制一些器件时,需要较强实时性,这时有必要在其中嵌入ASM语句,关于嵌入的方法众多,网上也可以找到相关说明,不过说得并不详细,这里结合本人调试18B20测温程序来说说如何在C中嵌入ASM语句。(题外话:18B20完全用C来写也可以的,这里仅只是作个示范) 看下图,在我们一般建立好的项目文件里,加上C51S.LIB文件。   此主题相关图片如下:   然后,点选我们写好的C程序,点右键,按下面操作进行。   此主题相关图片如下:   此主题相关图片如下:   跟着,我们在程序中建一个函数,里面嵌入ASM语句,在开始和结束,加上这两行,这样中间就可以写ASM语句。   此主题相关图片如下:
[单片机]
图解在keil中的<font color='red'>c程序</font>加入汇编语句
DS18B20温度传感器完整C程序
#include reg51.h #include intrins.h //_nop_();延时函数用 #define Disdata P0 //段码输出口 #define discan P2 //扫描口 #define uchar unsigned char #define uint unsigned int sbit DQ=P3^7; //温度输入口 sbit DIN=P0^0; //LED小数点控制 uint h; uint temp; // // //***********
[单片机]
经典c程序(12/20)
题目:学习使用register定义变量的方法。 1.程序分析: 2.程序源代码: void main() { register int i; int tmp=0; for(i=1;i =100;i++) tmp+=i; printf( The sum is %d ,tmp); } 题目:学习使用external的用法。 1.程序分析: 2.程序源代码: #i nclude stdio.h int a,b,c; void add() { int a; a=3; c=a+b; } void main() { a=b=4; add(); printf( The value of c is equal to %d ,c); } 题
[单片机]
keil c51中C程序的启动过程
汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接。下面看看它和main()函数是如何编译的; //主函数如下; void main(void) { while (1) 这是个无条件空循环。 { } } 把上面的main()函数编译后的汇编程序和反汇编代码整理后对照如下; ?C_C51STARTUP SEGMENT CODE ?PR?main?TESTMAIN SEGMENT CODE ?STACK SEGMENT IDATA RSEG ?STACK
[单片机]
AVR之简单的ADC程序
最近看了ATmega16的ADC,写下这个简单的程序,了解下ADC的简单控制。 程序用Proteus仿真的,用的是ICCAVR7 的编译器(头文件有所不同,自己掂量) 程序的内容是:通过PA1外部电压的读入并动态的显示在3位数码管上 #include iom16v.h #include AVRdef.h unsigned char tab ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//数码管0~9 void display(unsigned char smg); unsigned char temp; void delay_1ms(unsigned int z
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved