C语言编译器如何解释if语句?

17

在类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的外观以及是否有任何特殊的AST情况适用于“else-if”语句,或者它将编译为级联的if/else块。


13
它们之间不会相互转换,而是分别转换为各自的内部格式,这些格式恰好相同。请问您有什么实际的编程问题? - Raymond Chen
1
C语言将根据目标/编译器等不同的表示进行编译。 - maxwellb
2
@DamonSwayn,我很想知道您认为编译器如果以一种方式解释它与另一种方式有何意义;换句话说,替代解释会产生什么有意义的影响?事实上,这两种解释之间没有区别--它们是等价的。 - Ernest Friedman-Hill
8
有何不同?除了纯粹的外观差异外,你的两个变体没有任何区别。两个版本都具有完全相同的语义。当这段代码到达编译器开始解析其含义的地方时,它们将被表示为完全相同。因此,你的问题是什么并不清楚。 - AnT stands with Russia
2
问题询问是否引入额外的大括号(块)会影响编译器解析复杂if语句的方式,可能对生成的目标代码产生影响。有经验的C/C++程序员可能会发现答案很明显,但将其关闭为模糊、不明确或过于宽泛是不合理的。请投票重新开放。 - jogojapan
显示剩余5条评论
5个回答

19

它们相当于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中看到的那样,没有结构上的改变。


4
回答这个层次的问题时,使用像AST这样的缩写而不解释其含义是不诚实的。 - TonyK
1
@TonyK 从OP的角度来看,期望回答者成为一个活生生的维基百科也是不诚实的。在谷歌上输入“AST”不应该有什么问题。 - user529758
分支是C++用户的另一个重要主题。 - user2384250
AST = 抽象语法树 - Salek

12

在 C 和 C++ 中,将语句用多余的一对 {} 嵌套起来不会改变程序的语义。这个语句

a = b;

等同于这一个

{ a = b; }

等同于这个

{{ a = b; }}

并且对于这一个

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

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

在你的例子中,第一个版本和第二个版本唯一的区别就是后者中你加入了一堆冗余的{},就像我在上面的a = b例子中所做的那样。你多余的{}并没有改变任何东西。你呈现的这两个版本代码之间几乎没有什么实质性的区别,这使得你的问题基本上是没有意义的。

如果你的意思不是询问这个,要么澄清你的问题,要么更正代码。


5
这取决于语句。至少在C语言中,如果语句包含一个复合字面量,它将创建一个对象,其生命周期在最近的封闭块结束时结束。在这种情况下,用大括号括起来的单个语句确实会有所区别。但在这个特定的例子中,你是正确的,这并不重要,特别是因为if语句的子语句都是块(即使它们不是复合语句)。 - Keith Thompson
2
此外,{ }会创建一个新的作用域。 - user529758
3
@H2CO3:是的,但在问题的上下文中没有关系。这里的重点是,如果新范围不包含任何与该新范围有关或依赖于该新范围的内容,则该新范围是冗余的。冗余的范围是完全无关紧要的。 OP使用的“转换”正是创建了一个完全无关紧要的冗余范围。在这里“非冗余”的唯一可能性是Keith在他的评论中提到的-例如,在if条件中使用复合字面量。 - AnT stands with Russia

2
这两段代码实际上是相同的。你可以通过意识到“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”子句都重复执行此操作,则最终得到的正是第二种形式。这两段代码完全相同,并且编译器也会以完全相同的方式看待它们。

1
不,if语句的语法并不涉及“块”。请参考Naruil的答案,该答案正确引用了实际语法。 - Keith Thompson
1
这只是为了解释概念而进行的简化,而不涉及实际语法(并且混淆视听)。我认为这会帮助他看到整体情况(他代码的结构)。 - Ziffusion

1
请注意,尽管您的第一个语句按照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语句。由于它只是一个语句,所以不需要用大括号括起来。当你问:“编译器是否以这种方式读取它”,你必须记住大部分空格都是无关紧要的;语法决定了语法树的真正嵌套。

1
请注意,尽管缩进揭示了实际结构,但我肯定不建议使用它。 - ugoren

0

与第一个相似,但问题并不完全符合。

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

因此,如果和if-else和if-elseif-else语句最终都被编译器转换为抽象语法树(AST)。

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

编辑:

实际上,if/if else语句可能会在AST中形成更接近于第二个的内容。我不太确定,但如果以二叉树式条件分支结构的方式表示它作为底层级别,我也不会感到惊讶。如果您有兴趣深入了解它,请查找有关编译器理论的解析方面的相关研究。


越接近第一个,越接近第二个,你不确定。这不是一个答案。 - user207421
1
这绝对是一个答案。我并不确定它是如何被解析的,但其核心在于解析树的句法结构,如果发帖者想深入了解编译器如何处理它,那么解析树绝对是前进的方向。 - Nathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接