头文件没啥好说的,无非就是" "和< >的区别,这估计只要是学过C/C++的人都明白。现在的编译器对头文件的包含顺序没有要求,但老的C实现则不一样。当然,我们现在无需关心头文件顺序了。
我们为啥要包含头文件呢?头文件里面有定义嘛,使用任何函数之前都必须定义该函数。所以我们并不强求包含头文件,只要你自己在库函数使用之前定义该函数,有些编译器还会自动的添加标准库定义。但是呐,我们还是推荐将头文件添加上去,头文件里面有时候还会包含类型定义、常量定义、宏定义。
在linux环境下,gcc会在以下路径中查找头文件:/usr/local/include;libdir/gcc/target/version/include;/usr/target/include;/usr/include。C++程序还会查找libdir/../include/c++/version。target是一个标准名指的是你的GCC编译的目录。version则是版本号了。
Gcc可以使用-Idir参数指定头文件目录。-nostdinc选项则会阻止GCC搜寻某个头文件目录。-nostdinc在编译系统内核的时候会很有用,因为系统内核不使用标准C库,内核使用的是源码里面自带的库文件。注意:-nostdinc对-Idir无效。
我们可以在GCC -I选项参数的任意位置放置-I-标志,在-I-标志之前的目录我们只查找由引号标记的头文件,-I-标记之后的目录则查找所有头文件。你要是想指定查找由引号标记的头文件目录,我们不推荐使用-I-标记,而是使用用-iquote选项。
如果很不幸,你有一个目录叫"-"。这种情况下你得使用-I./- 和-I-区别开来。
为了防止头文件被包含多次,你需要使用宏的条件语句:
/*File foo. */
#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN /*
*the entire file
*/ #endif /*!FILE_FOO_SEEN*/
这是一种很常见的结构被称为wrapper #ifndef
针对C++的优化:C++会记住wrapper #ifndef的布尔状态,如果第二次重复包含同一个头文件,C++会直接忽略该文件(直接不扫描)。wrapper #ifndef之外的注释并不会影响这种优化。
一般在系统头文件中像FILE_FOO_SEEN这种宏名以“__”开头,所以在个人头文件中建议使用“_”开头,以防冲突。
C++中我们有两种方式防止头文件被重复包含,但我们都不推荐使用。
一、#import
#import实际上来自于Objective-C的标准做法,是#include的一个变种。#import包含头文件,但最多只包含一次。我们不推荐的理由是:#import放权给了用户,用户必须知道一个头文件必须只包含一次。但我们的宗旨是把这一任务交给头文件来完成。
二、#pragma once
#pragma once 指令使用在头文件里面,符合我们的宗旨。但是并不是所有的预处理器都能识别该指令。如果考虑可移植性,该指令显然是要移除的。
在预处理过程中,预处理器会告诉编译器标记符号的位置,实际上就是哪个文件哪一行。编译出错的时候编译器往往会有提示,告诉你错误可能在那个文件哪一行。这是通过语法解析器在解析代码的时候自动插入的$line 宏
在glibc中有的函数可能只是一个宏定义,也可能是一个实实在在的函数。这对我们的程序没什么影响。使用宏定义函数的理由是:可以产生内联扩展,这要比函数调用快的多,缺点是不易于debug(原因是编译器给出的行号不是宏展开的地方,而是宏定义的地方)。基于宏定义函数的缺点,你可能想避免使用宏定义函数。你有两种方式来避免:
1、给函数名添加括号
2、使用#undef预处理指令
比如abs函数,既有宏定义也有函数实现。
#include <stdlib.h>
abs(i); //根据编译器选择可能是宏,也可能是函数调用
(abs)(i); //一定是函数调用 #undef abs
abs(i); //一定是函数调用