《带你学C带你飞学习笔记》—— SE40高级宏(课后题未完成)

本文详细解析了C语言中的宏定义概念,包括宏定义的本质、预处理功能、不带参数及带参数的宏定义的区别,并通过实例展示了宏定义在实际应用中的注意事项。

知识点回顾

1. 宏定义的本质

再牛逼的宏定义,说到底也是机械替换

2. C语言三大预处理功能
宏定义、文件包含、条件编译

3.不带参数的宏定义
宏定义分为带参数和不带参数两种情况,不带参数的情况就是我们熟悉的直接替换操作。

例如;

#define PI 3.14

这个宏定义的作用是把程序中出现的PI在预处理阶段全部替换成3.14.

4. 带参数的宏定义

C语言允许宏定义带有参数,在宏定义中的参数为形式参数,在宏调用中的参数为实际参数,这点和函数有些类似
例如:

#define MAX(x, y) (((x) > (y)) ? (x) : (y))

这个宏定义的作用是求出x和y两个参数中比较大的一个。

测试题

0. 平时常说的 C 语言三大预处理功能是什么?(吹牛谈资,不能不知)
答:宏定义、文件包含、条件编译

1. 说到底,宏定义的实质是什么?
答:机械替换。
关于宏定义有一点大家一定要了解,就算再复杂,它也只是替换,不做计算,也不做表达式求解。

2. 请问下面的宏为什么无法比较 x 和 y 两个参数的大小?

#define MAX (x, y) (((x) > (y)) ? (x) : (y))

答:MAX和(x,y)之间有空格。
在宏定义的时候,宏的名字和参数列表之间不能有空格,否则会被当做是无参数的宏定义。

3. 从初学者的角度来看,带参数的宏定义和函数十分相似,你作为渐入佳境的大牛,你能否从实现逻辑上指出它俩的不同之处?
答:宏定义是简单的机械替换,宏定义是在堆上做文章。
答:虽然宏定义也有所谓的”形参“和”实参“,但在宏定义的过程中,并不需要为形参指定类型。这是因为宏定义只是进行机械替换,并不需要为参数分配内存空间。
而函数不同,在函数中形参和实参是两个不同的变量,都有自己的内存空间和作用域,调用时是要把实参的值传递给形参。

4. 下面这个宏定义存在什么安全隐患?

#define SQUARE(x) x * x

答:当x为i++时,i为叠加两次。
正解:如果单独传入一个数字,那是没问题的,但是如果你传入一个式子,那么问题就来了,比如传入3+2,那么宏定义替换后的结果便是3+2*3+2,显然不能获得预期的结果

比较稳妥的做法是给参数加上小括号;

#define SQUARE(x) ((x) * (x))

5. 课堂中留下的问题,请问下面宏定义隐藏着一个难以发现的 BUG,请你找出来?

#define SQUARE(x) ((x) * (x))

答:当宏定义的参数遇到++、–运算符时,BUG出现。
下面是出发BUG的代码:

#include <stdio.h>

#define SQUARE(x) ((x) * (x))

int main(void)
{
        int i = 1;

        while (i <= 5)
        {
                printf("%d 的平方是%d\n", i-1, SQUARE(i++));
        }

        return 0;
}

答:在此宏定义调用时,是从右往左进行计算的。
这是因为 C 语言默认将参数从右到左的顺序依次入栈,参数和局部变量都是存放在栈中的,还记得吧?
SQUARE(i++) 先展开了,变成 ((i++) * (i++))
由于 ++ 在后,所以是先使用 i 的值,再自增,上面式子的结果是 1,完事之后 i 的值变成了 3(自增了两次)。
所以,第一次打印便是:2 的平方是 2。
同样的道理,第二次先展开 SQUARE(i++),此时 i 的值是 3,完事之后变成 5,所以第二次打印便是:4 的平方是 12。

动动手

逆波兰法计算器大神代码缺失

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值