"#define for if(false){} else for"的可能用途是什么?

pax*_*blo 45 c c-preprocessor

在另一个问题中,我刚刚发现了C智慧的这颗小珍珠:

#define for if (false) {} else for
Run Code Online (Sandbox Code Playgroud)

这导致MSVC为一个非常有效的声明吐出"常量表达式"警告:

for (int i = 0; i <= 10; i++) {...}
Run Code Online (Sandbox Code Playgroud)

我理解为什么 MSVC抱怨,因为它扩展到:

if (false) {} else for (int i = 0; i <= 10; i++) {...}
Run Code Online (Sandbox Code Playgroud)

我只是不明白为什么开发人员会使用那个小片段.有人有想法吗?

Ada*_*eld 92

这是修复旧版Visual C++(v6.0及更早版本)中的错误.在过去,Visual C++已经破坏了关于在for语句中声明的变量的范围规则:

// This compiles in old versions of Visual C++, but it is in fact INVALID C++
for(int i = 0; ...)
{
    ...
}

for(i = 0; ...)
{

}
Run Code Online (Sandbox Code Playgroud)

换句话说,Visual C++给出i了一个范围,好像它是在循环之外声明的,它允许你在循环完成后继续使用它.这导致代码如上面的代码片段.在更多符合标准的编译器中,i不再在第二个for循环的定义范围内,因此编译器会发出有关i未定义的错误.

为了解决这个问题,有些人使用了这个宏(或非常相似的等效宏):

#define for if(0) {} else for
Run Code Online (Sandbox Code Playgroud)

这会将for循环更改为:

if(0)
{
}
else
    for(int i = 0; ...)
    {
        ...
    }
Run Code Online (Sandbox Code Playgroud)

这会将for循环放入一个额外的范围级别,这样for无论Visual C++的bug如何,循环中声明的任何变量都将超出范围.这可以确保相同的代码在Visual C++和符合标准的编译器中一致地正确编译,并且不正确的代码无法一致地正确编译.

另请注意,如果宏被定义为:

// DO NOT USE
#define for if(1) for
Run Code Online (Sandbox Code Playgroud)

然后虽然这会对一些简单的代码产生相同的效果,但它会突然导致以下代码被错误地编译:

if(foo)
    for(...)
    {
        ...
    }
else
    doSomething();
Run Code Online (Sandbox Code Playgroud)

因为如果你扩展宏,你得到这个:

if(foo)
    if(1)
        for(...)
        {
            ...
        }
    else
        doSomething();
Run Code Online (Sandbox Code Playgroud)

else现在与错误匹配if!因此,巧妙地使用if(0) {} else而不是if(1)避免这个问题.

作为最后一点,#define for if(0) {} else for不会导致无限递归,因为预处理器不会递归替换您当前定义的宏.在这种情况下,它只会做一次更换.

  • 非常好的答案. (8认同)

Ear*_*rlz 7

根据快速搜索,它是MSVC中的一个被克服的错误.

据我了解,

for(int i=0...){.....} 
//later at the same scope level in the same function
for(int i=0...){...}

将导致重新定义'i'错误.

如果for语句包含在if语句中,则编译器按原样运行,以便不存在重定义错误(显然它解释'if'但不是'for'的范围级别)

  • 只使用`if(1)for`就是一个bug.假设你有代码`if(condition)for(...){}; 否则do_happy_thing();` - 现在你的代码坏了,找出原因会有很多麻烦. (3认同)
  • MSVC 6.0早于C99标准AFAIK ......"VC 6早于某些标准.它具有'扩展',一旦'我'被定义它存在直到函数结束." (2认同)