我写了一个示例来显示我的问题,这里是使用嵌套宏来调用来检查数字是正数还是奇数。我知道使用这个调用是多余的,但正如我之前所说,它只是表明如果以嵌套方式使用宏,则存在一些问题:
#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++))
^
`
有人可以帮忙解释为什么以及如何解决这个问题?感谢。
答案 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 answer和rici'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)。