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

时间:2012-01-06 21:26:28

标签: c c-preprocessor

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

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

   /* do something */
   return 42;
}

MACRO表格:

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

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

9 个答案:

答案 0 :(得分:25)

只有两个条件可以以任何“合理”的方式运作。

  1. 宏没有return语句。您可以使用do while技巧。

    #define macro(x) do { int y = x; func(&y); } while (0)
    
  2. 您只定位GCC。

    #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; })
    
  3. 如果你解释为什么你必须使用一个宏(你的办公室有“宏星期一”或其他东西吗?)会有所帮助。否则我们无法提供帮助。

答案 1 :(得分:7)

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

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

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

有人用这种方式使用它:

int temp = 3;
A(temp);

预处理后,代码为:

int temp = 3;
{ int temp = temp*2; printf("%d\n", temp); }

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

答案 2 :(得分:6)

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

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

void foo(void)
{
    int a = 42;
    {   
        int b = 42;
        {
            int c = 42; 
        } 
    }
}

答案是肯定的。

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

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

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

答案 3 :(得分:5)

这基本上没问题,除了宏通常用do { ... } while(0)括起来(请查看this question进行解释):

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

此外,只要您的原始fooAlloc函数返回long,您就必须更改宏来以其他方式存储结果。或者,如果您使用GCC,则可以尝试compound statement扩展名:

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

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

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

答案 4 :(得分:2)

Eldar的答案向您展示了宏编程的大部分缺陷以及一些有用的(但非标准的)gcc扩展。

如果你想坚持标准,宏(对于通用性)和inline函数(对于局部变量)的组合可能是有用的。

inline
long fooAlloc(void *f, size_t size)
{
   size_t      i1, i2;
   double   *data[7];

   /* do something */
   return 42;
}


#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T))

在这种情况下,使用sizeof仅在编译时计算类型的表达式而不是其值,因此这不会评估F两次。

BTW,“尺寸”通常应使用size_t输入,而不能使用long或类似内容输入。

编辑:至于Jonathan关于inline函数的问题,我已经写了一些关于inline C99模型的内容,here

答案 5 :(得分:0)

是的,它应该在您使用块结构时起作用,并且临时变量在此块的内部范围内声明。

注意}}冗余之后的最后一个\。

答案 6 :(得分:0)

他们可以。他们经常不应该这样做。

为什么这个功能需要是一个宏?你可以改为内联吗?

答案 7 :(得分:0)

如果您正在使用c ++使用内联,或者使用-c3使用gcc,它将为您内联所有功能。 我仍然不明白为什么你需要宏观化这个功能。

答案 8 :(得分:0)

一个不完美的解决方案:(不适用于递归宏,例如彼此内部有多个循环)

#define JOIN_(X,Y) X##Y
#define JOIN(X,Y) JOIN_(X,Y)
#define TMP JOIN(tmp,__LINE__)

#define switch(x,y) int TMP = x; x=y;y=TMP

int main(){
  int x = 5,y=6;
  switch(x,y);
  switch(x,y);
}
运行预处理器后

将成为:

int main(){
   int x=5,y=6;
   int tmp9 = x; x=y; y=tmp9;
   int tmp10 = x; x=y; y=tmp10;
}