Распределяет ли C память для переменных, которые не определены в начале функции? - PullRequest
0 голосов
/ 05 октября 2018

Вот мой сценарий, почему я задаю этот вопрос:

static ProfileUnit* g_units_header;
static ProfileUnit* g_units_tail;
static int g_units_count;

void Destroy() {
    if (!g_units_header) {
        return;
    }

    typedef std::vector<ProfileUnit*> PUVect;
    PUVect stack(g_units_count);
    ProfileUnit* p = g_units_header;
    while (p) {
        stack.push_back(p);
        p = p->next;
    }
    for (PUVect::const_iterator it = stack.begin(); it != stack.end(); ++it) {
        free(*it);
    }

    g_units_header = g_units_tail = nullptr;
    g_units_count = 0;
}

Отразятся ли "stack" и "p" в стеке вызовов, если "g_units_header" равен nullptr?Это не очень хороший пример, я просто хочу объяснить сценарий.просто сосредоточиться на вопросе.

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Хотя у компилятора есть много возможностей, особенно с кодом, который, как доказывается, не дает никаких наблюдаемых эффектов, стандарт C действительно указывает, что время жизни автоматической переменной (локальной переменной) начинается при входе в блок вкоторый объявляется (это тело foo в случае, о котором вы спрашиваете.) [Примечание 1]

До тех пор, пока объявление не будет достигнуто во время выполнения foo - если оно достигнуто -- переменная имеет неопределенное значение, что ограничивает ее использование.Кроме того, имя переменной не отображается до ее объявления.Тем не менее, переменная существует (если она не была исключена, поскольку компилятор определил, что она не имеет значения).

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


Примечания:

  1. Массивы переменной длины являются исключением из этого правила, поскольку они не могут быть распределены, пока не известен их размер, который не можетбыть определенным, пока декларация не будет фактически оценена.
0 голосов
/ 05 октября 2018

(Для иллюстрации я буду использовать небольшое изменение кода в исходной версии вашего вопроса. С тех пор вы редактировали вопрос, чтобы изменить код, но это не меняет ответа.)

На практике ответ зависит от используемого компилятора и параметров оптимизации.Некоторые возможные результаты:

  1. val оптимизируется полностью независимо от значения p.
  2. val помещается в регистр (считается ли это «памятью»«?)
  3. val помещается в стек независимо от значения p.
  4. val помещается в стек только в том случае, если p не равно NULL.

На моей машине gcc -O3 делает # 4:

$ cat test.c
#include <stdio.h>

void foo(int* p) {
    if (!p) return;
    int val = 0xdeadbeef;
    scanf("%d", &val);
}

Скомпилировано:

$ gcc -S -O3 test.c

Вывод (отредактировано для краткости):

$ cat test.s
_foo:                                   ## @foo
    testq   %rdi, %rdi  ## <<< p == NULL?
    je  LBB0_2          ## <<< Will jump over the stack allocation below
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp   ## <<< Allocate stack for val
    movl    $-559038737, -4(%rbp)   ## 0xdeadbeef
    leaq    L_.str(%rip), %rdi
    leaq    -4(%rbp), %rsi
    xorl    %eax, %eax
    callq   _scanf
    addq    $16, %rsp   ## <<< Deallocate val
    popq    %rbp
LBB0_2:
    retq
...