标准c语言的环境分为翻译环境和执行环境。
翻译环境的过程:
过程解释:
组成的一个程序的每个源文件通过编译分别转换为目标代码.
链接器同时会引入标准c的库函数,而且它可搜索程序员个人的程序库,将其需要的函数链接到程序中.
每个目标文件由链接器捆绑在一起,形成了一个单一而完整的可执行程序.
预定义符号:
eg.printf("file:%s line:%d\n",_FILE_,_LINE_);
#define定义标识符
使用注意:定义标识符号,最后不要加上;,预编译出错。表达式一定要加括号,避免参数替换出问题操作符优先问题等。
#define替换规则:
1.被定义的符号首先被替换。
2.替换文本被插入程序中原来文本的位置,参数名被数值替换。
3.重复扫描是否还有由#define定义的符号。
注意:宏不能出现递归,预处理器搜索#define定义的符号时,字符串常量并不被搜索。
#和##
#的作用:把参数插入到字符串中,用#,把一个宏参数变成对应的字符串
eg.int i=10;
#define Print(format,value)\
printf("the value of "#value" is "format"\n,value);
Print("%d",i+3);
输出结果: The value of I+3 is 13
##的作用:
把位移它两边的符号合成一个合法的标识符
sum##num--->sumnum(要先有这个合法的标识符)
宏和函数的对比:
展开时机:
1.宏: 在预处理阶段被展开。宏的替换是在编译之前进行的,简单地将宏的名字替换为其定义的代码。
2.函数: 在编译阶段被调用,实际代码在链接时被引入。函数的调用是在运行时进行的。
参数传递:
3.宏: 使用宏可以接受不同数量和类型的参数。参数在宏的定义中直接展开,没有类型检查。
4.函数: 参数的数量和类型在函数声明和定义中是明确定义的,并且会进行类型检查。
类型检查:
5.宏: 不进行类型检查,因为它只是进行简单的文本替换。
6.函数: 有参数类型检查,编译器能够检测到传递给函数的参数是否与函数声明中的参数类型匹配。
代码体积:
7.宏: 可能会导致代码膨胀,因为宏的替换是直接文本替换,可能会生成多个实例的相同代码。
8.函数: 代码体积相对较小,因为函数的代码只有一份,被多次调用。
副作用:
9.宏: 可能会有副作用,因为宏的展开是直接文本替换,可能导致意外的行为。
10.函数: 更容易控制副作用,因为函数的执行是按照定义的过程进行的。
调试:
11.宏: 调试宏可能比较困难,因为它在预处理阶段展开,不容易跟踪。
12.函数: 调试函数相对容易,因为函数调用发生在运行时,可以使用调试器逐步执行。
13.命名约定:宏全部大写,函数名不要全部大写。
总体而言,宏和函数在不同情境下有各自的优势和劣势。宏适用于一些需要进行简单文本替换的场景,而函数提供了更多的结构和类型安全。选择使用哪种取决于具体的需求和代码设计。
条件编译:
在编译时可选择编译或放弃编译是很方便的。
常见的条件编译指令:
#if 常量表达式 .......#endif
多分支条件编译:
#if....... #elif...#else...#endif
文件包含:
头文件被包含:
本地文件包含:#include"filename"
查找策略:先在原文件内查找,若该头文件未找到,编译器就像找库函数头文件一样,在标准库找头文件。
库文件包含:#include
查找策略:头文件直接去标准路径下去找,找不到提示编译错误。
知识点:若库文件用"",查找效率很低。
嵌套文件包含:用条件编译
其他预处理指令
#error #pragma #line