目录
预处理指令的特点
- 1,以#开头
- 2,预处理指令都是在编译之前执行的
- 预处理指令后面都是没有分号
C 程序从编写到编译,链接,执行的一个流程
- 1,新建一个.c 源文件,.c 的文件是c 程序的源文件
- 2,在.c 的源文件中写上符合 c 语法规范的源代码
- C语言严格区分大小写
- 除了字符串常量,其他的地方必须使用英文输入法
- 3,使用 cc -c 指令去编译源文件
- 格式:cc -c 源文件名称
- 编译做的事情
- a,先执行源文件中的预处理指令,如果有文件包含指令,就将文件的内容拷贝到指令的地方
- b,检查 .c 文件中的语法是否符合规范
- 1,如果符合,生成.o 目标文件,就是.c 文件对应的二进制指令
- 2,如果不符合语法规范,就报错,不会生成.o 的目标文件
- 4,链接
- cc xx.o
- a,为.o 的目标文件添加启动代码
- b,链接函数,告诉编译器要调用的函数在什么地方,调用的时候,去正确的地方找实现
- c,链接成功以后,就会生出一个可执行文件,这个文件就是我们程序了
预处理指令的分类
- 文件包含指令 # include
- 宏定义:可以将一段 c 代码定义为 1 个标识,使用这个标识就可以使用这段代码
- 条件编译指令:
宏定义:
- 他是一个预处理指令,所以他在编译之前执行
- 作用:
- 可以为一段 C 代码定义一个标识,如果你要使用这个 c 代码,那么就是用这个标识就可以了
- 语法:
- ----#define 宏名 宏值
- ----#define N 10
- 如何使用宏
- 在C 代码中,直接使用宏的名称就可以了
- 宏的原理是
- 在预编译的时候,就会执行源文件中的预处理指令,会将 C 代码中使用宏名的地方替换为宏值,
- 将 C 代码中的宏名替换为宏值的过程就叫做宏替换
在使用宏的时候需要注意的地方
- 宏值可以是任意的东东(常量,表达式),在定义宏的时候并不会去检查语法,但是替换的时候就会检查语法了
#include <stdio.h>
# define M 10
# define N 10+10+10
int main(int argc, const char * argv[]) {
int num = M+10;
printf("M = %i
",M);
printf("num = %i
",num);
// 宏值是整个表达式,而并不是计算的结果
int num1 = N * 2;
printf("num1 = %i
",num1);
return 0;
}
-
无论宏值是什么东西,在定义宏的时候不会去检查语法,只有当完成了宏替换的时候才会去检查替换以后是否符合语法规范
-
如果宏值是一个表达式,宏值并不是这个表达式的结果,而是这个表达式的本身
-
如果宏值中包括一个变量名,那么在使用这个宏之前必须保证这个变量存在不然就会报错
# define X a+a
int main(int argc, const char * argv[]) {
int a = 10;
int num2 = X;
return 0;
}
- 无法通过赋值符号给宏改值,因为宏根本就不是变量
- 宏的作用域问题
- a, 宏可以定义在函数里面也可以定义在函数外面
- b,从定义宏的地方,后面的所有的地方都可以使用这个宏,跟变量不一样,就算这个宏定义在这个大括号里面,在这个后面也是可以访问的
- undef 宏名 -->解除宏指令
- 默认情况下,宏从定义的地方一致到文件结束都可以使用,#undef 宏名可以让指定的宏提前消失
- 字符串中如果出现了宏名,并不会认为是一个宏,而是认为是字符串的一部分,字符串中并不会出现宏替换
- 宏的层层替换
- 宏值当中我们用到了另外一个宏名,那么就会将这个宏值当中的宏名替换为对应的宏值
# define P 3
# define A 5
# define JISUAN P*A
int main(){
printf("JISUAN = %i
",JISUAN);
}
- 如果后面跟了分号,那么就会将分号作为宏值的一部分,但是建议不要这么写
- C语言的代码都可以用作宏值
# define p printf
# define d "%i"
int main(){
p(d,10);
return 0;
}
define 和 typedef 的区别
- ----#define 是一个预处理指令,在预编译的时候执行,在预编译的时候会把宏名替换为宏值
- typedef 是一个 C 代码.在运行的时候执行
- ----#define可以将任意的C 代码取 1 个标识名
- typedef 只能为数据类型取名字
宏的高级用法
- 我们在定义宏的时候,宏名是可以带参数的,在这个宏值当中,可以直接使用这个参数
- 如果使用的宏有参数,那么就必须要在使用它的时候为这个宏的参数传值
# define B(a) a+10+a
int main(){
// 有参数的宏
int num3 = B(8);
printf("num3 = %i
",num3);
return 0;
}
- 宏替换的原理是什么?
- 先将传入的值传递给宏的参数,那么宏的参数的值就是我们传递的值
- 再把宏值当中使用参数的地方换成参数的值
- 最后,再将使用宏名的地方,替换为最后的宏值
# define MAX(a,b) a > b ? a:b
int main(){
printf("最大值是:%i
",MAX(10, 0));
return 0;
}
使用带参数的宏注意点
- 宏不是函数,所以宏的参数不需要写数据类型,直接写参数名称即可
- 我们在定义宏的时候,编译器是如何区分宏名和宏值的呢?
- ----#define空格宏名空格宏值 --->两个空格之间的就是宏名
- 为带参数的宏传递的时候是本色传递(本色传递:你给的什么,那就是什么,预编译的时候啥都没有就是直接本色的替换)
- 宏一定程度上可以实现和函数一样的效果
- 宏一旦换了行就代表宏定义结束了,
- 代码很少的时候会使用宏,只有一句,两句的时候可以使用宏
条件编译指令
- 他也是一个预处理指令,所以在预编译阶段执行
- 作用:默认情况下我们所有的 C 代码都会被编译为二进制代码,条件编译指令的作用:可以让编译器只编译置顶的部分
- 条件编译指令的一种用法
- if 条件
- c 代码
- endif
# define M 10
int main(){
// 条件编译指令
#if M == 10
printf("我是条件编译指令的代码
");
#endif
return 0;
}
// 输出:我是条件编译指令的代码
- 注意:条件只能是宏,不能是变量,因为:预编译的时候变量还没有呢
int m = 10;
#if m == 10
printf("我是 变量 m == 10 才会执行的代码
");
#endif
- 条件编译指令的第二种用法
# define M 10
int main(){
#if M > 0
printf("M>0编译这个代码
");
#elif M > 0 && M < 20
printf("M > 0 && M < 20编译这个代码
");
#elif M >20 && M < 40
printf("M >20 && M < 40编译这个代码
");
#else
printf("否则编译这个代码");
#endif
return 0;
}
- 条件编译指令和 if 语句的对比
- 1,条件编译指令是一个预处理指令,在预处理阶段执行,if 语句是 C代码.在程序运行的时候执行
- 2,if 语句全部都要被编译为二进制指令,条件编译指令只会编译条件符合的代码
- 3,实际上,if 语句一定程度上可以换成条件编译指令,但是条件编译指令的条件不能是变量参与,只能是宏
- 条件编译指令的第三种用法,如果定义了指定的宏,就编译指定的代码
#define M 10
int main(){
// 第三种用法
#if M
printf("如果定义了这个宏,就执行这个代码
");
#endif
return 0;
}
int main(){
// 第三种用法
#ifndef M
printf("如果没有定义了这个宏,就执行这个代码
");
#endif
return 0;
}
条件编译指令的应用场景
// 在调试的时候,调试信息有很多,又不想每次都一个一个的删除,那么可以使用条件编译指令,每次只需要修改自己定义的宏就可以对 printf 函数的控制
#include <stdio.h>
# define SHAN_DEBUG 0
# if SHAN_DEBUG == 0
# define print(val1,val2) printf(val1,val2)
# else
# define print(val1,val2)
# endif
int main(int argc, const char * argv[]) {
print("%d",10);
return 0;
}
// 无论一个文件包被#include 多少次,我只引入一次
#ifndef lianxi_h
#define lianxi_h
#include <stdio.h>
// 写自己的代码
#endif /* lianxi_h */
// 原理:在 main.c中#include lianxi.h,导入很多次,#include 的作用就是将此文件全部复制过来,所以复制第一次在编译的时候如果没有导入 lianxi.h 这个文件,全部被copy 过来,此时没有定义 lianxi.h 这个宏,那么就定义这个宏 lianxi.h,然后在第二次copy 过来的时候,发现定义了这个 lianxi.h 这个宏,所以就不会重复的 copy 了