使用参数定义宏时,“ else”没有先前的“ if”错误

时间:2019-05-01 08:29:09

标签: c nested-if

考虑以下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");    

我不明白为什么编译器会抱怨。该程序实际上转移到了什么?

谢谢

4 个答案:

答案 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)