Когда локальные переменные инициализируются в C? - PullRequest
2 голосов
/ 27 марта 2019

Рассмотрим пример ниже

void func(int i){
  if(i) {
    int arr[2048] = {0};
    //Doing something related to arr;
  } else {
    //Doing something
  }
}

У меня есть объявление большого массива в блоке if. Инициализация этого массива должна занять некоторое время. Мой вопрос: если i == 0, этот массив будет инициализирован вообще?

Ответы [ 3 ]

6 голосов
/ 27 марта 2019

если i == 0, будет ли этот массив вообще инициализирован?

потому что ваш код

if(i) {
  int arr[2048] = {0};
  //Doing something related to arr;
} else {
  //Doing something
}

массив не существует, если i==0, поэтому он не может быть инициализирован, массив существует только в ветви , если , где i != 0

3 голосов
/ 27 марта 2019

Чтобы понять поведение компилятора, вы должны учитывать, что в языке C каждая переменная имеет класс хранения ( ISO / IEC 9899: 201x §6.2.4 Длительность хранения объектов )которые характеризуют его поведение и его « жизнь », что означает существование такого объекта (переменная является объектом) и законные условия доступа к нему.Классы хранения 4: статические, потоковые, автоматические и распределенные.Последние используют динамическое распределение памяти.

В вашем случае массив arr[2048] является автоматическим объектом, время жизни которого определяется (в том же абзаце стандарта @ point 6) как:

Для такого объекта, у которого нет типа массива переменной длины, его время жизни простирается от входа в блок, с которым он связан, до тех пор, пока выполнение этого блока не закончится каким-либо образом.(Ввод закрытого блока или вызов функции приостанавливает, но не прекращает выполнение текущего блока.)

Если блок вводится рекурсивно, каждый раз создается новый экземпляр объекта.

Начальное значение объекта не определено .

Если для объекта указана инициализация, она выполняется каждый раз, когда объявление или составной литерал достигается висполнение блока ;в противном случае значение становится неопределенным при каждом достижении декларации.

Это объясняет, что:

  1. Жизнь объекта начинается с начала блока, в котором он находитсяопределены.
  2. Начальное значение (в нашем случае содержимое массива) неопределенно: компилятор не инициализирует область памяти.И когда указана инициализация, она выполняется, когда выполнение достигает блока .

Первый пункт ясен и уже является ответом на ваш вопрос.Код, на который вы ссылаетесь:

{    //Block init
  int arr[2048] = {0};
  //Doing something related to arr;
}    // block end

Если вы не введете в блок life вашего объекта, массив не начинается: массив не существует.Конечно, при этом условии никакие операции не могут быть выполнены над объектом, даже инициализация.

Теперь точка 2 помогает лучше уточнить.Выражение:

int arr[2048] = {0};

Функционально не интерпретируется компилятором как объявление с инициализацией объекта из-за класса хранения объекта массива.Кроме того, оно, по существу, рассматривается как объявление плюс присвоение .

В чем разница?

Реализовано объявление инициализированной переменной, имеющей класс хранения, отличный от автоматическогос механизмом, прозрачным для кода пользователя, статически присваивающим значения в разделе BSS, или с кодом, принадлежащим прологам и эпилогам компилятора, что может произойти, даже если к объекту нет доступа .

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

Это официальное поведение.Изучив скрытно, вы можете увидеть, что в некоторых случаях пространство для автоматических переменных выделяется заранее с начала жизни объекта, но это поведение строго зависит от архитектуры ЦП, и в основном, когда это происходит, не создают функционального расхождения междукод языкового стандарта (который является базовым свойством совместимого компилятора ).

0 голосов
/ 27 марта 2019

На практике переменная будет инициализирована перед использованием , независимо от того, помещена она во внутреннюю область или нет. Этот код:

void func1 (int i){
  if(i) {
    int arr[2048] = {0};
    printf("%d", arr[666]);
  } else {
    //Doing something
  }
}

дает точно такой же машинный код, что и этот код:

void func2 (int i){
  int arr[2048] = {0};
  if(i) {
    printf("%d", arr[666]);
  } else {
    //Doing something
  }
}

gcc -O3 на x86 дает:

.LC0:
        .string "%d"
func1:
        test    edi, edi
        jne     .L4
        ret
.L4:
        xor     esi, esi
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

и

.LC0:
        .string "%d"
func2:
        test    edi, edi
        jne     .L7
        ret
.L7:
        xor     esi, esi
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

Как видите, они идентичны.

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

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