#include预处理命令几乎使我们在第一次接触C的时候就会碰到的预处理命令,可我现在还不怎么清楚,这次争取一次搞懂。

一、#include预处理指令的基本使用

预处理指令可以将别处的源代码内容插入到现在的位置,可以标识出某一段程序代码只有在特定条件下才会被编译。

#include预处理指令告诉预处理器,将指定头文件的内容插入到预处理器指令的相应位置。

指定插入头文件有两种方式:

 #include <filename>
#include "filename"

当要包含标准链接头文件,或者实现版本所提供的头文件时,应该使用第一种格式(尖括号的格式)。范例:

 #include <math.h>    // 数学函数的原型

如果想包含程序所开发出的文件,则使用第二种格式(双引号的格式)。

#include预处理指令所插入的文件,通常文件扩展名是.h,并且文件内部不外乎是函数原型宏定义类型定义

头文件中最常用的形式如下:

字面常量——例如,stdio.h中的EOF、NULL和BUFFSIZE(标准I/O缓冲区大小)。

宏函数——例如,getc(stdin)通常用getchar()定义,而getc()经常用于定义较复杂的宏,

头文件ctype.h通常包含ctype系列函数的宏定义。

函数声明——例如,string.h头文件(一些旧的系统中是strings.h)包含字符串函数系列的函数声明。在ANSI C和后面的标准中,

函数声明都是函数原型形式。

结构模板定义——标准I/O函数中使用FILE结构,该结构包含了文件和文件缓冲区的信息,FILE结构在头文件stdio.h中。

类型定义——标准I/O函数使用指向FILE的指针作为参数。通常stdio.h用#define或typedef把FILE定义为指向结构体的指针。

类似的,size_t和time_t类型也定义在头文件中。

只要使用#include预处理指令,这些定义就可以被任何源代码文件所使用。

可以在#include预处理指令中使用宏。如果使用宏,此宏被取代之后,必须生成正确的#include预处理指令。

 #ifdef _DEBUG_
#define MY_HEADER "myProject_dbg.h"
#else
#define MY_HEADER "myProject.h"
#endif
#include MY_HEADER

上述代码被处理时,_DEBUG_宏时有定义的,那么预处理器会插入myProject_dbg.h的内容,否则会插入myProject.h的内容。

二、预处理如何找到头文件

不同的C语言实现版本有自己的搜索路径,想办法找出#include预处理指令所要求包含的文件。"文件名是否区分大小写"由实现版本自行决定。

对使用尖括号的方式包含的文件,预处理器通常会在特定的系统路径下搜索,例如Unix系统的/usr/local/include和/usr/include。

对于使用双引号指定的文件("filename")来说,预处理器通常会在当前目录下寻找,通常也就是包含此程序其他原始文件的目录。

如果在当前目录下没有找到,那么预处理也会搜索系统的include路径。filename可以包含路径。如果真的包含路径,则预处理只会到此目录中寻找。

也可以为#include预处理器指定自己的是搜索路径,做法可以使用编译器命令行选项,或在环境变量中加入搜索路径,这样的环境变量常常被命名为INCLUDE。

具体做法参见所使用编译器的文件说明。

三、嵌套的#include预处理指令

#include预处理器可以被嵌套,也就是说,通过#include预处理器而插入的源代码文件本身也可以有#include预处理指令。

预处理器允许至多15层的嵌套包含(nested include)。因为头文件有时候会被彼此互相包含,很容易发生相同的一个文件被多次包含的情况。

例如,假设myProject.h包含如下代码:

 #include<stdio.h>

如果源代码文件包含下面的#include预处理指令,就会包含两次stdio.h,一次直接包含,一次间接包含:

 #include <stdio.h>
#include "myProject.h"

然而,可以轻易的利用条件式编译的预处理指令避免多次包含相同的文件。

四、避免多次包含

 #ifndef INCFILE_H_
#define INCFILE_H_
/* incfile.h实际的内容写在这里 */
#endif /* INCFILE_H_ */

第一出现包含incfile.h的预处理指令时,INCFILE_H_宏是没有被定义的。

预处理因此插入#ifndef和#endif之间的内容,包含了“定义INCFILE_H_宏”的预处理指令。

一旦后续包含incfile文件,#ifndef的定义就不会成真,预处理会忽略#ifndef和#endif之间的内容。

五、定义自己的头文件

可以定义自己的头文件,通常其扩展名是.h,可以使用操作系统允许的任何文件名。

理论上不一定对头文件使用扩展名.h,但是它是大多数C程序员惯用的扩展名,所以最好使用它。

头文件不能包含实现代码,即可执行代码。头文件可以包含声明,但不能包含函数定义或初始化的全局数据。

函数定义和初始化的全局数据应该放在扩展名为.c的源文件中。

可以在头文件中放置函数原型、struct类型定义、符号定义、extern语句和typedef。

一个常用的技巧是创建一个头文件,它含有程序中所有函数的原型以及类型声明。

然后讲这些作为一个独立的单元来管理,并放在程序源文件的开头。

如果源文件包含多个头文件,必须避免信息的重复,这个可以利用条件编译避免。

任何文件的内容都可以通过#include这种方法包含到程序中,只要在引号中指定文件名即可。

六、管理多个源文件

复杂程序总是包含多个源文件和头文件。

理论上,可以使用一个#include指令把另一.c源文件的内容包含在当前的.c文件中,但是通常这是不必要的,甚至不合理。

在.c文件中应该只使用#include指令包含头文件。当然,头文件常常包含#include指令,以包含其他的头文件。

复杂程序中的.c文件一般包含一组相关的函数。在编译开始前,预处理器会插入#include指令指定的每个头文件的内容。

编译器从每个.c源文件中创建有个对象文件。所以的.c文件都编译好之后,链接器就会把对象文件合并到有个可执行的模块中。

如果C编译器带有交互式的开发环境,则通常提供项目功能,即一个项目包含并管理组成程序的所有源文件和头文件。

这通常意味着,不必担心在创建可执行文件的过程中把文件存储在什么地方,开发环境会处理。

但对于大型应用程序,最好自己创建一个适当的文件夹结构,而不是让IDE把所有的文件都放在同一个文件夹中。

七、外部变量

一个由几个源文件组成的程序通常需要使用在其他文件内定义的全局变量。

为此,可以使用关键字extern将它们声明为外部变量。

例如,使用如下语句在其他文件内定义了一个全局变量(是在任何函数之外):

 int number = ;
double in_to_mm = 2.54;

然后,要在一个函数中访问访问,可以使用一下语句指定这些变量是外部的:

 extern int number;
extern double in_to_mm;

这些语句不会创建这些变量,只会告诉编译器,这些名称在文件外定义,但可以应用于源文件的其他地方。

指定为extern的变量在程序的外部声明和定义,通常实在另一个源文件中。

如果要让当前文件中的所有函数都可以访问这些外部变量,必须在文件的开头,在任何函数的定义之前将它们声明为外部变量。

程序是由几个文件组成的,可以把所有已初始化的全局变量放在一个文件的开头,将所有的extern语句放在另一个头文件中。

使用include语句包含该头文件,所有的extern语句就合并到需要访问这些变量的程序文件中。

每个全局变量在文件中只能定义一次,但是,全局变量可以根据需要在许多文件中声明为外部变量。

八、多文件编译

一般都不会用#include直接包含C文件,而是用头文件把需要的东西(其他源文件中的函数、变量、类型)放在一起,引用头文件。

编译的时候,命令要链接多个源文件。

C语言include预处理命令与多文件编译的更多相关文章

  1. 【C语言入门教程】2.8 C 语言的预处理命令

    预处理命令是在程序编译阶段进行执行的命令,用于编译与特定环境相关的可执行文件.预处理命令扩展了 C 语言,本节将选择其中一些常用的预处理命令进行讲解. 2.8.1 宏替换命令 宏替换命令的作用类似于对 ...

  2. C语言之预处理命令

    /**************************************************************************** Title:C之预处理命令 Time:201 ...

  3. C语言之预处理命令与用typedef命名已有类型

    预处理命令 主要是改进程序设计环境,以提高编程效率,不属于c语言本身的组成部分,不能直接对它们进行编译,必须在对 程序编译之前,先对程序中的这些特殊命令进行“预处理”.比如头文件. 有以下三类:宏定义 ...

  4. C语言:预处理命令总结

    预处理指令是以#号开头的代码行,# 号必须是该行除了任何空白字符外的第一个字符.# 后是指令关键字,在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行 ...

  5. C语言的预处理命令

    C语言编译器处理时经过的第一个步骤是预处理,就是从.c文件处理为.i文件.在预处理时编译器做了一些展开替换的处理. 1>头文件展开,即将#include "stdio.h"类 ...

  6. C预编译, 预处理, C/C++头文件, 编译控制,

    在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...

  7. C++命令行多文件编译(g++)

    在刚开始学Java时用命令行进行编译代码.而C++一直在用IDE, 这次尝试下命令行编译.vs下也可以用cl.exe.link.exe等命令来进行编译 但这次是通过安装MinGW来学习命令编译,主要用 ...

  8. Linux C编程学习之C语言简介---预处理、宏、文件包含……

    C的简介 C语言的结构极其紧凑,C语言是一种模块化的编程语言,整个程序可以分割为几个相对独立的功能模块,模块之间的相互调用和数据传递是非常方便的 C语言的表达能力十分强大.C语言兼顾了高级语言和汇编语 ...

  9. C语言 Include指令(引用头文件)

    #include "one.h" #include "two.h" int main(int argc, const char * argv[]) { one( ...

随机推荐

  1. Lucas定理模板【bzoj2982】【combination】

    (上不了p站我要死了,侵权度娘背锅) Description LMZ有n个不同的基友,他每天晚上要选m个进行[河蟹],而且要求每天晚上的选择都不一样.那么LMZ能够持续多少个这样的夜晚呢?当然,LMZ ...

  2. linux安装mysql数据库(5.7之前的版本)

    到mysql官网下载mysql编译好的二进制安装包   解压32位安装包: 进入安装包所在目录,执行命令:tar mysql-5.6.17-linux-glibc2.5-i686.tar.gz   复 ...

  3. 如何避免CSS :before、:after 中文乱码

    问题: 在进行页面开发时,经常会使用:before, :after伪元素创建一些小tips,但是在:before或:after的content属性使用中文的话,会导致某些浏览器上出现乱码. 解决方案: ...

  4. python定时执行方法

    1  time.sleep import time for i in range(5): print(i) time.sleep(10) 2 用shed import time import sche ...

  5. Google Protocol Buffer安装编译及使用

    近期玩了玩谷歌的Protocol Buffer.以下就简介下 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准.眼下已经正在使用的 ...

  6. 在elasticsearch里如何高效的使用filter

    今天在做查询category的时候,遇到一个问题,查询出来的cateogry为food,fun的形式.但是我需要的只是food或者fun 不包含逗号. 开始想着在aggs后再做过滤,这样有些麻烦.遂在 ...

  7. 【Hadoop】Hadoop mr wordcount基础

    1.基本概念 2.Mapper package com.ares.hadoop.mr.wordcount; import java.io.IOException; import java.util.S ...

  8. elasticsearch 聚合时 类型转换错误

    有一个域 inp_type本来为Interger类型,但有的存储了string类型; 执行这个域的聚合时, "aggs":{ "level2":{ " ...

  9. Tomcat 启动或者发布项目时提示Publishing failed:Resource /xxxx does not exist

    解决方法: 刷新一下项目,有可能是磁盘文件和Eclipse项目中文件不一致造成的. 重新启动eclipse 删除tomcat server 重新发布下即可

  10. ElasticSearch Java Api-删除索引

    删除可以是删除整个索引库,也可以根据文档id删除索引库下的文档,还可以通过query查询条件删除所有符合条件的数据. 一.删除整个索引库 下面的例子会删除indexName索引: DeleteInde ...