第一句子大全,网罗天下好句子,好文章尽在本站!

C语言头文件被include后都发生了什么?为何不能在头文件定义变量

时间:2012-05-24

头文件里一般存放公开的函数原型,数据类型等内容,其他模块需要使用这些函数或者数据类型时,只需包含相应头文件即可

友情提示:本文共有 2437 个字,阅读大概需要 5 分钟。

头文件是C语言的一个重要组成部分,这种类型的文件名一般以 .h 结尾,h 表示 header,因此被称为“头文件”。头文件里一般存放公开的函数原型,数据类型等内容,其他模块需要使用这些函数或者数据类型时,只需包含相应头文件即可。

相信读者大都使用过C语言的头文件,不过还是有可能对其理解不透彻,这会导致读者在遇到一些问题时不知道如何解决。本文将较为详细的讨论C语言头文件的特点,并在此基础上,分析几个初学者常会跳进的“陷阱”,以及相应的解决办法。

C语言的#include语法

头文件通常与C语言的#include 语法配合使用,意为“将头文件内容包含进来”,例如在 t.c 文件里写下这段C语言代码:

编译器在编译这段C语言代码之前,会有一个“预处理”的过程,在此过程中,stdio.h 里的内容被展开到 t.c 文件里。事实上,在终端输入 gcc -E 命令即可查看预处理后的C语言代码:

# gcc -E t.c

可见,编译器在预处理阶段会将 stdio.h 的内容展开到 main() 函数之前。事实上,如果创建 str.h 文件,并在其中写入“hello worldn”,我们甚至可以写出下面这样的C语言代码:

输入 gcc -E 查看编译器预处理后的C语言代码,会发现编译器将 str.h 文件里的内容“hello worldn”展开到 printf() 里了,此时 printf(#include “str.h”); 等价于 printf(“hello worldn”);,所以编译并执行这段C语言代码,得到如下输出:

# gcc t.c# ./a.out hello world

到这里,相信读者已经发现,在C语言程序开发中,#include实际上就是把头文件里的内容复制到对应的位置。

避免C语言代码重复包含头文件

今天在我的交流群里有个小伙伴在编写C语言程序时,遇到了自己无法解决的错误。为了讨论主题,我把他的问题简化:创建 test.h 文件,并在其中定义一个全局变量:

// test.h 文件int global_val = 0;

然后创建 t1.c 文件,使用 #include 包含该头文件,相应的C语言代码如下,请看:

编译这段C语言代码,小伙伴发现编译器报错了:

错误信息提示变量 global_val 被重复定义,但是小伙伴查看自己的代码,发现只有 test.h 里一处定义了变量 global_val,这让他很迷惑。

小伙伴会感到迷惑,主要是因为对C语言的“头文件”机制理解不够深入,他认为只有 test.h 文件一处定义变量 global_val,不可能会导致“重复定义”错误的。

实际上,按照我们上面的分析, #include 包含头文件并没有什么特别的,它只是将头文件里的内容复制到 #include 处而已。知道了这一点,再看小伙伴的C语言代码,就一切明了了:他不小心(也有可能故意)包含了 test.h 文件两次,所以 test.h 文件里的内容会被赋值到 main() 函数之前两次,就相当于:

int global_val = 0;int global_val = 0;int main()...

这当然会引发“重复定义”的错误。解决错误的办法很简单,避免头文件被重复包含即可,所以删去一个#include "test.h" 就可以了。不过,我们能够轻易发现头文件被重复包含,是因为这里的代码很简单。如果C语言代码再复杂一点,或者多几层嵌套,就比较难发现头文件被重复包含了。

例如,test1.h 包含了 test2.h 文件,test2.h 文件包含了 test.h 文件。这种情况下,t.c 文件同时包含 test1.h 和 test.h 文件,一样会引起 test.h 文件被重复包含的。

在实际的C语言项目开发中,头文件一般都要加上预编译条件语句,比较常用的有 #ifdef ,#ifndef等。例如,将 test.h 文件做如下修改:

上述C语言代码中的 #ifndef 和 #define 配合,可避免该头文件在同环境中被重复包含。所以即使 t.c 文件中写了多次 #include "test.h"文件,编译器也不会再报错:

编译并执行这段C语言代码,可得如下输出:

# gcc t2.c# ./a.out val = 1

初学者感到头疼的问题

有的读者知道使用 #ifdef 等条件编译语句避免头文件在同环境被重复包含,但还是有可能写出有问题的C语言程序。下面这个问题也是群里小伙伴提出的,为了讨论主题,我对其做了精简:小伙伴在 test.h 文件和 t1.c 文件的工程基础上,新建了 t2.c 文件,其中 t2.c 文件的内容如下:

显然,t2.c 文件也包含了 test.h 头文件,并使用了其中定义的 global_val 变量。然后小伙伴在将 add_val() 函数的原型加入 test.h 头文件里:

接着,小伙伴在 t1.c 文件里调用了 add_val() 函数,相关C语言代码如下,请看:

写好这些C语言代码后,发现编译报错了,依然是重复定义的错误,小伙伴感到非常迷惑。为什么写了预编译语句,还是出现这种错误呢?

答案其实很简单,预编译条件语句仅作用于同一环境。t1.c 和 t2.c 文件属于两个模块,因此#ifndef 不能避免 test.h 文件被 t1.c 和 t2.c 同时包含,这就会导致 int global_val = 0; 在整个C语言程序中有两处定义,编译器自然会报错。

小结

本文较为详细的介绍了C语言中头文件的性质,并在此基础上,分析了初学者常遇到的两个问题。应明白,在实际的C语言项目开发中,很少有程序员会在头文件里定义全局变量。作为延伸,如果本文中 test.h 文件里的 global_val 定义为 static 变量,那么编译就不会报错了。究竟为什么,以及加上 static 会带来什么样的变化,留给读者自己思考了。

本文如果对你有帮助,请点赞收藏《C语言头文件被include后都发生了什么?为何不能在头文件定义变量》,同时在此感谢原作者。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
你眼中的C语言世界

你眼中的C语言世界

...环境。编译的过程可以这样理解:一个C语言包含多个源文件组成,每个源文件有很多可读的文本组成,这些文本使用具体的编程语言。C编译器把每一个源文件翻译成机器语言,也就是计算机系统上执行程序所需要的指令。然后...

2023-06-19 #经典句子

浅谈C/C++的预编译语句

浅谈C/C++的预编译语句

...言#预编译语句是C/C++语言的一大特色,大部分的C/C++程序文件中都有预编译语句,比如说#include这样的语句就是一行预编译语句。预编译语句以符号"#"开头,如同字面意思一样,通常用于程序的预处理,也就是指定了编译器在正...

2023-08-26 #经典句子

用实例代码带你回顾C语言基础运算符和表达式知识点汇总

用实例代码带你回顾C语言基础运算符和表达式知识点汇总

...个程序改正过后的代码如下:#include /*头文件*/int main() /*主函数*/{ double x; /*定义变量双精度变量*/int i; /*定义整型变量*/x=3.6...

2016-03-21 #经典句子

计算机二级考试C语言高频考点

计算机二级考试C语言高频考点

...令:以“#”开头的语句2.C程序的生成过程(1)C程序是先由源文件经编译生成目标文件,然后经过连接生成可执行文件。(2)源程序的扩展名为.c,目标程序的扩展名为.obi,可执行程序的扩展名为.exe。【考点2】常量、变量和数据类型1.标...

2023-06-19 #经典句子

C语言指针

C语言指针

...的。变量与指针的本质2.1 内存地址我们编写一个程序源文件之后,编译得到的二进制可执行文件存放在电脑的硬盘上,此时它是一个静态的文件,一般称之为程序。当这个程序被启动的时候,操作系统将会做下面几件事情:把...

2023-08-13 #经典句子

所有程序员都必须知道的语言!C++之基本结构

所有程序员都必须知道的语言!C++之基本结构

...处理命令。例如:#include<iostream.h>表示本程序包含有头文件iostream.h。以上所述的有关函数、输入/输出流等概念将在以后的章节中详细介绍。好了,本文到此结束。如果对编程、计算机、程序员方面感兴趣的话,欢迎私信联系...

2023-11-22 #经典句子

使用C语言编写程序对数据进行排序

使用C语言编写程序对数据进行排序

...以数组方式由小到大输出。#include //加载标准输入输出头文件。int main()//定义整型主函数。{int a,b,c,x,y,t,sz[10];//定义整型变量和数组变量。for(a=1;a

2009-06-29 #经典句子

C语言可以在执行语句中间定义变量吗?

C语言可以在执行语句中间定义变量吗?

想了解更多精彩内容,快来关注lemoontree在C++中(.CPP文件是按C++标准编译的),按照语法我们可以把变量定义在任何位置,只要在用到变量前对该变量进行声明定义就行了,位置不做特别要求,比如函数中任意位置出现的For循环...

2023-06-19 #经典句子