Как x = x + 1 оценивается компилятором и как представляется в сборке? - PullRequest
1 голос
/ 02 ноября 2019

Я пытаюсь понять, как компилятор "видит" часть i + 1 из выражения i = i + 1. Я понимаю, что i = 3 означает помещение значения 3 в память местоположения переменной i.

Мое предположение о i = i + 1 состоит в том, что компилятор ожидает значение с правой стороны от "="оператор, поэтому он получает значение из памяти местоположения переменной i (которая равна 3 после присваивания) и добавляет к ней 1, а окончательный результат выражения «i + 1» (3 + 1 = 4) сохраняетсяобратно в память местоположения переменной i в качестве значения. Это правильно?

И если это так, это означает, что любая переменная / комбинация переменных и литералов, присутствующих в правой части оператора "=", всегда будет заменена значением, хранящимся в них, и этим значениеммогут быть добавлены / вычтены / и т. д. со значениями из других переменных / литералов (как в выражении x + 1), в то время как конечным результатом этих вычислений также будут литеральные значения (например: 5, литеральные строки и т. д.), и они будуттакже хранятся как значения в одной переменной в левой части оператора "=".

Мне также любопытно, как этот код видится в сборке, и каковы основные операции этого увеличения i(i = i + 1);

#include <stdio.h>
int main()
{
    int i = 3;
    i = i + 1; // i should have the value of 4 stored back in it;
    return 0;
}

1 Ответ

3 голосов
/ 02 ноября 2019

Это не отвечает в общем случае. Это зависит от целевой платформы. Если вы хотите проверить сборку, вы можете сделать это с параметром -S с помощью gcc. Когда я сделал это с вашим кодом, он дал мне следующее:

/tmp$ cat main.s 
    .file   "main.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $3, -4(%rbp)
    addl    $1, -4(%rbp)
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 9.2.1-8) 9.2.1 20190909"
    .section    .note.GNU-stack,"",@progbits

Краткое краткое объяснение того, что здесь происходит. Сначала мы нажимаем значение стека указателя. Это так, чтобы мы могли вернуться позже.

.cfi_startproc
pushq   %rbp

Затем мы устанавливаем кадр стека с этим кодом. Это соответствует объявлению переменных.

.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp

Тогда мы имеем это. Комментарии мои.

movl    $3, -4(%rbp) # i = 3;
addl    $1, -4(%rbp) # i = i + 1;

Наконец, мы возвращаемся из основной функции

movl    $0, %eax # Store 0 in the "return register"
popq    %rbp     # Restore stackpointer
.cfi_def_cfa 7, 8
ret              # return

Обратите внимание, что между строками нет связи 1-1. Даже для очень простых линий.

Обратите также внимание, что C накладывает требование на наблюдаемое поведение программы, а не на сгенерированную сборку. Так, например, компилятор может удалить все тело для главной функции, потому что переменная i не используется видимым образом. И это будет, если вы используете оптимизацию. Когда я перекомпилировал ваш код с помощью -O3, я получил это вместо:

/tmp/$ cat main.s
    .file   "main.c"
    .text
    .section    .text.startup,"ax",@progbits
    .p2align 4
    .globl  main
    .type   main, @function
main:
.LFB11:
    .cfi_startproc
    xorl    %eax, %eax
    ret
    .cfi_endproc
.LFE11:
    .size   main, .-main
    .ident  "GCC: (Debian 9.2.1-8) 9.2.1 20190909"
    .section    .note.GNU-stack,"",@progbits

Обратите внимание, сколько было удалено из main. Интересно, что movl $0, %eax изменилось на xorl %eax, %eax. Если вы думаете об этом, то совершенно очевидно, что это операция «установка нуля». Можно разумно спорить, почему кто-то может писать такие вещи. Ну, оптимизатор, конечно, не оптимизировать для удобства чтения. Есть несколько причин, почему это лучше. Вы можете прочитать о них здесь: Как лучше всего установить регистр в ноль в сборке x86: xor, mov или и?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...