企业网站首页布局设计,asp网站开发教程入门,百度知道网页版进入,wordpress 4.7.9目录
一、什么是宏以及宏的声明方式
1.宏常量#xff1a;
2.宏函数#xff1a;
二、宏的替换原则
三、宏设计的易犯错误 ERROR1#xff1a;尾部加分号#xff08;当然有些特定需要加了分号#xff0c;这里说明一般情况#xff09;
ERROR2#xff1a;宏函数定义时
2.宏函数
二、宏的替换原则
三、宏设计的易犯错误 ERROR1尾部加分号当然有些特定需要加了分号这里说明一般情况
ERROR2宏函数定义时没有保证其参数在表达式中的独立性以及表达式的独立性 ERROR3带副作用的宏参数
四、###
1.使用# 把一个宏参数变成对应的字符串
2.使用##允许宏定义从分离的文本片段创建标识符
五、宏函数特性分析 前言 宏每个C语言学习者绕不开的话题其使用简便又十分易错稍不注意甚至处处小心仍然容易写出BUG。那么宏是什么应该如何正确使用(减小使用错误概率)今天让我们来一探究竟。
一、什么是宏以及宏的声明方式 #define 机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏macro或定义宏define macro。 宏的声明方式
1.宏常量
#define 宏常量名 宏常量值 ------- 例#define MAX 1000
说明 a.宏常量名和普通常量名没什么区别不过习惯上使用大写字母表示 b.语句不同成分之间用空格隔开末尾不需要加分号
2.宏函数
#define 宏函数名参数列表 宏函数表达式 ------ 例 #define ADD ( x, y ) ( ( x ) ( y ) )
ps.上述函数表达式看起来很别扭但是这是比较安全的写法下文会进行解释。
说明 a.宏函数名和普通变量名没什么区别不过习惯上使用大写字母表示 b.语句不同成分之间用空格隔开末尾不需要加分号
注意参数列表的左括号必须与宏函数名紧邻。如果两者之间有任何空白存在参数列表就会被解释为宏函数体的一部分。
二、宏的替换原则 我们在设计宏和使用宏时往往因为对宏的替换原则不够了解而出现错误而宏的替换总结起来就是两个字——“生硬”。 宏是在编译的阶段进行的替换编译器会对代码进行检查对使用了宏常量名和宏函数名的代码段将对应的常量值或函数表达式直接在原位置处嵌入式替换。
例
#define MAX 1000
#define ADD( x, y) ( ( x ) ( y ) )int main()
{
// 替换前printf(%d,MAX);printf(%d,ADD( 1, 2));
// 替换后printf(%d,1000);printf(%d,( ( 1 ) ( 2 ) ));return 0;
} 这就如同我们进行覆盖式 ctrl c/v 一样如此这般确实显得编译器很生硬但这也是没有办法毕竟语法规定同时编译器也不能自作主张的替换这就要求我们编程者根据特性来好好设计宏了 。
三、宏设计的易犯错误 了解了宏的替换原则想必对于设计宏犯错误的原因也大概知道了就是由于生硬的替换而导致逻辑或者语义甚至语法的错误出现。 ERROR1尾部加分号当然有些特定需要加了分号这里说明一般情况 宏替换后 max MAX就变成 max 1000 这也就导致了在if else语句之间出现了一个空语句else找不到if而落单了这很明显是有问题的。 宏替换后printf(%d,MAX) 就变成了 printf(%d,1000) 这一个明眼人都能看出明显问题。
ERROR2宏函数定义时没有保证其参数在表达式中的独立性以及表达式的独立性 这里我们设计一下加法宏函数 这样的结果会是多少呢想必根据上文提到的替换规则应该很清楚发生替换后变为
5 * 2 3 13 回答正确
那如果这样呢还是13吗 这时就昏头了是先计算宏参数值呢还是先替换测试瞧瞧 这是 5 * 1 1 2 1的结果看来是先直接替换无疑了这也很体现宏的“生硬” 宏函是仿照函数的应该和函数基本性质相同才对而对于函数函数参数传参时应该先计算后传入对于函数表达式更是一个整体全算完了才进行外部动作。
所以对于宏函数的函数表达式我们应该用括号将参数以及函数表达式的独立性体现出来于是我们这样定义 #define ADD(xy) x y ERROR3带副作用的宏参数 当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
这里的副作用通常是指对自身进行改变的作用。如x 1 与 x 我们进行替换 结果 在进行判断后xy自增选择结束后y又进行自增所以这即便给每个参数都确保的独立性也无法保证其正确。 四、###
1.使用# 把一个宏参数变成对应的字符串
#对于宏有这么一个技巧可以把一个宏参数变成对应的字符串
PRINT(%d, i 3) 变为 printf(the value of i 3 is“%d” \n, 1 3)
ps.这里还涉及printf字符串拼接的特性printf(hahanihao) - printf(hahanihao)
2.使用##允许宏定义从分离的文本片段创建标识符 这样的连接必须产生一个合法的标识符否则其结果就是未定义的。
五、宏函数特性分析 通过上文应该不会有人还觉得宏函数那么好干嘛还要使用普通函数了吧。
a.调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。(创建函数栈桢有内存消耗)所以宏比函数在程序的规模和速度方面更胜一筹。
b.函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。但也意味着宏没有类型检查这在一些时候是不安全的体现。
c. 每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度,增加编译工作量。 d.宏在编译阶段就进行替换是没法调试的。
4. 宏可能会带来运算符优先级的问题导致程容易出现错。 综上宏函数适合短小计算可以替代小型函数但大型函数也是无能为力对于类型检查有要求的可以使用enum枚举常量来替代。