【问题标题】:Function-like macros and strange behavior类似函数的宏和奇怪的行为
【发布时间】:2013-02-08 16:38:30
【问题描述】:

我已经开始阅读《Effective C++》,在第 2 项中的某个时间点,提到了以下内容:

// call f with the maximum of a and b
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

...

int a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a is incremented twice
CALL_WITH_MAX(++a, b+10); // a is incremented once

这里是在调用 f 之前 a 递增的次数 取决于与之比较的对象!

确实,如果我在f 中使用简单的打印语句,第一次调用会打印 7,但我终其一生都无法弄清楚原因。我错过了一些明显的东西吗?

【问题讨论】:

  • @nhahtdh 不仅仅是“想”,宏简单的文本替换。对于许多编译器(例如 g++),您可以要求在预处理器通过后查看代码。

标签: c++ macros effective-c++


【解决方案1】:

使用g++ -E myprog.cpp(如果您不使用whatever-your-compiler-is,则将g++ 替换为whatever-your-compiler-is)- 它适用于几乎所有编译器,它会在预处理后生成实际的东西。

这是一个很好的例子,说明了为什么你不应该使用宏来做函数类型的事情。

如果您使用内联函数,您(可能)会得到更多期望:

 inline void CallWithMax(int a, int b) 
 {
     f((a) > (b) ? (a) : (b));
 }

任何体面的编译器都应该至少能够像宏一样高效地执行此操作,另外还有一个优势是您的 ab 在调用代码中被评估一次,并且不会发生任何“奇怪”的事情。

如果您使用调试符号构建代码,您也可以单步执行内联函数,因此如果您想查看函数内部的实际值 ab,您可以这样做。宏,因为它们在源代码中展开到原来的地方,所以你无法真正看到里面发生了什么。

【讨论】:

    【解决方案2】:

    编译器会用您传入的内容逐字逐句替换宏。所以你最终得到了

    int a = 5, b = 0;
    f((++a) > (b) ? (++a) : (b));
    f((++a) > (b+10) ? (++a) : (b+10));
    

    【讨论】:

    • 这个故事的寓意是:永远不要在宏体内多次引用宏参数,否则会发生坏事
    • @NikBougalis - 道德是永远不要将宏应用于具有副作用的参数
    • 这也许是更好的表达方式。
    • 啊啊啊是啊!!谢谢你,我被困住了
    猜你喜欢
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多