#include_include是什么意思_#include

对于一个程序员来说,只要写过一个“hello world!”的C程序,就不得不用到#指令,因为没有# “stdio.h”,就不可能输出”hello world!”字符串,它除了可以在程序头部包含库函数头文件的用法,其实还有很多强大的用法。今天我们就重新认识一下这个貌不惊人的预处理指令。(如果有经验的程序员,可以直接跳过基础部分,为了照顾不同程度的读者,基础用法也做详细介绍。)

文件搜索机制:双引号模式

本意就是包含的意思,把另一个文件包含进当前文件中,有两种模式:

双引号模式分为绝对路径和相对路径两种模式。绝对路径是指从根目录开始按指定路径搜索头文件,“盘符:”、“盘符:\”、“盘符:/”就是表示下的根目录,形如:

在linux环境,只能是正斜杠/并且没有盘符的概念,在环境里,这三种是等价的用法。

相对路径则是指从源文件所在的当前路径开始搜索头文件,“.”就是当前目录,“..”就是当前目录的父目录,最好只用正斜杠“/”用来连接目录和目录,或目录和文件,虽然也可以用反斜杠,但最好养成和linux风格的一致性,因为在实际开发中,几乎都是用相相对路径的方法来搜索头文件,很好用绝对路径的。好处显而易见,就是灵活性强,源文件目录可以随意移动,而不改变头文件的搜索机制。举例如下:

# “./file.ext”# “file.ext”# “.//file.ext”# “/file.ext”# “../file.ext”# “..//file.ext”# “../..//file.ext”

“.”表示源文件所在的当前目录,“file.ext”表示文件,两者用正斜杠连接,表示当前目录下的文件,注意,很多教程,甚至资深程序员说“./”是当前目录,是严重错误的!正斜杠只是连接符号。

第一种和第二种是等价的用法,第二种只是第一种的简化写法。第三种和第四种用法是等价的,都表示当前目录的“子目录”下的文件,第四种也省略了当前目录的写法。第五种表示要使用的头文件在父目录下,第六种表示在兄弟目录(父目录的其它子目录),第七种表示文件在“叔叔目录”下。

文件搜索机制:尖括号模式

一般情况下,如果包含的是程序员自定义的头文件,要用到双引号模式的搜索机制,而且最好要用相对目录模式。但是如果是C语言标准的库函数头文件,就用尖括号模式,会更加方便。在尖括号内直接放入要包含的文件名即可。比如:

尖括号本身就表示一个特定的目录,所以不用像双引号模式那样显式地指定头文件路径。在系统中,安装不同的c语言IDE开发环境,标准库函数的头文件路径都会不同,在同一个开发环境中,不同的库函数所在的目录也可能不一样,当然也可以一样。

比如在 2022中,以我个人的电脑为例,.h和.h都被安装在:

C: Files (x86) Kits10\10.0.22621.0ucrt

而.h头文件就被安装在:

C: Files 2022\VCToolsMSVC14.37.32822

而在我个人电脑安装的环境里,.h和.h被安装在:

C: Files\CLion 2023.3.2binmingw-w64-

而.h头文件被安装在:

C: Files\CLion 2023.3.2binmingwlibgcc-w64-13.1.0

我们不用关心系统提供的标准头文件的具体路径,直接尖括号访问即可。当然,这些头文件的目录可以被修改成新的位置,但是必须要在对应的IDE里设置,而不是简单的移走,我要反复强调的是,千万不要轻易这样干。

头文件查找机制的进阶思考

1)

双引号模式:预处理器会先在指定的路径寻找头文件,如果寻找不到会再到编译器自身的路径中去寻找(存放在IDE或操作系统的环境变量中)。

尖括号:预处理只会到编译器默认的目录中去寻找。

2)

程序员自定义的头文件也可以使用尖括号模式访问,比如# 。有两种方法,比如可以直接放在编译器的默认目录里,比如目录,或者其他目录,比如stdio.h所在的目录里都可以。也可以在IDE中设置一个新的路径的环境变量也可以。

类似的上面情况,也可以反过来把标准库函数用双引号模式访问,比如# “stdio.h”,这两种颠倒或混用的做法,我强烈不建议。

如果你需要把源代码编译成静态链接库lib或者动态链接库dll,提供给其他项目使用,就需要放到标准库的目录里,用尖括号访问是合适的,如果想了解如何用C语言生成静态或动态库,可以看我专栏的相关文章。

否则,尖括号和双引号可以让代码阅读者,能够一眼分辨出哪些是自定义头文件,哪些是标准头文件,混用的情况下,会让维护者迷惑,不是一种良好的编程实践。

包含防护机制

头文件的包含防护机制,非常重要,我在介绍预处理指令# once、条件编译指令的用法时,都提到过。

指令就是将头文件的全部内容原样复制到当前文件里,如果一个头文件被间接或直接的多次被“包含”到当前源文件,编译的时候必然报错。比如:

//code.c
#include “myheader.h”
#include “other.h”
//other.h
#include “nyheader.h”

这是最简单的例子,在other.h里间接的重复包含了.h文件的内容。为了避免一个头文件被多次重复包含进同一个源文件的问题,要么使用在头文件的顶部插入一条# once指令,表示这个头文件在同一个源文件里只能包含一次,关于#指令的俄详细用法,可以参见《段誉和语言》的相关文章,要么使用条件编译指令,条件编译指令的用法就成为头文件包含防护机制,使用起来也很简单,就是在头文件的顶部插入如下一段代码:

#ifndef _MY_HEADER_H
#define _MY_HEADER_H
......
#endif/*my_header.h*/

要替换为头文件的文件名的大写字母,当这个头文件第一次被包含时,标识符 是没有被定义的,所以# 语句为真,里面内容会被包含进去,省略号指代的是头文件内容。当这个头文件第二次或多次被包含时,因为# 已经被定义了,所以# 为假,就不被执行,就不会被重复包含。关于条件编译指令的详细用法,可以看《段誉和语言》的相关文章。

的本质:到底什么是“文件包含”

本意就是包含的意思,把另一个文件包含进当前文件中,实际上只要是文件内容是文本格式,都可以被包含进来,比如.c、.h、.txt都是文本格式,就可以被包含进来。打个比方,让下面这段代码:

#include 
int main(){
int x,y;
x=3;
y=2;
printf(“%dn”,x+y);
return0;
}

我们完全可以把其中部分代码放进另一个文件,比如file.txt,然后包含进来:

#include 
intmain(){
int x,y;
#include “file.txt”
return 0;
}
//file.txt
x = 3;
y = 2;
printf(“%dn”,x+y);

现实中,没人会这样写,这个例子就是为了演示的本质局势简单的包含功能。而且照样可以编译通过。但是,我们一般约定俗成的吧源代码保存为后缀为c的文件,函数的声明保存后缀为h的文件。

巧用这个特性,我们可以有很多使用有趣的用法,举两个例子分享一下。

化简数组

比如我们定义了一个数组,元素非常多,放在源程序里影响看代码,可以放在另外的文本文件里保存,既让代码清爽,又易于修改和维护。比如:

//src.c
char *namelst[] = {
#include “names.txt”
};
//names.txt
“Tom”,
“Jack”,
......
“vicky”

通过# “names.txt”,会让src.c文件清爽易读,而又不用去关心数组元素的具体内容。这里有个细节要说到,那就是:

#指令必须要独占一行

用#包含文件时,一定要单独占一行,也就是#语句当前行的前后必须都是空白。因为预处理器会将被包含的文件的全部内容从当前行的下一行开始插入,前后不为空白,就会出错。

之前我在另一篇讲#用法时,提到将大量数组元素包进文本文件,有个粉丝测试发现int a[]={# “items.txt”};报错,编译不通过,就是这个原因。

调试程序

有时候我们需要对几个代码块进行调试,一般都是通过条件编译指令来控制哪一个代码块生效,但是如果用就会非常方便和省事,比如:

int main(){
....
//功能1:
//#include “func1.txt”
//功能2:
//#include “func2.txt”
....

当我们需要调试代码块1的时候,只要去掉# “func1.txt”前面的注释就可以了,不挑食的就注释掉,调试的就去掉注释,非常的方便和间接,否则就必须要提哦啊见编译指令,或者用注释成块掉。我这个例子非常简单,反应不出来的作用,在这种简单场景下,注释更方便,在复杂的情况下,就会显出的省事。

还有更重要的一个用法,有时候我们会需要再一个项目里包含多个源文件,每个源文件都有一个main函数,因为main函数都对应某个功能的程序,编译肯定不给通过。

我们常规且简单的做法,就是一个main函数的源文件都单独为一个项目,要么通过cmake来控制在同一时间只能有一个main函数来启用。这个用法在我的另一篇文章里也有详细的介绍过。

今天我介绍另一个更加巧妙的办法,因为cmake是微软 IDE的弱项,功能支持比较差,用起来还不如CLION里用的方便,但是有时候又需要使用VS,因为编译器调试能力非常优秀,所以我经常这样干:建立一个源文件:

#include “main01.h”
#include “main02.h”
#include “main03.h”
......

每个main文件里都有一个main函数,需要调试的去掉注释,不调试的就注释掉。非常简单好用,我放一个我的截图如下:

#include_#include_include是什么意思

这个截图是我的项目文件夹的组织结构,一个源.c的文件作为开关控制要编译的头文件,每个.h文件都对应一个要调试的小功能。下面的图2就是源文件的内容:

#include_#include_include是什么意思

下面的图就是其中一个要调试的功能,这里举的是B.h 这个文件,其实后缀改为.c也没问题,主要是为了不和源文件混淆。B.h的内容如下,注意做上角的红箭头,指明了是B.H文件:

#include_include是什么意思_#include

本篇介绍就写到这里,其他有趣的玩法,请继续关注我的技术专栏:段誉和语言。

段誉,写于合肥。

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666

声明:1、本内容转载于网络,版权归原作者所有!2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!