考虑以下C代码。
#include <stdio.h>
#define foo(x) if (x>0) printf("Ouch\n");
int main()
{
int a = 4;
int b = -3;
if(a>b)
foo(b) ;
else
printf("Arrg!\n");
printf("thanks\n");
return 0;
}
程序运行时,出现错误提示error: ‘else’ without a previous ‘if’
。当根据宏定义将foo(b)
替换为if (b>0) printf("Ouch\n")
时,用大括号编写程序是否不应该转换为以下代码?
if(a>b){
if(x>0){
printf("Ouch\n");
}
}
else{
printf("Arrh!\n");
}
printf("thanks\n");
我不明白为什么编译器会抱怨。该程序实际上转移到了什么?
谢谢
答案 0 :(得分:7)
不,它变成了这段代码:
if (a > b)
if (b > 0) printf("Ouch\n");;
else
printf("Arrg!\n");
请注意,("Ouch\n")
之后有两个分号。这就是破坏代码的原因。 else
紧跟第二个分号,它是一个空语句,而不是之前的if
语句。原因是您在宏定义中使用了分号,而在宏定义中使用了分号。
我建议像this answer一样,将类似函数的宏的语句放在自己的块中。但这是一般性的建议,在此特定示例中,最好根本不要使用宏。
答案 1 :(得分:5)
问题是您的宏扩展到
if(a>b)
if (b>0) printf("Ouch\n"); ;
else
printf("Arrg!\n");
//...
,并且由于额外的else
而不能与;
一起使用。 (也可以在x
参数(#define foo(x) if ((x)>0) printf("Ouch\n")
后面加上括号)。
如果您从宏中丢失了;
,则会得到不同的解析:else
将与内部if
相匹配,如下所示:
if(a>b){ /*braces inserted to show the interpretation*/
if (b>0) printf("Ouch\n");
else printf("Arrg!\n");
}
,虽然您可以通过悬空else
制作宏来解决问题
#define foo(x) if ((x)>0) printf("Ouch\n"); else
//a ; after the macro else would complete the `else` with an empty branch
尤其是在其他if
-else
内部使用它会在编译gcc
和类似选项时触发clang
/ -Wall
之类的编译器中的警告,因此最好处理方式是惯用的
#define macro() do{/*macro_body*/}while(0)
以您的情况
#define foo(x) do{ if ((x)>0) printf("Ouch\n"); }while(0)
有些人喜欢总是将复合语句(由{
}
围着if
/ else
语句使用,虽然这也可以解决问题,但我觉得如果您制作类似函数的宏供他人使用,则最好不要在其上施加样式。
答案 2 :(得分:4)
我的建议是忽略有关如何修复该宏的任何建议,真实问题是您完全使用了宏。
几乎没有 没有 的理由在现代C语言中将宏(实际上只是稍微有点愚蠢的文本替换)用于除条件编译以外的任何操作。
使用实函数还可以解决所有奇怪的边缘情况,例如:
#define calc(x) x * x
:
int a = 7;
int b = calc(a + 1); // a + (1 * a) + 1, NOT (a + 1) * (a + 1)
如果您希望该宏可靠地工作,则需要将宏设置为类似((x) * (x))
,但是即使那个也将失败,并且更加复杂,例如b = calc(a++)
。 / p>
因此,简而言之,您在代码中应该 应该是:
void foo(int x) {
if (x > 0)
printf("Ouch\n");
}
如果 do 需要能够在任意位置使用函数宏 (裸语句,带括号的if块中的语句,无括号的if块中的语句,带括号和不带括号的while语句等),您必须诉诸于怪异的宏,例如(确保使用#include <stdbool.h>
才能访问false
,当然):
#define XYZZY(s) do {plugh(s);} while (false)
不过,我会强烈建议首先使它们起作用。
答案 3 :(得分:2)
在{ }
和if
块之后使用else
以避免这种问题一直被认为是安全和良好的做法。
正确的是
if(a>b) {
foo(b) ; /* keep inside { } */
}
else {
printf("Arrg!\n");
}
编写时,程序不应转换为以下代码 大括号?
否,编译器不会手动放置花括号。替换宏后,gcc -E test.c
看起来像
int main()
{
int a = 4;
int b = -3;
if(a>b)
if (b>0) printf("Ouch\n"); ; /* extra semicolon causes the issue */
else /* there is no if for this else block, previous one terminated by extra ; in above if */
printf("Arrg!\n");
printf("thanks\n");
return 0;
}
示例代码:
#include <stdio.h>
#define foo(x) if ((x)>0) printf("Ouch\n") /* if condition was wrong.. instead of x use (x) */
int main(void)
{
int a = 4;
int b = -3;
if(a>b)
{ /* always keep curly braces even though there is only one statement after if */
foo(b);
}
else
{
printf("Arrg!\n");
}
printf("thanks\n");
return 0;
}
尽管我建议像下面这样定义MACRO
#define foo(x) \
do { \
if((x) > 0) \
printf("Ouch\n"); \
} while(0)