预处理语句就是以#开头的语句。这些语句类型如下:
- #include:包含头文件
- #define:宏定义
- #undef:取消宏定义
- #ifdef,#endif:成对使用,判断是否定义了某个宏
宏定义
宏定义的本质就是原样替换,写什么值就替换成什么。
形式:#define 名字 值
1、示例使用
示例1:基本使用
#define PI 3.14
printf("%d",PI);//这实质就是执行printf("%d",3.14);将PI用3.14替换
示例2:利用宏定义中的值是其他的宏
#define a 1
#define b 2
#define c (a+b)//使用宏来充当值
printf("%d",c);//这实质就是执行printf("%d",(1+2));
示例3:宏定义实现代码开关
#define DEBUG//用作开关时,后面不跟值
#ifdef DEBUG
printf("DEBUG");//如果宏定义了DEBUG,就执行这个语句
#endif
/* #ifdef 与 #endif 配对出现*/
示例4:改变宏定义的值
#define a 100
/* ...一些代码 */
#undef a //取消a的宏定义
#define a 200//重新定义a为200
示例5:传参数的宏
#define MAX(a,b) ((a)>(b)?(a):(b))
/* 调用 */
MAX(1,2)//即a=1,b=2,这实际就是((1)>(2)?(1):(2))
2、注意点
注意点1:当宏定义的值为表达式时,需要加括号
#define N (3+2) //这里不能写3+2,必须写(3+2)
注意点2:宏定义的名称不能与函数名重复
注意点3:宏定义传参时,后面的值需要加括号
下列代码test2应该写为((n)*(n)) ,因为n*n也可能因为其他高优先级的情况被拆开。
3、宏定义与常量const的区别
1.1 定义不同
宏用 " #define " 声明,const 常量用 " const + 数据类型 " 声明
宏最后没用分号,const 常量声明需要用分号表示语句结束
宏不需要用等号赋值,cosnt 常量需要用等号赋值
1.2 处理阶段不同
宏定义在预处理阶段进行文本替换,const 常量在程序运行时使用。
1.3 存储方式不同
宏定义是直接替换,不会分配内存,存储于程序的代码段中。const 常量需要进行内存分配。
1.4 是否进行类型检查
宏定义是字符替换,不进行类型检查。
const 常量定义时需要声明数据类型,使用时会进行类型检测
1.5 其他
宏定义可以声明函数,也可以用undef取消宏
4、宏定义与typedef的区别
2.1 定义不同
typedef 是 C语言的关键字,用于创建类型别名,它需要使用标 识符和现有的类型进行配合
#define 是预处理指令,用于创建宏定义,它可以定义任意的标识 符和文本替换
2.2 处理阶段不同
关键字typedef在编译阶段有效,有类型检查的功能
#define则是宏定义,发生在预处理阶段,也就是编译之前, 它只进行简单而机械的文本替换,而不进行任何检查
2.3 定义变量时的含义不同
例如有以下声明:
typedef int* Pint16_t;
#define Pint16_t int*
这两种声明都是把int*用Pint16_t来替换。但定义变量时的含义不同:
/* typedef的含义 */
Pint16_t p1,p2;//这代表p1,p2都是int*类型
/* #define的含义 */
Pint16_t p1,p2;//这代表p1是int*型,p2是int型
代码验证如下:
2.4 作用域不同
typedef 有作用域限定。如果放在所有函数之外,它的作用域就是从它定义开始直到文件尾; 如果放在某个函数内,定义域就是从定义开始直到该函数结尾
#define 不受作用域约束,只要是在 #define声明后的引用都是正确的,不管是在某个函数内,还是在所有函数之外,作用域都是从定义开 始直到整个文件结尾。
5、宏定义传参与函数的区别
宏在调用时的效率是比函数高很多的;
函数的参数是有类型的,也存在类型检查。但宏的参数是没有类型与类型检查的;
函数可以递归,而宏不可以递归;
函数方便调试,而宏是不方便调试的;
对于参数而言,宏的参数是直接替换的,所以会有一些参数具有副作用。而函数的参数是临时拷贝的,没有副作用的情况;
#include的<>与" "的区别
#include <xxx.h>与#include "xxx.h"的区别是搜索路径不同:
- <>:编译器到标准库路径下寻找头文件,路径为/usr/include
- " ":编译器先在当前文件下寻找头文件,找不到时再去标准库寻找头文件
别名typdef
1、别名基本类型
基本类型有char、int、float、double这些。
形式:typdef <类型> <别名>;
如:typdef unsigned char uint8_t,这表示uint8_t就是unsigned char
2、别名基本指针
基本指针有char*、int*、float*、double*这些
形式:typdef <指针> <别名>;
如:typdef int* PData,定义时可以用 " PData a; "来代替 " int* a; "这样使用。
别名指针后,定义指针不需要写*,类型是PData,而不是PData*
3、别名结构体
形式:
typedef struct 结构体名{
//结构体成员
}结构体别名,*结构体指针别名;
/* 举例 */
typedef struct test{
int a;
int b;
}Name,*PName;
在上述举例中,是用Name作为struct test的别名,PName作为指针别名
4、别名数组指针
数组指针的定义是:<类型> (*指针名)[列大小]
别名形式就是把指针名换为别名:typedef <类型> (*别名)[列大小]
5、别名函数指针
函数指针的定义是:<返回值类型> (*指针名)(形参)
别名形式就是把指针名换为别名:typedef <返回值类型> (*别名)(形参)