C宏可以包含临时变量吗?

Hom*_*lli 11 c c-preprocessor

我有一个我需要宏功能的功能.该函数包含临时变量,我不记得是否有关于在宏替换中使用临时变量的规则.

long fooAlloc(struct foo *f, long size)
{
   long      i1, i2;
   double   *data[7];

   /* do something */
   return 42;
}
Run Code Online (Sandbox Code Playgroud)

MACRO表格:

#define ALLOC_FOO(f, size) \
{\
   long      i1, i2;\
   double   *data[7];\
\
   /* do something */ \
}
Run Code Online (Sandbox Code Playgroud)

这个可以吗?(即没有令人讨厌的副作用 - 除了通常的那些:不是"类型安全"等).顺便说一句,我知道"宏是邪恶的" - 在这种情况下我只需要使用它 - 没有多少选择.

Die*_*Epp 26

只有两个条件,它以任何"合理"的方式工作.

  1. 该宏没有return语句.你可以使用这个do while技巧.

    #define macro(x) do { int y = x; func(&y); } while (0)
    
    Run Code Online (Sandbox Code Playgroud)
  2. 你只针对GCC.

    #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; })
    
    Run Code Online (Sandbox Code Playgroud)

如果你解释为什么你必须使用一个宏(你的办公室有"宏星期一"或什么?)会有所帮助.否则我们无法真正帮助.


oua*_*uah 7

C宏只是(相对简单的)文本替换.

所以你可能会问的问题是:我可以在函数中创建块(也称为复合语句),如下例所示吗?

void foo(void)
{
    int a = 42;
    {   
        int b = 42;
        {
            int c = 42; 
        } 
    }
}
Run Code Online (Sandbox Code Playgroud)

答案是肯定的.

现在正如@DietrichEpp在他的回答中提到的那样,如果宏是一个像你的例子中的复合语句,那么将宏语句括起来do { ... } while (0)而不是仅仅是一个好的做法{ ... }.下面的链接解释do { ... } while (0)了宏试图阻止的情况:

http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

此外,当你编写类似函数的宏时,总是问自己是否有这样做的真正优势,因为大多数情况下编写函数更好.


ugo*_*ren 7

首先,我强烈推荐内联函数.宏可以做的事情很少而且他们做不到,而且他们更有可能做你期望的事情.

宏的一个陷阱,我在其他答案中没有看到,是变量名称的阴影.
假设你定义了:

#define A(x) { int temp = x*2; printf("%d\n", temp); }
Run Code Online (Sandbox Code Playgroud)

有人用这种方式使用它:

int temp = 3;
A(temp);
Run Code Online (Sandbox Code Playgroud)

预处理后,代码为:

int temp = 3;
{ int temp = temp*2; printf("%d\n", temp); }
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为内部温度影响外部.
常见的解决方案是调用变量__temp,假设没有人会使用此名称定义变量(这是一个奇怪的假设,假设你刚才这样做了).


Eld*_*mov 6

这通常是正常的,除了通常附带宏do { ... } while(0)(看一下这个问题的解释):

#define ALLOC_FOO(f, size) \
    do { \
        long      i1, i2;\
        double   *data[7];\
        /* do something */ \
    } while(0)
Run Code Online (Sandbox Code Playgroud)

此外,只要您的原始fooAlloc函数返回,long您必须更改您的宏以存储其他方式的结果.或者,如果您使用GCC,您可以尝试复合语句扩展:

#define ALLOC_FOO(f, size) \
    ({ \
        long      i1, i2;\
        double   *data[7];\
        /* do something */ \
        result; \
    })
Run Code Online (Sandbox Code Playgroud)

最后,您应该关注扩展宏参数的可能副作用.通常的模式是为块内的每个参数定义一个临时变量,并使用它们:

#define ALLOC_FOO(f, size) \
    ({ \
        typeof(f) _f = (f);\
        typeof(size) _size = (size);\
        long      i1, i2;\
        double   *data[7];\
        /* do something */ \
        result; \
    })
Run Code Online (Sandbox Code Playgroud)