在 C 复合语句中使用宏时

  • 本文关键字:复合语句 c macros
  • 更新时间 :
  • 英文 :


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

#include <stdio.h>
#include <stdlib.h>
#define num_is_positive_odd(num)                           
({                                                         
int __rc = 0;                                          
int __num = (num);                                     

printf("%s:%d: check linenumn", __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 positiven", 
__func__, __num);              
__rc = 1;                             
}                                         
__rc;                                     
})
#define num_is_odd(num)                       
({                                            
int __rc = 0;                             
int __num = (num);                        

if (__num / 2) {                          
printf("%s: number %d is oddn",      
__func__, __num);              
__rc = 1;                             
}                                         
__rc;                                     
})

int main()
{
int num = 4;
if (num_is_positive_odd(num++))
printf("%s: number %d is positive oddn", __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++))
^

'

有人可以帮助解释为什么以及如何修复它吗?谢谢。

这是使用称为语句表达式的 GCC 扩展 — 因此规则特定于 GCC(并且可能模拟 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 linenumn", __func__, 45); if (({ int __rc = 0; int __num = (__num); if (__num > 0) { printf("%s: number %d is positiven", __func__, __num); __rc = 1; } __rc; }) && ({ int __rc = 0; int __num = (__num); if (__num / 2) { printf("%s: number %d is oddn", __func__, __num); __rc = 1; } __rc; })) __rc = 1; __rc; }))
printf("%s: number %d is positive oddn", __func__, num);
return 0;
}

当手动格式化("炼狱"主题的变体(时,可能如下所示:

# 41 "gccm43.c"
int main(void)
{
int num = 4;
if (({ int __rc = 0;
int __num = (num++);
printf("%s:%d: check linenumn", __func__, 45);
if (({ int __rc = 0;
int __num = (__num);
if (__num > 0)
{
printf("%s: number %d is positiven", __func__, __num);
__rc = 1;
}
__rc;
}) &&
({ int __rc = 0; 
int __num = (__num);
if (__num / 2)
{
printf("%s: number %d is oddn", __func__, __num);
__rc = 1;
}
__rc;
}
))
__rc = 1;
__rc;
}
))
printf("%s: number %d is positive oddn", __func__, num);
return 0;
}

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

现在也很明显为什么编译器警告不使用(其中一个(__num(变量(。 外部声明将num++赋值给__num,但初始化的变量从不使用,因为int __num = (__num);的内部出现是指自己而不是外部__num,所以它不被使用。

使用静态内联函数会做得更好——像这样:

#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 positiven", __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 oddn", __func__, num);
rc = 1;
}
return rc;
}
static inline int num_is_positive_odd(int num)
{
int rc = 0;
printf("%s:%d: check linenumn", __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 oddn", __func__, num);
return 0;
}

执行以下宏扩展:

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

这两个都扩展到包含声明的块表达式:

int __num = (num);  

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

int __num = (__num);  

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

顺便说一下,保留以两个下划线开头的名称;您不允许创建它们。因此,宏表现出未定义的行为。这不是你眼前的问题,但是既然你无论如何都必须改变名字来强制卫生,你不妨做对。

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

为了完成 Jonathan Lever 的回答和 rici 的回答:如果你的实际代码需要语句表达式并且不能通过static inline函数来完成(这不太可能,在你问题中显示的函数中也不是这种情况(,你应该考虑在你的宏中生成唯一标识符(使它们更卫生(。一种可能的方法(特定于 GCC!(是使用cpp串联__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 positiven", 
__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函数。通常情况就是这样!

顺便说一句,__COUNTER__语句表达式都是 GNU 扩展(被 GCC 和 Clang 接受(,在 C11 标准 n1570之外。在某些情况下(不是你的(,你可以使用(如果你不想依赖GNU主义__COUNTER__......(标准__LINE__而不是GNU__COUNTER__,并采用约定每行调用一个宏(另见这个(。

最新更新