C++ FAQ的不安全宏的解释?

Gau*_*v K 31 c c++ c-preprocessor

根据C++ FAQ,宏是邪恶的:

[9.5]为什么我应该使用内联函数而不是普通的旧#define宏?

因为#define宏在四种不同的方面是邪恶的:邪恶的#1,邪恶的#2,邪恶的#3和邪恶的#4.有时你应该使用它们,但它们仍然是邪恶的.与#define宏不同,内联函数避免了臭名昭着的宏错误,因为内联函数总是只评估每个参数一次.换句话说,调用内联函数在语义上就像调用常规函数一样,只是更快:

// A macro that returns the absolute value of i
#define unsafe(i)  \
        ( (i) >= 0 ? (i) : -(i) )

// An inline function that returns the absolute value of i
inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  ans = unsafe(x++);   // Error! x is incremented twice
  ans = unsafe(f());   // Danger! f() is called twice

  ans = safe(x++);     // Correct! x is incremented once
  ans = safe(f());     // Correct! f() is called once
}
Run Code Online (Sandbox Code Playgroud)

与宏不同,还会检查参数类型,并正确执行必要的转换.

宏对你的健康有害; 除非必须,否则不要使用它们.

有人可以解释为什么unsafe(x++)增量x两次?我无法搞清楚.

lus*_*oog 69

通过预处理器运行它会显示问题.使用gcc -E(也可以使用cpp -P,-P选项也可以抑制生成的#行),

inline
int safe(int i)
{
  return i >= 0 ? i : -i;
}

int f();

void userCode(int x)
{
  int ans;

  //    increment 1      increment 2 (one of these)
  //        |             |     |
  //        V             V     V
  ans = ( (x++) >= 0 ? (x++) : -(x++) );
  ans = ( (f()) >= 0 ? (f()) : -(f()) );

  ans = safe(x++);
  ans = safe(f());
}
Run Code Online (Sandbox Code Playgroud)

作为无艺术的噪音音符,该功能f()也被unsafe宏调用两次.也许它是纯粹的(没有副作用)所以它本身并没有.但仍然不是最理想的.

因此,因为内联函数通常比类似函数的宏更安全,因为它们与其他基本元素在同一语义级别上工作:变量和表达式; 对于显式常数,enums通常可以更整洁 ; 好用是什么?

设置常量仅在编译时知道.您可以在编译时从命令行定义宏.代替

#define X 12
Run Code Online (Sandbox Code Playgroud)

在源文件中,您可以添加

-DX=12
Run Code Online (Sandbox Code Playgroud)

cc命令.您也可以#undef X从命令行中使用-UX.

这允许条件编译之类的东西,例如.

#if X
   do this;
#else
   do that;
#endif
   while (loop);
Run Code Online (Sandbox Code Playgroud)

由makefile控制,它本身可能是用configure脚本生成的.

X-Macros.对于X-Macros,IMO最引人注目的用途是将enum标识符与可打印字符串相关联.虽然它起初看起来很有趣,但它减少了这些并行定义的重复和同步问题.

#define NAMES(_) _(Alice) _(Bob) _(Caravaggio) _(DuncanIdaho)
#define BARE(_) _ ,
#define STRG(_) #_ ,
enum { NAMES(BARE) };
char *names[] = { NAMES(STRG) };
Run Code Online (Sandbox Code Playgroud)

请注意,您可以将宏的名称作为参数传递给另一个宏,然后使用该参数调用传递的宏,就好像它本身就是一个宏(因为它一个宏).有关X-Macros的更多信息,请参阅此问题.

  • ......而且,众所周知,这是一个UB! (2认同)
  • @BЈовић但是`?`引入了序列点对吗? (2认同)
  • @Koushik更正 - 在`?`(即在评估条件之后)但在执行两个分支之一之前有一个序列点. (2认同)

Dre*_*ann 17

编译程序之前有效地进行复制/粘贴.

unsafe(x++)
Run Code Online (Sandbox Code Playgroud)

会成为

( (x++) >= 0 ? (x++) : -(x++) )
Run Code Online (Sandbox Code Playgroud)


And*_*mas 10

预编译器在编译之前替换宏.

编译器看到了这个:

  ( (x++) >= 0 ? (x++) : -(x++) )
Run Code Online (Sandbox Code Playgroud)