C language include preprocessing command and multi-file compilation

The #include preprocessing command is almost the preprocessing command that we will encounter when we first come into contact with C, but I am not very clear now, so I will try to understand it this time.

1. Basic use of #include preprocessing directives

Preprocessing directives can insert source code content from elsewhere into the current location, and can identify a piece of program code that will only be compiled under certain conditions.

The #include preprocessing directive tells the preprocessor to insert the contents of the specified header file into the corresponding location of the preprocessor directive.

There are two ways to specify the header file to be inserted:

1 #include <filename>
2 #include "filename"

The first format (angle bracket format) should be used when including standard link headers, or headers provided by the implementation version. example:

1 #include <math.h>     // Prototype of math function

If you want to include files developed by the program, use the second format (the double-quote format).

The file inserted by the #include preprocessing directive usually has a file extension of .h, and the inside of the file is nothing more than function prototypes , macro definitions , and type definitions .

The most common forms used in header files are as follows:

Literal constants - for example, EOF, NULL, and BUFFSIZE (standard I/O buffer size) in stdio.h.

Macro functions - for example, getc(stdin) is usually defined with getchar(), and getc() is often used to define more complex macros,

The header file ctype.h usually contains macro definitions for the ctype family of functions.

Function declarations - For example, the string.h header file (strings.h on some older systems) contains function declarations for the string function family. In ANSI C and later standards,

Function declarations are all in the form of function prototypes.

Structure template definition - The FILE structure is used in standard I/O functions. This structure contains information about files and file buffers. The FILE structure is in the header file stdio.h.

Type Definition - Standard I/O functions take a pointer to FILE as an argument. Usually stdio.h uses #define or typedef to define FILE as a pointer to a structure.

Similarly, size_t and time_t types are also defined in header files.

These definitions can be used by any source code file as long as the #include preprocessor directive is used.

Macros can be used in #include preprocessor directives. If a macro is used, the correct #include preprocessor must be generated after the macro is superseded.


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

When the above code is processed, the _DEBUG_ macro is defined, then the preprocessor will insert the content of myProject_dbg.h, otherwise it will insert the content of myProject.h.

2. How to find header files in preprocessing

Different C language implementations have their own search paths to find a way to find the files required by the #include preprocessing directive. The "case sensitivity of filenames" is at the discretion of the implementation version.

For files included using angle brackets, the preprocessor will usually search in a specific system path, such as /usr/local/include and /usr/include on Unix systems.

For files specified with double quotes ("filename"), the preprocessor will usually look in the current directory, which is usually the directory containing other raw files for the program.

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

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

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

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

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

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

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

1 #include<stdio.h>

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

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

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

四、避免多次包含

1 #ifndef INCFILE_H_
2 #define INCFILE_H_
3 /* incfile.h实际的内容写在这里 */
4 #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将它们声明为外部变量。

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

1 int number = 0;
2 double in_to_mm = 2.54;

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

1 extern int number;
2 extern double in_to_mm;

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

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

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

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

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

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

八、多文件编译

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

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


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325807027&siteId=291194637