高级宏定义

平时常说的 C 语言三大预处理功能是什么?(吹牛谈资,不能不知) 

答:宏定义;文件包含;条件编译。

说到底,宏定义的实质是什么?

答:替换。

关于宏定义有一点大家一定要了解,就算再复杂,它也只是替换,不做计算,也不做表达式求解。

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

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

答:这是由于在 MAX 和参数 (x, y) 之间存在空格导致的。

从初学者的角度来看,带参数的宏定义和函数十分相似,你作为渐入佳境的大牛,你能否从实现逻辑上指出它俩的不同之处?

答:虽然宏定义也有所谓的”形参“和”实参“,但在宏定义的过程中,并不需要为形参指定类型。这是因为宏定义只是进行机械替换,并不需要为参数分配内存空间

而函数不同,在函数中形参和实参是两个不同的变量,都有自己的内存空间和作用域,调用时是要把实参的值传递给形参。

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

#define SQUARE(x) x * x

答:如果单独传入一个数字,那是没问题的。

但如果你传入一个式子,那么问题就来了。

比如传入 3+2,那么宏定义替换后的结果便是 3+2 * 3+2,显然不能获得预期的计算结果。

比较稳妥的做法给参数也加上小括号:#define SQUARE(x) ((x) * (x))

课堂中留下的问题,请问下面宏定义隐藏着一个难以发现的 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;
}

看,程序完全不按套路出牌:

但如果将宏定义改成函数:

#include <stdio.h>

int square(int x);

int square(int x)
{
        return x * x;
}

int main(void)
{
        int i = 1;

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

        return 0;
}

程序妥妥地按照我们的要求去实现:

这是因为 C 语言默认将参数从右到左的顺序依次入栈,参数和局部变量都是存放在栈中的,还记得吧?

printf("%d 的平方是%d\n", i-1, SQUARE(i++));

SQUARE(i++) 先展开了,变成 ((i++) * (i++))。

由于 ++ 在后,所以是先使用 i 的值,再自增,上面式子的结果是 1,完事之后 i 的值变成了 3(自增了两次)。

所以,第一次打印便是:2 的平方是 1

同样的道理,第二次先展开 SQUARE(i++),此时 i 的值是 3,完事之后变成 5,所以第二次打印便是:4 的平方是 9。

大千世界,无奇不有,学习编程,就要动手!

不要想当然,哈哈哈哈~

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
宏定义是C/C++语言中的一个特性,它可以将一些常用的代码片段或者常量定义为一个宏,从而方便代码编写和维护。除了常规的宏定义,还有一些高级用法,以下是一些常见的高级用法: 1. 带参数的宏定义 可以使用宏定义来定义带有参数的函数。例如: ``` #define MAX(a,b) ((a) > (b) ? (a) : (b)) ``` 这个宏定义定义了一个函数,用于获取两个数中的最大值。在代码中使用该宏的方式如下: ``` int a = 10; int b = 20; int max = MAX(a, b); ``` 在编译时,编译器会将 MAX(a, b) 替换为 ((a) > (b) ? (a) : (b)),相当于直接调用了 MAX 函数。 2. 条件编译 可以使用宏定义来进行条件编译。例如: ``` #define DEBUG #ifdef DEBUG printf("debugging\n"); #endif ``` 在编译时,如果定义了 DEBUG 宏,编译器会将 #ifdef 和 #endif 之间的代码包含进去;否则,这段代码就会被忽略掉。 3. 字符串连接 可以使用宏定义来进行字符串连接。例如: ``` #define CONCAT(a,b) a##b ``` 在代码中使用该宏的方式如下: ``` int ab = CONCAT(a, b); ``` 在编译时,编译器会将 CONCAT(a, b) 替换为 ab,相当于直接将 a 和 b 连接起来。 4. 复杂的宏定义 可以使用宏定义来进行复杂的操作,例如: ``` #define SQUARE(x) ((x) * (x)) #define MAX(a,b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; }) #define PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) ``` 这些宏定义分别用于计算平方、获取两个数中的最大值、格式化输出字符串。其中,MAX 宏使用了 GCC 的扩展语法,可以返回两个参数中的最大值;PRINTF 宏使用了可变参数语法,可以传入不定数量的参数。 总之,宏定义是一个非常强大的工具,可以帮助我们编写出更加简洁、高效的代码。但是,在使用宏定义时需要注意避免一些潜在的问题,如宏定义中可能存在的副作用、宏定义中可能存在的类型错误等,需要谨慎使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倦504

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值