预处理指令:
程序猿编写的代码不是标准C代码,并不能被真正的编译器索编译,需要一段程序把代码翻译一下。
翻译的过程叫做预处理,被翻译的代码叫做预处理指令,以#开头的都是预处理指令查看预处理的过程:gcc -E code.c 把预处理结果显示在终端上gcc -E code.c -o code.i 把预处理的结果存储到code.i文件中预处理指令的分类:#inlcude 文件包含#include<> 从系统指定目录下查找并导入头文件#include"" 先从文件当前目录下找,找到就导入该头文件;如果找不到,再从系统指定目录下找并导入头文件操作系统通过环境变量来指定头文件的查找路径,或者通过设置编译参数来指定头文件的查找路径-I/path.bashrc#define 宏定义宏常量: #define MAX 100优点:提高代码的扩展性(方便批量修改)、提高可读性、提高安全性、还可以在case后面使用注意:一般宏名全部大写,末尾不要加分号【局部变量全部小写、全局变量首字母大写、指针变量+p、数组arr、字符串str、函数名全部小写+下划线】预定义好的宏:__func__ 获取函数名__FILE__ 获取文件名__LINE__ 获取当前行号__DATE__ 获取当前日期__TIME__ 获取运行时间宏函数: 其实就是带参数的宏宏函数不是真正的函数,不检查参数类型,没有传参,没有返回值,只有计算的结果#define sum(a,b) a+b1、把代码使用到的宏函数替换为宏函数后面的代码。2、再把宏函数代码中使用到的参数替换为调用者提供的参数宏函数的二义性:由于宏代码所处的位置、参数不同导致宏有不同的功能,这就叫做宏的二义性。如何避免宏的二义性:1、宏函数整体加小括号,每个参数都加小括号2、使用宏函数时,不要提供带自变运算符的变量作为参数注意1:容易出选择题,例如:哪个宏有二义性、宏函数的运算结果注意2:定义宏尽量别换行,如果要换行要在每行末尾加上续航符\,建议宏函数最外面加上大括号运算符:# 把宏函数的参数变成字符串## 合并两个参数变成一个标识符普通函数与宏函数的区别?它们是什么:普通函数:是一段具有某项功能的代码段,会被编译成二进制指令存储到代码段内存中,函数名就是首地址,有独立的命名空间、栈内存宏函数:是一个带参数的宏,并不是真正的函数,而只是代码的替换,仅仅只是使用起来像函数有什么不一样:函数: 返回值 类型检查 安全 压栈、出栈 速度慢 跳转宏函数: 运算结果 通用 危险 替换 速度快 冗余条件编译:根据条件决定那些代码是否参与最终的编译版本控制:#if#elif#else#endif头文件卫士:防止头文件被重复包含#ifndef 宏名#define 宏名#endif 判断调试:#ifdef 宏名(DEBUG)#else#endif用于输出调试信息:#ifdef DEBUG#define debug(...) printf(__VA_ARGS__)#else #define debug(...)#endif#define error(...) printf("%s:%s:%d %s:%m %s %s\n",__FILE__,__func__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)
头文件应该怎么写:
问题:头文件可能被任意个源文件包含,意味着头文件中的内容会在多个目标文件中存在,合并时不能有冲突。
重点:头文件中只能编写声明语句,不能有定义语句全局变量声明 extern int num;函数声明 宏常量宏函数typedef 类型重定义结构、联合、枚举的类型声明
头文件的编写原则:
1、为每一个.c文件编写一份.h文件,.h文件是对.c文件的说明
2、如果需要用到某个.c文件中的函数、变量、宏,只需要把它的头文件导入
3、.c也需要导入它的.h文件,目的是为了让声明和定义一致
头文件的相互包含:
假如a.h包含了b.h,b.h有需要包含a.h,这种情况叫做头文件的相互包含,这种情况就会编译出错。
错误:未知类型名错误“xxx”,一般都是因为头文件相互包含导致的(还可能是复制文件时粗心,忘记改宏名)
解决方案:把a.h中需要的内容,和b.h中需要的内容提取出来,编写一个c.h
注意:头文件的相互包含和重复包含的区别