预处理指令
廖家龙 用心听,不照做

C语言的代码主要分为两类:
1)C代码
2)预处理代码:以#开头的代码

预处理指令的分类:
1)文件包含指令:#include
2)宏定义:#define【可以将一段C代码定义为一个标识,使用这个标识就可以使用这段代码】
3)条件编译指令:#if【只编译指定的C代码为二进制指令】

预处理指令的特点:
1)都是以#开头
2)预处理指令的后面没有分号

文件包含指令:

文件包含指令作用:可以将指定的文件的内容拷贝到写指令的地方
#include”文件路径”
#include<文件路径>

绝对路径:路径从根目录开始
相对路径:相对于当前这个文件夹的路径,和当前文件路径相同的部分删除【一般把导入文件放到和main.c相同的文件夹下】

#include”文件路径”和#include<文件路径>的寻找指定文件的方式不一样:
1)前者先去当前源文件所在的目录中查找这个文件,如果有直接包含,如果没有再去系统自带的编译器目录中查找,如果有直接包含,如果没有报错
2)后者直接去编译器目录中查找,如果有包含,没有报错

宏定义:

它是一个预处理指令,所以它在编译之前执行,可以将一段C代码定义为一个标识,使用这个标识就可以使用这段代码

宏的原理:在预编译的时候,就会执行源文件中的预处理指令,会将C代码中使用宏名的地方替换为宏值【任意的C代码】,将C代码中的宏名替换为宏值的过程叫做宏替换/宏代换

在定义宏的时候不会去检查语法,只有当完成了宏替换的时候,才会去检查替换以后是否符合语法规范

如果宏值是一个表达式,那么宏值并不是这个表达式的结果,而是这个表达式本身

如果宏值中包括一个变量名,那么在使用这个宏之前必须要保证这个变量已经存在

【默认情况下,宏从定义的地方一直到文件结束都可以使用,#undef 宏名可以让指定的宏提前失效】
#define N 10

只能在中间使用这个宏,#undef N下面宏失效

#undef N

字符串中如果出现了宏名,系统不会认为这是一个宏,而是认为是字符串的一部分

如果宏后面跟了分号,那么就会把分号作为宏值的一部分

我们在定义宏的时候,宏名是可以带参数的,在这个宏值中可以直接使用这个参数,如果使用的宏有参数,那么就必须要在使用它的时候为这个宏的参数传值

宏不是函数,所以宏的参数不需要加类型说明符

为带参数的宏传值的时候,是本色传递,如果传递一个变量,并不是传递这个变量的值

条件编译指令:

默认的情况下,所有的C代码都会被编译为二进制代码,条件编译指令可以让编译器只编译指定部分的代码

在预编译的时候,如果条件成立,就会将其中的C代码编译成二进制指令,如果条件不成立,就不会将其中的C代码编译成二进制指令【条件只能是宏,不能是变量】

If语句无论如何全部都要被编译为二进制指令,条件编译指令只会将符合条件的C代码编译为二进制指令

如果定义了指定的宏,就编译其中的代码

无论一个文件被#include多少次,只希望它最终只会被包含一次

static和extern:

C语言中的关键字,用来修饰变量和函数

如果局部变量被static修饰,这个变量就叫做静态变量,静态变量不再存储在栈区域,而是存储在常量区,当函数执行完之后,这个静态变量不会被回收,后面再去执行这个函数的时候,声明静态变量的这句话就不会再执行了,直接略过,直接使用这个静态变量的值【静态变量只有一份】

extern不能修饰局部变量

当我们分模块开发的时候,如果要在模块中声明全局变量,全局变量的声明要写在.h文件中,全局变量的实现要写在.c文件中【如果将全局变量定义在模块中,这个全局变量就必须要使用static或者extern修饰】

如果定义在模块中的全局变量使用extern修饰,这个模块中的全局变量就可以跨模块访问;使用static修饰,这个模块中的全局变量就只能在当前模块中访问

如果函数被extern修饰,那么这个函数可以跨模块调用;如果函数被static修饰,那么这个函数只能在当前模块中调用,无法跨模块调用

如果函数没有写static或者extern,那么这个函数默认就是extern