'else' 在定义带有参数的宏时没有先前的 'if' 错误
'else' without previous 'if' error when defining macro with arguments
考虑下面的 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");
我不明白为什么编译器会抱怨。程序实际转移到什么地方?
谢谢
不,它正在变成这个代码:
if (a > b)
if (b > 0) printf("Ouch\n");;
else
printf("Arrg!\n");
注意("Ouch\n")
后有两个个分号。这就是破坏代码的原因。 else
跟在第二个分号之后,它是一个空语句,而不是之前的 if
语句。原因是您的宏定义中有一个分号,而您调用宏的地方有另一个分号。
我建议将类函数宏的语句放在它自己的块中,如 所建议的那样。但这是一般性建议,在这个具体示例中,最好根本没有宏。
在 if
和 else
块之后使用 { }
以避免此类问题一直被认为是安全和良好的做法。
正确的是
if(a>b) {
foo(b) ; /* keep inside { } */
}
else {
printf("Arrg!\n");
}
shouldn't the program transfer into the following code when written
with braces?
不,编译器不会手动放置大括号。宏替换后 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)
我的建议是忽略任何关于如何修复那个宏的建议,真正的问题是你正在使用这个宏完全没有。
几乎没有在现代语言中使用宏的理由(实际上只是稍微不那么愚蠢的文本替换) 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))
,但即使 that 也会因更复杂而失败,例如 b = calc(a++)
.
所以,简而言之,您应该在您的代码中包含的内容是:
void foo(int x) {
if (x > 0)
printf("Ouch\n");
}
如果你做需要能够使用函数宏任何地方(裸语句,花括号if块中的语句,语句在一个无支撑的 if 块中,有支撑和无支撑的 while 语句等等),你必须求助于奇怪的宏,例如(当然要确保 #include <stdbool.h>
才能访问 false
):
#define XYZZY(s) do {plugh(s);} while (false)
但是,我强烈建议首先让它们发挥作用。
问题是您的宏扩展为
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
中,在编译 -Wall
和类似选项时会在 gcc
/clang
等编译器中触发警告,所以最好的处理方法是使用惯用语
#define macro() do{/*macro_body*/}while(0)
你的情况
#define foo(x) do{ if ((x)>0) printf("Ouch\n"); }while(0)
有些人喜欢总是将复合语句(由 {
}
包围)与 if
/else
语句一起使用,虽然这也可以解决问题,我觉得如果你做一个类似函数的宏给别人用,最好不要强行给他们一个样式。
考虑下面的 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");
我不明白为什么编译器会抱怨。程序实际转移到什么地方?
谢谢
不,它正在变成这个代码:
if (a > b)
if (b > 0) printf("Ouch\n");;
else
printf("Arrg!\n");
注意("Ouch\n")
后有两个个分号。这就是破坏代码的原因。 else
跟在第二个分号之后,它是一个空语句,而不是之前的 if
语句。原因是您的宏定义中有一个分号,而您调用宏的地方有另一个分号。
我建议将类函数宏的语句放在它自己的块中,如
在 if
和 else
块之后使用 { }
以避免此类问题一直被认为是安全和良好的做法。
正确的是
if(a>b) {
foo(b) ; /* keep inside { } */
}
else {
printf("Arrg!\n");
}
shouldn't the program transfer into the following code when written with braces?
不,编译器不会手动放置大括号。宏替换后 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)
我的建议是忽略任何关于如何修复那个宏的建议,真正的问题是你正在使用这个宏完全没有。
几乎没有在现代语言中使用宏的理由(实际上只是稍微不那么愚蠢的文本替换) 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))
,但即使 that 也会因更复杂而失败,例如 b = calc(a++)
.
所以,简而言之,您应该在您的代码中包含的内容是:
void foo(int x) {
if (x > 0)
printf("Ouch\n");
}
如果你做需要能够使用函数宏任何地方(裸语句,花括号if块中的语句,语句在一个无支撑的 if 块中,有支撑和无支撑的 while 语句等等),你必须求助于奇怪的宏,例如(当然要确保 #include <stdbool.h>
才能访问 false
):
#define XYZZY(s) do {plugh(s);} while (false)
但是,我强烈建议首先让它们发挥作用。
问题是您的宏扩展为
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
中,在编译 -Wall
和类似选项时会在 gcc
/clang
等编译器中触发警告,所以最好的处理方法是使用惯用语
#define macro() do{/*macro_body*/}while(0)
你的情况
#define foo(x) do{ if ((x)>0) printf("Ouch\n"); }while(0)
有些人喜欢总是将复合语句(由 {
}
包围)与 if
/else
语句一起使用,虽然这也可以解决问题,我觉得如果你做一个类似函数的宏给别人用,最好不要强行给他们一个样式。