深圳网站开发哪个好,wordpress菜单移动,网站建设 银川,国际公司名称大全名头✨个人主页#xff1a; 熬夜学编程的小林
#x1f497;系列专栏#xff1a; 【C语言详解】 【数据结构详解】
预处理
1、预定义符号
2、#define定义常量
3、#define定义宏
4、带有副作用的宏参数
5、宏替换的规则
6、宏和函数的对比
总结 在C语言中#xff0c;预处…
✨个人主页 熬夜学编程的小林
系列专栏 【C语言详解】 【数据结构详解】
预处理
1、预定义符号
2、#define定义常量
3、#define定义宏
4、带有副作用的宏参数
5、宏替换的规则
6、宏和函数的对比
总结 在C语言中预处理阶段是代码执行之前的一个重要步骤负责对源代码进行宏替换、条件编译等处理。预处理器提供了强大的工具使得我们能够在编写代码时更加灵活、高效。
1、预定义符号
C语言设置了⼀些预定义符号可以直接使用预定义符号也是在预处理期间处理的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C其值为1否则未定义
举个例子
printf(file:%s line:%d\n, __FILE__, __LINE__); 这些预定义符号可以在编写代码时提供有用的信息例如记录日志、调试代码或实现跨平台的条件编译。通过利用这些符号我们能够在不同的编译环境中编写更具灵活性和可移植性的代码。 2、#define定义常量 基本语法
#define name stuff
举个例子
#define MAX 1000
#define reg register //为 register这个关键字创建⼀个简短的名字
#define do_forever for(;;) //⽤更形象的符号来替换⼀种实现
#define CASE break;case //在写case语句的时候⾃动把 break写上。
// 如果定义的 stuff过⻓可以分成⼏⾏写除了最后⼀⾏外每⾏的后⾯都加⼀个反斜杠(续⾏符)。
#define DEBUG_PRINT printf(file:%s\tline:%d\t \
date:%s\ttime:%s\n ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
思考在define定义标识符的时候要不要在最后加上 ; 呢? 比如
#define MAX 1000;
#define MAX 1000
建议不要加上 ; ,这样容易导致问题。 比如下面的场景
#include stdio.h
#define MAX 100;
int main()
{int condition 10;int max 0;if (condition)max MAX;elsemax 0;return 0;
} 如果是加了分号的情况等替换后if和else之间就是2条语句而没有大括号的时候if后边只能有⼀条语句。这里会出现语法错误。 3、#define定义宏
#define机制包括了⼀个规定允许把参数替换到文本中这种实现通常称为宏macro或定义宏definemacro。下面是宏的申明方式
#define name( parament-list ) stuff 其中的 parament-list 是⼀个由逗号隔开的符号表它们可能出现在stuff中。 注意参数列表的左括号必须与name紧邻如果两者之间有任何空白存在参数列表就会被解释为stuff的⼀部分。 举例
#define SQUARE( x ) x * x
这个宏接收⼀个参数 x .如果在上述声明之后你把 SQUARE( 5 ); 置于程序中预处理器就会用 下面这个表达式替换上面的表达式 5 * 5
警告 这个宏存在⼀个问题观察下面的代码段
#includestdio.h
#define SQUARE( x ) x * x
int main()
{int a 5;printf(%d\n, SQUARE(a 1));return 0;
} 乍⼀看你可能觉得这段代码将打印36事实上它将打印11为什么呢
替换文本时参数x被替换成a1,所以这条语句实际上变成了
printf (%d\n,a 1 * a 1 );
这样就比较清晰了由替换产生的表达式并没有按照预想的次序进行求值。 在宏定义上加上两个括号这个问题便轻松的解决了
#define SQUARE(x) (x) * (x) 这样预处理之后就产生了预期的效果
printf (%d\n,(a 1) * (a 1) ); 这里还有⼀个宏定义
#define DOUBLE(x) (x) (x)
定义中我们使用了括号想避免之前的问题但是这个宏可能会出现新的错误。
#includestdio.h
#define DOUBLE(x) (x) (x)
int main()
{int a 5;printf(%d\n, 10 * DOUBLE(a));return 0;
} 这将打印什么值呢看上去好像打印100但事实上打印的是55. 我们发现替换之后
printf (%d\n,10 * (5) (5));
乘法运算先于宏定义的加法所以出现了 55 . 这个问题的解决办法是在宏定义表达式两边加上⼀对括号就可以了。
#define DOUBLE( x) ( ( x ) ( x ) ) 提示所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。 4、带有副作用的宏参数
当宏参数在宏的定义中出现超过⼀次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。 例如
x1;//不带副作⽤
x;//带有副作⽤
MAX宏可以证明具有副作用的参数所引起的问题。
#includestdio.h
#define MAX(a, b) ( (a) (b) ? (a) : (b) )
int main()
{int x 5;int y 8;int z MAX(x, y);printf(x%d y%d z%d\n, x, y, z);//输出的结果是什么return 0;
}
这里我们得知道预处理器处理之后的结果是什么
z ( (x) (y) ? (x) : (y)); 根据#define的替换原则将z的表达式替换成 z ( (x) (y) ? (x) : (y)); 先计算(x)(y)根据后置的口诀先使用再1因此5 与 8 比较5不大于8因此执行y语句此时x6 y9 然后执行y根据后置口诀先使用再1因此把9赋值给z最终y1因此y10 所以输出的结果是x6 y10 z9 5、宏替换的规则
在程序中扩展#define定义符号和宏时需要涉及几个步骤。 1. 在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换。 2. 替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值所替换。 3. 最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。 注意 1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏不能出现递归。 2. 当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。 6、宏和函数的对比
宏通常被应用于执行简单的运算。 比如在两个数中找出较大的⼀个时写成下面的宏更有优势⼀些。
#define MAX(a, b) ((a)(b)?(a):(b))
那为什么不用函数来完成这个任务 原因有⼆ 1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算⼯作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜⼀筹。 2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于 来比较的类型。宏的参数是类型无关的。 和函数相比宏的劣势 1. 每次使用宏的时候⼀份宏定义的代码将插⼊到程序中。除非宏比较短否则可能大幅度增加程序的长度。 2. 宏是没法调试的。 3. 宏由于类型无关也就不够严谨。 4. 宏可能会带来运算符优先级的问题导致程容易出现错。 宏有时候可以做函数做不到的事情。比如宏的参数可以出现类型但是函数做不到。 #define MALLOC(num, type)\ //内容较长使用\(续行符)与下一行连接
(type )malloc(num*sizeof(type))
...
//使⽤
MALLOC(10, int);//类型作为参数
//预处理器替换之后
(int *)malloc(10*sizeof(int)); 总结 本篇博客就结束啦谢谢大家的观看如果公主少年们有好的建议可以留言喔谢谢大家啦