16

在类 C 语言中,我们习惯于使用类似于以下的 if 语句:

if(x == 5) {
    //do something
}
else if(x == 7) {
    //do something else
}
else if(x == 9) {
    //do something else
} else {
    //do something else
}

我的问题是,编译器是否以这种方式看到 if 语句,或者它最终被解释为:

if(x == 5) {
    //do something
}
else {
    if(x == 7) {
        //do something
    }
    else {
        if(x == 9) {
            //do something
        }
        else {
            //do something else
        }
    }
}

编辑:我意识到虽然这个问题在我脑海中是有道理的,但对于其他普通民众来说,它可能听起来相当愚蠢。我更多地指的是 AST 的外观以及“else-if”语句是否有任何特殊的 AST 案例,或者是否将其编译为级联 if/else 块。

4

5 回答 5

17

它们相当于 C 编译器。C中没有特殊else if的语法。第二个if只是另一个if语句。


为了更清楚,根据 C99 标准,if 语句定义为

selection-statement:
    if (expression) statement
    if (expression) statement else statement
    switch (expression) statement

复合语句定义为

compound-statement:
    {block-item-list(opt) }
block-item-list:
    block-item
    block-item-list block-item
block-item:
    declaration
    statement

当编译器前端尝试理解源代码文件时,它通常遵循以下步骤:

  1. 词法分析:将纯文本源代码转换为“令牌”列表
  2. 语义分析:解析令牌列表并生成抽象语法树(AST)

然后将树传递给编译器中间端(优化)或后端(生成机器码)

在您的情况下,此 if 语句

if(x == 7) {
    //do something else
} else if(x == 9) {
    //do something else
} else {
    //do something else
}

被解析为选择语句中的选择语句,

    selection-stmt
    /     |      \
 exp     stmt     stmt
  |       |        |
 ...     ...    selection-stmt
                /      |      \
              exp     stmt    stmt
               |       |       |
              ...     ...     ...

和这个

if(x == 7) {
    //do something else
} else {
    if(x == 9) {
        //do something else
    } else {
        //do something else
    }
}

是选择语句中的复合语句中的相同选择语句:

    selection-stmt
    /     |      \
 exp     stmt     stmt
  |       |        |
 ...     ...    compound-stmt
                      |
                block-item-list
                      |
                  block-item
                      |
                     stmt
                      |
                selection-stmt
                /      |      \
               exp    stmt    stmt
                |      |       |
               ...    ...     ...

所以他们有不同的AST。但它对编译器后端没有任何影响:正如您在 AST 中看到的那样,没有结构上的变化。

于 2013-06-09T03:07:41.723 回答
12

在 C 和 C++ 中,将语句包含在冗余对中{}不会改变程序的语义。这个说法

a = b;

相当于这个

{ a = b; }

相当于这个

{{ a = b; }}

和这个

{{{{{ a = b; }}}}}

冗余{}对编译器完全没有影响。

在您的示例中,第一个版本和第二个版本之间的唯一区别是您添加到后者中的一堆冗余{},就像我在a = b上面的示例中所做的那样。你多余的{}改变绝对没有。您提供的两个版本的代码之间没有明显的区别,这使您的问题基本上毫无意义。

如果您想问其他问题,请澄清您的问题或更正代码。

于 2013-06-09T04:12:23.790 回答
1

实际上,这两个代码片段是相同的。通过意识到“if”语句的语法如下所示,您可以了解为什么会这样:

if <expression>
    <block>
else
    <block>

NOTE that <block> may be surrounded by curly braces if necessary.

因此,您的代码分解如下。

// if <expression>
if (x == 5)

// <block> begin
{
    //do something
}
// <block> end

// else
else

// <block> begin
if(x == 7) {
    //do something else
}
else if(x == 9) {
    //do something else
} else {
    //do something else
}
// <block> end

现在,如果您在“else”的块周围加上花括号,正如语言所允许的那样,您最终会得到第二种形式。

// if <expression>
if (x == 5)

// <block> begin
{
    //do something
}
// <block> end

// else
else

// <block> begin
{
    if(x == 7) {
        //do something else
    }
    else if(x == 9) {
        //do something else
    } else {
        //do something else
    }
}
// <block> end

如果您对所有“if else”子句重复执行此操作,您最终会得到您的第二种形式。这两段代码完全相同,编译器以完全相同的方式查看。

于 2013-06-09T03:21:33.620 回答
0

更接近第一个,但问题并不完全适合。

当一个程序编译时,它会经历几个阶段。第一阶段是词法分析,第二阶段是句法分析。词法分析分析文本,将其分成标记。然后语法分析查看程序的结构,并构造抽象语法树(AST)。这是在编译期间创建的底层语法结构。

因此,基本上,if 和 if-else 以及 if-elseif-else 语句最终都会被编译器构造成抽象语法树 (AST)。

这是关于 AST 的维基百科页面:https ://en.wikipedia.org/wiki/Abstract_syntax_tree

编辑:实际上,if/if else 语句可能形成更接近 AST 中的第二个语句。我不太确定,但如果它在底层表示为类似二叉树的条件分支结构,我不会感到惊讶。如果您有兴趣更深入地了解它,可以对编译器理论的解析方面进行一些研究。

于 2013-06-09T02:59:48.130 回答
0

请注意,虽然您的第一条语句是根据 if-else “阶梯”约定缩进的,但实际上显示真正嵌套的“正确”缩进是这样的:

if(x == 5) {
    //do something
} else 
  if(x == 7) {              // <- this is all one big statement
    //do something else
  } else 
    if(x == 9) {            // <- so is this
      //do something else
    } else {
      //do something else
    }

缩进是空格;它对编译器没有任何意义。在第一个之后你有else一个重要的if声明。由于它只是一个语句,因此它不需要大括号。当您问“编译器是否以这种方式读取它”时,您必须记住大多数空间是微不足道的;语法决定了语法树的真正嵌套。

于 2013-06-09T04:57:29.720 回答