C Program基础-宏定义
写好c语言,漂亮的宏定义是非常重要的,我们在阅读别人工程时,往往能看到大量的宏定义,宏定义可以增加代码的可读性,也能增加代码的可移植性,一个好的宏定义甚至是一件艺术品。今天我们就来看看宏定义的方方面面。
(一) 宏 vs 函数
在软件开发中过程中,经常有很多重复使用的代码段或功能模块,这些功能既可以用函数实现,也可以用宏实现,那么究竟是用函数好还是宏定义好呢?这就要求我们对二者做合理的取舍。
我们来看下面的例子,求两个数的较大者:
(1)宏定义:
#define Max(a, b) ((a) > (b)? (a):(b))
(2)函数实现:
int max(int a, int b)
{
return a > b ? a : b;
}
一般来说我们不会选择用函数实现,原因有两个:
首先,函数调用会带来额外的开销,调用需要开辟栈空间,将形参压栈,记录返回地址,并释放栈空间,而使用宏定义则在代码规模和速度方面都比函数更胜一筹;
其次,函数定义时需要指定参数类型,比如上面的函数只能对int运算,对于其他的类型long,float, double等,每一种都需要写一个函数,反之,上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。
我们再来看一个宏定义:
#define MALLOC(type) (type *)malloc(sizeof(type))
这个宏定义是我们可以对任何类型分配堆空间,这个用函数是很难实现的。
但是,并不是说宏定义就比函数好,定义宏的时候有很多地方需要注意:
(二)宏定义的陷阱
(1)不能忽视宏定义中的空格
我们看宏定义: #define f(x) ((x) - 1) 假如我们不小心写成 #define f (x) ((x) - 1) ,此时编译是无法通过的,编译器把f 替换成了(x) ((x) - 1),因此宏定义时不能忽视空格,但是宏调用时却没有关系:
对于上面的宏定义 #define f(x) ((x) - 1) ,我们可以这样来调用:
void main()
{
int a = ;
int result = f (a);
printf("result: %d\n", result);
}
(2)宏定义时不能吝啬括号
我们常常看到如下的宏定义
#define abs(a) ((a) >=0 ? (a) : -(a))
在宏定义中,使用了大量的括号,这些括号的作用是预防引起与优先级有关的问题,
假如我们把abs定义成:
#define abs(a) a >=0 ? a : -a
这个宏定义在运算abs(-10)时是没有问题的,但假设我们有如下宏调用:
void main()
{
int a = ;
int b = ;
int result_abs = abs(a - b); printf("result_abs: %d\n", result_abs);
}
程序输出的结果是: result_abs: -
我们把宏展开就能看出其中的端倪 abs( - ) // 10 - 20 >=0 ? 10 - 20 : - 10 - 20 ,这里的表达式 -10 - 20并没有按照我们所期望的 - (10 - 20)展开,所以造成了上面的错误,
我们再来看另一个宏定义: #define sum(a, b) a + b
当我们有如下宏调用时就会出错 int result_sum = sum(, ) * sum(, ); 因为宏展开变成了 3 + 5 * 2 + 3,由于*优先级高于+,所以程序输出 result_sum:
我们将宏改为 #define sum(a, b) ((a) + (b)) 就可以避免出现上面的错误。
因此我们在宏定义是要做的是在每一个参数以及整体表达式都加上括号
(3) "++" "--"在宏定义时引发的错误
即使我们在每个参数及整体表达式都加上括号,"++", "--"引发的错误仍然难以避免
我们定义一个max宏: #define max(a, b) ((a) > (b) ? (a) : (b))
有如下的调用:
void main()
{
int arr_number[] = { , , };
int biggest = arr_number[];
int i = ;
while (i < )
{
biggest = max(biggest, arr_number[i++]);
}
printf("The biggest number in array is %d\n", biggest);
}
最后程序的输出是: bigest number in array is 为什么会输出20呢?我们期望的是30。
我们来分析下程序运行的过程:
bigest初始化为10,进入while循环,i = 1,运算表达是 bigest = max(bigest, arr_number[i++]); 我们将这里的max宏展开,
上面的表达式等价于
biggest = ((biggest) > (arr_number[i++]) ? (biggest) : (arr_number[i++]))
我们发现i自增了两次,下一次循环进入时i = 3,跳出了循环,输出的20是最后一次自增前的值,即arr_number[2].
这里的解决办法有两个 (a) 将max定义为函数, (b)宏调用时不使用 i++。
(4)宏不是类型定义
为了增加程序的可移植性,我们会如下定义宏:
#define Student1 struct student1{ \
char id[]; \
char name[];\
int age;\
char sex[];\
}
我们也可以用typedef来定义一个新的类型:
typedef struct student2
{
char id[];
char name[];
int age;
char sex[];
} student2;
这两种命名方式看似差不多,但是typedef的方式更通用一些,考虑下面的代码:
#define ST_P1 struct student1*
typedef student2 *ST_P2; void main()
{
ST_P1 stp1_a, stp1_b;
ST_P2 stp2_a, stp2_b;
}
当我们试图用ST_P1和ST_P2来声明多个变量时,问题就来了,
stp1_b并不是指向student1结构的指针变量,而是student结构变量,我们把宏展开看看就很清楚:
struct student1 *stp1_a, stp1_b
这个语句a被定义为指向结构的指针,而b被定义为结构
因此我们最好用typedef来定义类型,而不是#define
(三)经典宏定义
(1)assert宏
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,我们常常这样使用assert宏:
void main()
{
int* p = NULL;
p = (int *)malloc(sizeof(int));
assert(p != NULL);
...
}
如果让我来定义assert宏,我可能会定义成如下的形式:
#define assert(exp) if(!exp) _assert(#exp, __FILE__, __LINE__)
上面的宏使用的是语句,在某些情形下,这个宏有一些难以察觉的错误,假如我们有如下的宏调用:
if(x > )
assert(y > x);
else
assert(x > y);
...
我们把宏展开:
if(x > )
if(!(y > x))) _assert("y > x", __FILE__, __LINE__);
else
if(!(x > y)) _assert(" x > y", __FILE__, __LINE__);
...
由于else默认是跟离它最近的if匹配,我们重新缩进下:
if(x > )
if(!(y > x))) _assert("y > x", __FILE__, __LINE__);
else
if(!(x > y)) _assert(" x > y", __FILE__, __LINE__);
...
这和我们想要的结果是不同的,
我们再来看看系统如何定义assert宏:
#define assert(exp) (void)((exp) || (_assert(#exp, __FILE__, __LINE__), 0))
这个宏是一个表达式,不是语句,利用了||语句的短路现象,||前面的表达式为真时,后面的不再计算,当||之前为假时,才处理后面的表达式;
宏中有个#exp,这里的#号是提取exp的值;
表达式最后有一个0处于逗号运算符之后,这样处理确保整个表达式最后值为0,即为false。
C Program基础-宏定义的更多相关文章
- 黑马程序员——C语言基础 枚举 宏定义 自定义 static exterm
Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) (一)枚举 1)枚举类型的定义 枚举是C语言中的一种基本数据类型,并不是构 ...
- 【编程基础】C语言常见宏定义
我们在使用C语言编写程序的时候,常常会使用到宏定义以及宏编译指令,有的可能比较常用,有的可能并不是很常用,是不是所有的C语言宏定义以及宏指令你都清楚呢? 指令 用途详细介绍 # 空指令,无任何效果 # ...
- 零基础逆向工程16_C语言10_宏定义_头文件_内存分配_文件读写
#define 无参数的宏定义的一般形式为:#define 标识符 字符序列 如:#define TRUE 1 注意事项: 1.之作字符序列的替换工作,不作任何语法的检查 2.如果宏定义不当,错误要到 ...
- C基础:关于预处理宏定义命令
为了程序的通用性,可以使用#define预处理宏定义命令,它的具体作用,就是方便程序段的定义和修改. 1.关于预定义替代 #define Conn(x,y) x##y#define ToChar(x) ...
- App开发流程之通用宏定义及头文件
工欲善其事,必先利其器. 在正式实现各种炫酷的功能和UI前,做好准备工作是提高后续开发效率的必经之路. 所以,这个系列,我不是在各种堆技术,更关注的是“兵马动”之前的“粮草行”,有些繁琐,但当清晰理出 ...
- c语言宏定义
一. #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能理解该命令的本质,总是在此处产生一些困惑,在编程时 ...
- C++宏定义详解
一.#define的基本用法 #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质 ...
- C语言宏定义相关
写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等.下面列举一些成熟软件中常用得宏定义......1,防止一个头文件被重复包含#ifndef COMDEF_H# ...
- iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册
宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...
随机推荐
- 【2017年最新】 iOS面试题及答案
设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情. 1). MVC模式:Model View Control,把模型 视图 控制器 ...
- 【js】深拷贝和浅拷贝区别,以及实现深拷贝的方式
一.区别:简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝. 此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这 ...
- WebGL学习笔记(1)
基本的WebGL图形操作(详细参考教程:https://www.yiibai.com/webgl,需要1周左右熟悉webgl的对象方法以及着色器代码):绘制三角形 drawElements gl.TR ...
- MAMP:MySQL wasn't able to start
MAMP 我点击start server的时候 发现mysql服务器打不开 http://images.cnblogs.com/cnblogs_com/lwwen/1231721/o_11111111 ...
- 解决jquey中当事件嵌套时,内层事件会执行多次的问题
出现情景:当内层事件需要外层事件触发后产生的一些值得时候 情景复现: <!DOCTYPE html> <html lang="en"> <head&g ...
- ACM 2000~2002
ACM 2000 输入三个字符后,按各个字符的ASCⅡ码从小打到的顺序输出这三个字符. import java.util.Scanner; public class Lengxc {public ...
- vscode vue 项目保存运行lint进行代码修正
{ "editor.tabSize": 2, "files.associations": { "*.vue": "vue" ...
- JZOJ 4273. 【NOIP2015模拟10.28B组】圣章-精灵使的魔法语
4273. [NOIP2015模拟10.28B组]圣章-精灵使的魔法语 (File IO): input:elf.in output:elf.out Time Limits: 1000 ms Mem ...
- Python读取 csv文件中文乱码处理
需求:按行解析读取csv文件存入关系型数据库——主要是中文字体解析:遇到的问题:直接解析出来的数据为list形式,而且编码格式为unicode;解决问题:前提了解: 中文编码的规则 —— GB2312 ...
- [NOIP2017]逛公园(DP)
先spfa一遍处理出d[]数组,(从n开始bfs一遍标记可以达到n的点) 题意即,在走最短路的基础上,可以最多多走K长度的路径, 考虑DP,每次剩余可走的长度会因决策而改变,所以考虑dp[i][j]为 ...