0.标准头文件的特点:具有幂等性、相互独立性、和文件级别的声明等同。
库中的每一个可见函数都占据着单独的C源文件;
每个隐藏的名字都以一个下划线和一个大写字母开头;
库中隐藏的函数和数据对象通常占据以x开头命名的C源文件;
在一个库的可见函数定义中,函数名被一对括号括住,所有这样的函数声明都会被相应的头文件中的宏定义所掩盖,所以这对括号阻止了翻译程序识别和展开宏。
1. 头文件<assert.h>唯一的目的就是提供宏assert的定义。
2.可以通过在程序的某些地方定义宏NDEBUG来改变assert的展开方式,如果NDEBUG在包含了<assert.h>d的源文件中某处被定义为宏名,那么宏assert就被直接定义为:#define assert(ignore) ((void) 0)
3.宏assert应该作为一个宏而不是一个实际的函数来实现。如果为了访问一个实际函数而禁止宏定义,那么这种行为是未定义的。
4.源文件的名字和源文件的行数分别是预定义的宏_FILE_和_LINE_的值。
5.宏assert没有任何返回值。
6.对<assert.h>来说,每次包含它的时候它的行为都会发生变化。该头文件会改变assert的定义来适应NDEBUG当前的定义状态。
7.要打开断言可以这样写:
#undef NDEBUG
#include <assert.h>
要关闭断言,可以这样写:
#define NDEBUG
#include <assert.h>
8.宏NDEBUG可以重定义,这是良性重定义。
9.宏不能直接调用库的任何输出函数(为了保证库头文件之间的独立性)。
10.一个程序如果不包含某个头文件,就可以定义宏来对该头文件中的任意名字重新命名。
11.宏必须能扩展为一个void类型的表达式。
12.宏应该可以扩展为一个有效并且紧凑的代码。
13.预处理运算符
#(单井号) ——字符串化运算符(后边没有参数时就是空指令,它没有任何作用)
##(双井号) ——连接运算符
#@
——字符化运算符
14.内置宏__LINE__没有扩展成字符串字面量,它变成了一个十进制常量。把它转换成适当的形式需要一个额外的处理层。那要通过向头文件中添加两个隐藏的宏_STR和_VAL来实现。其中一个宏用它的十进制常量扩展来取代__LINE__,另一个是把十进制常量转化为一个字符串字面量。忽略其中任一个都会得到字符串字面量"__LINE__",而不是你想要的结果。
15.当宏参数是另一个宏的时候 ,需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开. (对于理解"_VAL(__LINE__)"是很重要的!!!)
PS:宏_VAL和_STR的命名可以是任意的