#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. About Markdown -- 进入Markdown园子

    起初也就是打算简单一些Markdown在编辑Blog方面的一些常用操作和注意事项,没想到,一下没刹住,毫无防备地闯进了这个好趣的园子-. 1. 认识 Markdown HTML(HyperText M ...

  2. sqlserver 巧用REVERSE和SUBSTRING实现lastindexof

    原文:sqlserver 巧用REVERSE和SUBSTRING实现lastindexof select REVERSE(SUBSTRING(REVERSE(testFixtureNumber),0, ...

  3. OVF? OVA? VMDK? – File Formats and Tools for Virtualization

    I recently worked on a project to create a “virtual appliance” for one of our customers. They have a ...

  4. RS-232

    RS-232 锁定 同义词 rs232一般指RS-232 本词条由“科普中国”百科科学词条编写与应用工作项目 审核 . 个人计算机上的通讯接口之一,由电子工业协会(Electronic Industr ...

  5. 【转】C++调用Matlab的.m文件

    原文地址     Matlab是一个强大的数学计算/仿真工 具,其内置了很多实用的现成的函数,而且我们经常也自己定义很多m函数.但在很多情况下,我们不得不使用VC编程.那么,如何在VC中利用matla ...

  6. VUE -- 用组件上传文件和用xmlrequest上传

    xmlrequest: sendForm(str, types) { var form = this.$refs.ipas_form; var oOutput = document.querySele ...

  7. Docker删除全部镜像和容器

    杀死所有正在运行的容器 docker kill $(docker ps -a -q) 删除所有已经停止的容器 docker rm $(docker ps -a -q) 删除所有未打 dangling ...

  8. win10 virtualenv

    一 创建新虚拟环境 virtualenv appiumenv 二 激活 appiumenv\Scripts\activate 注意是正斜杠,

  9. Python连接mongodb提取部分字段内数据并写入txt文件

    #coding=utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8') from pymongo import MongoClient ...

  10. testng执行报错:org.testng.TestNGException: Cannot find class in classpath

    org.testng.TestNGException: Cannot find class in classpath 解决办法:project->clean 再次执行正常运行