在c复合语句中使用宏

时间:2018-06-08 03:28:03

标签: c macros

我写了一个示例来显示我的问题,这里是使用嵌套宏来调用来检查数字是正数还是奇数。我知道使用这个调用是多余的,但正如我之前所说,它只是表明如果以嵌套方式使用宏,则存在一些问题:

#include <stdio.h>
#include <stdlib.h>

#define num_is_positive_odd(num)                           \
({                                                         \
    int __rc = 0;                                          \
    int __num = (num);                                     \
                                                           \
    printf("%s:%d: check linenum\n", __func__, __LINE__);  \
    if (num_is_positive(__num) && num_is_odd(__num))       \
        __rc = 1;                                          \
    __rc;                                                  \
 })

#define num_is_positive(num)                  \
({                                            \
    int __rc = 0;                             \
    int __num = (num);                        \
                                              \
    if (__num > 0) {                          \
        printf("%s: number %d is positive\n", \
               __func__, __num);              \
        __rc = 1;                             \
    }                                         \
    __rc;                                     \
 })

#define num_is_odd(num)                       \
({                                            \
    int __rc = 0;                             \
    int __num = (num);                        \
                                              \
    if (__num / 2) {                          \
        printf("%s: number %d is odd\n",      \
               __func__, __num);              \
        __rc = 1;                             \
    }                                         \
    __rc;                                     \
 })



int main()
{
    int num = 4;

    if (num_is_positive_odd(num++))
        printf("%s: number %d is positive odd\n", __func__, num);

    exit(0);
}

使用命令编译时:gcc -Wunused-variable chk_comps.c

显示错误:

chk_comps.c: In function ‘main’:
chk_comps.c:7:9: warning: unused variable ‘__num’ [-Wunused-variable]
     int __num = (num);                                     \
         ^
chk_comps.c:47:9: note: in expansion of macro ‘num_is_positive_odd’
     if (num_is_positive_odd(num++))
         ^

`

有人可以帮忙解释为什么以及如何解决这个问题?感谢。

3 个答案:

答案 0 :(得分:3)

您执行以下宏扩展:

if (num_is_positive(__num) && num_is_odd(__num)) 

这两个扩展到阻止包含声明的表达式:

int __num = (num);  

其中num是内部宏的宏参数。由于实际的宏参数为__num,因此生成的替换文本为

int __num = (__num);  

声明完成后和定义之前,C声明的范围即开始。因此,该声明中的__num都引用相同的局部变量(因此初始化为未初始化的值)。同时,外部块中的__num完全被内部块中的__num遮蔽,并且正如编译器所说,未使用。如果您指定-Wall而不是仅启用一个警告,编译器可能会警告您关于未初始化的初始化,这可能是另一个线索。

顺便说一句,保留以两个下划线开头的名称;你不被允许创建它们。所以你的宏表现出不确定的行为。这不是你迫在眉睫的问题,但是因为无论如何都要改变名称以强加hygiene,你也可以这样做。

使用static inline函数而不是宏来解决此问题。认真。函数具有可预测的范围,与宏不同,正如您所见,它的扩展是不卫生的。它们需要您更少的思考,更容易调试,阅读和理解,并且不会慢一些。

答案 1 :(得分:3)

这是使用名为statement expressions的GCC扩展 - 因此规则特定于GCC(可能是Clang模拟GCC)。

如果您运行gcc -E,则可以看到main()的原始输出(我添加了void)是:

# 41 "gccm43.c"
int main(void)
{
    int num = 4;

    if (({ int __rc = 0; int __num = (num++); printf("%s:%d: check linenum\n", __func__, 45); if (({ int __rc = 0; int __num = (__num); if (__num > 0) { printf("%s: number %d is positive\n", __func__, __num); __rc = 1; } __rc; }) && ({ int __rc = 0; int __num = (__num); if (__num / 2) { printf("%s: number %d is odd\n", __func__, __num); __rc = 1; } __rc; })) __rc = 1; __rc; }))
        printf("%s: number %d is positive odd\n", __func__, num);
    return 0;
}

,当手动格式化(主题为&#39;炼狱&#39;的变体)时,可能如下所示:

# 41 "gccm43.c"
int main(void)
{
    int num = 4;

    if (({ int __rc = 0;
           int __num = (num++);
           printf("%s:%d: check linenum\n", __func__, 45);
           if (({ int __rc = 0;
                  int __num = (__num);
                  if (__num > 0)
                  {
                      printf("%s: number %d is positive\n", __func__, __num);
                      __rc = 1;
                  }
                  __rc;
                }) &&
                ({ int __rc = 0; 
                   int __num = (__num);
                   if (__num / 2)
                   {
                       printf("%s: number %d is odd\n", __func__, __num);
                       __rc = 1;
                   }
                   __rc;
                 }
               ))
           __rc = 1;
           __rc;
         }
       ))
        printf("%s: number %d is positive odd\n", __func__, num);
    return 0;
}

int __num = (__num);行有问题;你正在用它自己初始化变量,它并没有真正做好(在初始化之前和之后值是不确定的)。您还使用(__num / 2)来确定__num是否为奇数,这是检测奇数的奇怪方法;你应该使用(__num % 2)

现在也很明显为什么编译器警告不使用__num(变量)之一。外部声明将num++分配给__num,但永远不会使用初始化变量,因为int __num = (__num);的内部引用是指自己而不是外部__num,所以它< em> isn&#t; t 使用。

使用静态内联函数可以做得更好 - 就像这样:

#include <stdio.h>
#include <stdlib.h>

static inline int num_is_positive(int num)
{
    int rc = 0;

    if (num > 0)
    {
        printf("%s: number %d is positive\n", __func__, num);
        rc = 1;
    }
    return rc;
}

static inline int num_is_odd(int num)
{
    int rc = 0;

    if (num % 2)        // BUG fixed
    {
        printf("%s: number %d is odd\n", __func__, num);
        rc = 1;
    }
    return rc;
}

static inline int num_is_positive_odd(int num)
{
    int rc = 0;

    printf("%s:%d: check linenum\n", __func__, __LINE__);
    if (num_is_positive(num) && num_is_odd(num))
        rc = 1;
    return rc;
}

int main(void)
{
    int num = 4;

    if (num_is_positive_odd(num++))
        printf("%s: number %d is positive odd\n", __func__, num);

    return 0;
}

答案 2 :(得分:1)

要同时完成Jonathan Leffer's answerrici's one:如果您的实际代码需要statement expressions且无法通过static inline 函数完成(这不太可能)而不是你在问题中显示的函数中的情况),你应该考虑在宏中生成唯一标识符(使它们更加hygienic)。一种可能的方式(特定于GCC!)是使用cpp concatenation __COUNTER__,如下所示:

#define num_is_positive_count(num,Count)     \
({                                           \
    int rc_##Count = 0;                      \
    int num_##Count = (num);                 \
                                             \
    if (num_##Count > 0) {                   \
        printf("%s:number %d is positive\n", \
               __func__, num_##Count);       \
        rc_##Count = 1;                      \
    }                                        \
    rc_##Count;                              \
 })

(顺便说一句,我正在避免以_开头的标识符)

然后你需要一个双重间接

 #define num_is_positive_count2(num,Count) \
    num_is_positive_count(num,Count)
 #define num_is_positive(num) num_is_positive_count2(num,__COUNTER__)

但是,如果可以,您最好使用static inline功能。通常就是这种情况!

BTW __COUNTER__语句表达式都是GNU扩展(由GCC&amp; Clang接受),之外C11标准n1570。在某些情况下(不在您的情况下),您可以使用(如果您不想依赖于GNU-ism __COUNTER__ ...)标准__LINE__而不是GNU {{ 1}}并采用约定来每行调用一个宏(另请参阅this)。