Как C-подобный компилятор интерпретирует оператор if

В 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
        }
    }
}

EDIT: Я понял, что, хотя вопрос имеет смысл в моей голове, он, вероятно, казался довольно глупым для остального населения. Я больше имел в виду, как выглядит АСТ, и были ли какие-либо особые случаи АСТ для операторов "else-if" или если он был скомпилирован как каскадный блок if/else.

Ответы

Ответ 1

Они эквивалентны компилятору 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

Когда компилятор frond-end пытается понять файл исходного кода, он часто следует за этими шагами:

  • Лексический анализ: превратите исходный текст в текстовый текст в список "токенов"
  • Семантический анализ: проанализировать список токенов и сгенерировать абстрактное синтаксическое дерево (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, структурных изменений нет.

Ответ 2

В обоих случаях C и С++, заключая оператор в избыточную пару {}, не изменяет семантику программы. Это утверждение

a = b;

эквивалентно этому

{ a = b; }

эквивалентно этому

{{ a = b; }}

и к этому

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

Резервный {} не имеет никакого значения для компилятора.

В вашем примере единственная разница между первой версией и второй версией - это куча избыточного {}, который вы добавили к последнему, как и в моем примере a = b выше. Ваш избыточный {} ничего не меняет. Нет никакой заметной разницы между двумя версиями кода, которые вы представили, что делает ваш вопрос практически бессмысленным.

Либо уточните свой вопрос, либо исправьте код, если вы хотите спросить о чем-то еще.

Ответ 3

Два фрагмента кода, по сути, идентичны. Вы можете понять, почему это верно, осознав, что синтаксис оператора "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

И если вы делаете это повторно для всех предложений "если еще", вы получите именно свою вторую форму. Два фрагмента кода точно идентичны и точно аналогичным образом рассматриваются компилятором.

Ответ 4

Ближе к первому, но вопрос не совсем подходит.

Когда скомпилированные программы проходят через несколько этапов. Первый этап - лексический анализ, затем второй этап - синтаксический анализ. Лексический анализ анализирует текст, разделяя его на токены. Затем синтаксический анализ смотрит на структуру программы и строит абстрактное синтаксическое дерево (AST). Это базовая синтаксическая структура, созданная во время компиляции.

Итак, в основном, if и if-else и if-elseif-else - это все, в конечном итоге, структуры в абстрактное синтаксическое дерево (AST) компилятором.

Здесь страница wikipedia по AST: https://en.wikipedia.org/wiki/Abstract_syntax_tree

изменить: И на самом деле, и если /if else оператор, вероятно, образует нечто более близкое ко второму внутри АСТ. Я не совсем уверен, но я не удивлюсь, если он будет представлен на базовом уровне как двоичная древовидная условная ветвящаяся структура. Если вы заинтересованы в более глубоком изучении этого вопроса, вы можете провести некоторое исследование аспекта анализа теории компиляторов.

Ответ 5

Обратите внимание, что хотя ваш первый оператор имеет отступ в соответствии с соглашением 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. Поскольку это всего лишь одно утверждение, оно не требует скобок вокруг него. Когда вы спрашиваете: "компилятор читает это так", вы должны помнить, что большая часть пространства ничтожна; синтаксис определяет истинное вложение дерева синтаксиса.