类c编译器如何解释if语句

how does a C-like compiler interpret the if statement

本文关键字:解释 if 语句 编译器 何解释      更新时间:2023-10-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看起来如何,以及是否有任何特殊的AST案例用于'else-if'语句,或者它是否会被编译为级联if/else块。

它们相当于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. 语义分析:解析token列表并生成抽象语法树(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中看到的,没有结构上的变化。

在C和c++中,将语句封装到冗余的{}对中不会改变程序的语义。这句话

a = b;

等于这个

{ a = b; }

等于这个

{{ a = b; }}

和这个

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

多余的{}对编译器来说绝对没有区别。

在您的示例中,第一个版本和第二个版本之间的唯一区别是您添加到后者的一堆冗余{},就像我在上面的a = b示例中所做的那样。您的冗余{}绝对不会改变任何东西。你给出的两个版本的代码之间没有明显的区别,这使得你的问题本质上没有意义。

如果你想问别的问题,要么澄清你的问题,要么纠正代码。

这两段代码实际上是相同的。通过了解"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"重复执行此操作从句,你最终会得到第二种形式。这两段代码完全相同,并且被编译器以完全相同的方式看待。

请注意,尽管您的第一个语句是根据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语句。因为它只是一个语句,所以不需要用大括号括起来。当你问"编译器是这样读的吗"时,你必须记住,大多数空间都是无关紧要的;语法决定语法树的真正嵌套。

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

当一个程序被编译时,它要经历几个阶段。第一阶段是词法分析,第二阶段是句法分析。词法分析分析文本,把它分成不同的符号。然后语法分析查看程序的结构,并构造抽象语法树(AST)。这是在编译过程中创建的底层语法结构。

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

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

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