C语言:预处理
文章目录
- 前言
- 一.预定义符号
- 二.#define
-
- 1.#define定义标识符
- 2.#define定义宏
- 3.#define替换规则
- 4. # 和 ##
- 5.带副作用的宏参数
- 6.宏和函数的对比
- 7.命名约定
- 三.#undef
- 四.条件编译
- 五.文件包含
前言
预处理是C语言的一个重要功能,它由预处理程序负责完成。
一.预定义符号
__FILE__//进行编译的源文件__LINE__//文件当前的行号__DATE__//文件被编译的日期__TIME__//文件被编译的时间
这些预定义符号是语言内置的,可以直接使用。
#includeint main(){printf("file:%s\nline:%d\n",__FILE__,__LINE__);}
如以上代码打印了源文件和当前行号:
二.#define
1.#define定义标识符
使用方法:#define 标识符 常量
#define定义的标识符,在预处理时会完成替换。
替换后,代码中不再含有标识符。
上图中的标识符MAX,STRING经过预处理后被替换成100,“abcdef”。
2.#define定义宏
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)。
下面是宏的申明方式:
#define name( parament-list ) stuff
其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中。
我们仍然举例说明:
#define SUB(x,y) x-yint main(){int a = 10;int b = 20;int c = SUB(a, b);printf("%d", c);}
上述代码中,在进行替换时,a,b作为参数也替换了x和y。
这就叫做宏。
Tips:参数列表的左括号必须和name相邻,如果之间有空白,参数列表就会变成stuff的一部分。
即下图的情况:
3.#define替换规则
宏替换发生在程序运行的预处理阶段,不进行实质性的程序运算,只做简单的文本替换。
如何理解这个文本替换呢?
请看以下代码:
#define SQUARE(x) x*xint main(){int a = 10;int b = SQUARE(a);int c = SQUARE(a+1);printf("%d\n", b);printf("%d\n", c);}
输出结果为100 和 21.
如下图:
由于是文本替换,不会自动给我们加上括号,
基于操作符的优先级,没有达到我们想要的效果。
因此在写宏的时候,不要吝啬括号,这样可以防止出错。
#define SQUARE(x) ((x)*(x))//完善后,单独的元素带括号,总体的结果也带了括号
4. # 和 ##
#的使用:
我们看以下代码,发现两个printf有很大的相似性。
可不可以其他们封装成一个函数Print呢?
#includeint main(){int a = 10;printf("the value of a is %d\n",a);int b = 10;printf("the value of b is %d\n",b)}
似乎是不行的,我们没法改变字符串里面的N为a或者b。
void Print(int n){printf("the value of N is %d\n", n);}
难道就这样没有办法了吗?
我们先看以下代码的输出:
int main(){printf("Nice shoot!!!\n");printf("Nice ""shoot!!!\n");}
执行后我们发现输出是一样的。
这说明C语言里的字符串会自动连接。
如下图:
这就有说法了,
如果我们利用这个特点,在两个字符串中间插入a,b的通式,不就能够完成了吗?
我们定义一个宏,
利用#操作符将参数直接转换成字符串,
即把参数N变为“N”,
字符串连接后就形成了我们想要的效果。
#define Print(N) printf("the value of "#N" is %d\n",N);int main(){int a = 10;Print(a);int b = 20;Print(b);}
输出为
我们可以再深一步,如果a和b的数据类型不同,应该怎么写,来继续达到我们的目的?
#define Print(N,format) printf("the value of "#N" is "format"\n",N)int main(){int a = 10;Print(a,"%d");double b = 20.0;Print(b,"%lf");}
没错,我们可以把打印格式作为参数传入写成通式。
##的使用:
用于将其两边的符号合成为一个:
如以下代码:
#define CAT(Tf,boys) Tf##boysint main(){int CAT(Tf, boys) = 5;printf("%d", Tfboys);}
5.带副作用的宏参数
我们看以下代码:
int main(){int a = 2;int b = a + 1;int b = ++a;}
a+1和++a都给b赋值3,结果是同样的。
但++a的同时对a造成了改变,我们将其视为副作用。
这个副作用可能在我们意识不到的时候出错。
在宏定义中我们应该尽量避免。
#define MAX(x,y) ((x)>(y)?(x):(y))int main(){int a = 5;int b = 8;int c = MAX(a++, b++);printf("%d\n", c);printf("%d\n", a);printf("%d\n", b);}
如以上代码中,倘若我们只是口算,即使对++运算很熟悉,也很容易出错。
输出结果为9 , 6,10
6.宏和函数的对比
- 宏通常被应用于执行简单的运算,在这点上在程序的规模和速度上比函数更胜一筹。
- 函数得指定参数类型,而宏是类型无关的,也是不够严谨的。
- 宏是没法调试的!!!
- 宏的参数可以出现类型,而函数不行。
-
7.命名约定
- 宏名全部大写
- 函数名不要全部大写
三.#undef
这条指令用于移除一个宏定义。
#define MIN 5int main(){printf("%d\n", MIN);#undef MINprintf("%d\n", MIN);}
以上代码中第一条printf语句正常,第二条语句报错。
就是因为#undef移除了MIN这个宏定义。
四.条件编译
在编译一个程序的时候我们如果要将一组语句编译或者放弃是很容易的,因为我们有条件编译指令。
int main(){int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){arr[i] = i;#if 1 printf("%d ", arr[i]);#endif}}
以上代码中当为#if 1 时,printf("%d ",arr[i]);编译
若改成#if 0 则不编译。
常见的条件编译指令:
-
#define 定义一个预处理宏
-
#undef 取消宏的定义
-
#if 编译预处理中的条件命令
-
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
-
#ifndef 与#ifdef相反,判断某个宏是否未被定义
-
#elif 若#if, #ifdef, #ifndef或前面的 #elif 条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
-
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
-
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
-
defined 与#if, #elif配合使用,判断某个宏是否被定义
五.文件包含
应注意双引号和尖括号的使用。
以上为本文全部内容。
由于我是新手小白,如有错误请斧正。