Когда выделяется память под автоматические переменные?

В книгах пишут, что когда выполнение программы заходит в функцию, то выделяется память под все автоматические переменные функции при вхождении и она освобождается при завершении этой функции. Например:

void function(int value)
{
     int a = 10;

     // ...
     
     double b = 6.3;
     
     for (size_t i = 0; i < 10; ++i)
     {
          // ...
     }

}

Память под все переменные (value, a, b, i) выделяется при вхождении в функцию или при объявлении?


Ответы (1 шт):

Автор решения: eanmos
  1. Память может быть выделена во время вызова функции, когда указатель на вершину стека сдвигают на определенное количество байт, чтобы зарезервировать место под локальные переменные. Например, код

    void function(int value)
    {
          int a = 10;
          double b = 6.3;
    }
    

    будет скомпилирован (-O0 -mno-red-zone -m32) в

    function(int):
        push    ebp
        mov     ebp, esp
    
        // Резервируем 16 байт под локальные переменные. Помните,
        // что на x86 стек растет по направлению к младшим адресам,
        // поэтому мы ВЫЧИТАЕМ из регистра esp.
        sub     esp, 16
    
        mov     DWORD PTR [ebp-4], 10
        fld     QWORD PTR .LC0
        fstp    QWORD PTR [ebp-16]
        nop
        leave
        ret
    

    На практике в системах с System V ABI такое выделение памяти под локальные переменные вообще не происходит. По соглашению вызываемая функция может свободно использовать 128 байт после указателя на вершину стека для хранения своих локальных переменных. Эта свободная область в 128 байт называется Red zone.

    Именно поэтому, чтобы продемонстрировать выделение памяти в примере выше, пришлось передать компилятору флаг mno-red-zone.

  2. Локальная переменная может быть помещена в регистр, тогда память под нее вообще не будет выделена.

    Мы можем использовать ключевое слово register при объявлении переменной, чтобы посоветовать компилятору поместить переменную в регистр. Хотя такие советы компилятор по большей части игнорирует.

  3. Копилятор может встроить значение локальной переменной в саму инструкцию процессора, тогда память под переменную также не будет выделена.

  4. В процессе оптимизации компилятор может вообще избавится от некоторых локальных переменных, следовательно, и память для них выделена не будет.

    Например, следующий код

    int foo(void) {
        int a = 4;
        int b = 9;
        return a + b;
    }
    

    будет скомпилирован (-O2) в

     foo:
         mov     eax, 13
         ret
    
→ Ссылка