Подтвердить что ты не робот

Определение переменной внутри оператора switch

В следующем коде, почему переменной i не присвоено значение 1?

#include <stdio.h>      

int main(void)
{   
    int val = 0;
    switch (val) {         
        int i = 1;   //i is defined here

        case 0:
            printf("value: %d\n", i);
            break;
        default:
            printf("value: %d\n", i);
            break;
    }
    return 0;
}

При компиляции я получаю предупреждение о том, что i не инициализируется, несмотря на int i = 1;, который явно инициализирует его

$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
    printf("value %d\n", i);
    ^

Если val = 0, то выход будет 0.

Если val = 1 или что-то еще, то вывод также равен 0.

Пожалуйста, объясните мне, почему переменная i объявлена, но не определена внутри коммутатора. Объект с идентификатором i существует с автоматической продолжительностью хранения (внутри блока), но никогда не инициализируется. Почему?

4b9b3361

Ответ 1

В соответствии со стандартом C (6.8 Выражения и блоки), подчеркивайте мое:

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

И (6.8.4.2 Оператор switch)

4 Оператор switch вызывает управление для перехода на, в или мимо оператор, который является корпусом коммутатора, в зависимости от значения контролирующее выражение, а также наличие метки по умолчанию и значения любых меток на корпусе или в корпусе переключателя. Случай или дефолт ярлык доступен только в ближайшем закрывающем переключателе утверждение.

Таким образом, инициализатор переменной i никогда не оценивается, потому что объявление

  switch (val) {         
      int i = 1;   //i is defined here
      //...

не достигается в порядке выполнения из-за переходов к меткам case и как любая переменная с длительностью автоматического хранения имеет неопределенное значение.

См. также этот нормативный пример из 6.8.4.2/7:

ПРИМЕР В фрагменте искусственной программы

switch (expr) 
{ 
    int i = 4;
    f(i); 

case 0: 
    i = 17; /* falls through into default code */ 
default:
    printf("%d\n", i); 
}

объект с идентификатором i существует с время автоматического хранения (внутри блока), но никогда инициализируется, и, следовательно, если управляющее выражение имеет ненулевое значение значение, вызов функции printf будет иметь доступ к неопределенному стоимость. Аналогично, вызов функции f не может быть достигнут.

Ответ 2

В случае, когда val не равен нулю, выполнение переходит непосредственно к метке по умолчанию. Это означает, что переменная i, определенная в блоке, не инициализируется и ее значение неопределенно.

6.8.2.4 Оператор switch

  1. Оператор switch приводит к тому, что элемент управления переходит к, в или мимо оператора, который является в зависимости от значения управляющего выражения и от наличия метку по умолчанию и значения любых меток на корпусе или в корпусе переключателя. Случай или метка по умолчанию доступна только в ближайшем заключении оператора.

Ответ 3

В самом деле, ваш i объявлен внутри блока switch, поэтому он существует только внутри switch. Однако его инициализация никогда не достигается, поэтому она остается неинициализированной, когда val не равно 0.

Это немного похоже на следующий код:

{
   int i;
   if (val==0) goto zerovalued;
   else goto nonzerovalued;
   i=1; // statement never reached
   zerovalued:
     i = 10;  
     printf("value:%d\n",i);
     goto next;
  nonzerovalued:
     printf("value:%d\n",i);
     goto next;
  next:
     return 0;
 }

Интуитивно подумайте о необработанном объявлении, например о запросе компилятора для некоторого местоположения (в кадре вызова в стеке вызовов или в регистре или что-то еще), и подумайте об инициализации в качестве оператора присваивания. Оба являются отдельными шагами, и вы можете посмотреть объявление инициализации в C, например int i=1;, в качестве синтаксического сахара для декларации raw int i;, за которым следует инициализирующее присвоение i=1;.

(на самом деле, вещи немного сложнее, например, с int i= i!=i; и еще более сложными в С++)

Ответ 4

Строка для инициализации переменной я int я = 1; никогда не вызывается, потому что она не принадлежит ни одному из доступных случаев.

Ответ 5

Инициализация переменных с автоматическими длинами хранения подробно описана в C11 6.2.4p6:

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

т.е. время жизни i в

switch(a) {
    int i = 2;
    case 1: printf("%d",i);
            break;
    default: printf("Hello\n");
}

от { до }. Его значение неопределенно, если только объявление int i = 2; не достигнуто при выполнении блока. Поскольку объявление перед меткой case, объявление не может быть достигнуто, так как switch переходит к соответствующему ярлыку случая - и по инициализации.

Поэтому i остается неинициализированным. И так как это происходит, и поскольку он не имеет своего адреса, который никогда не принимался, использование неинициализированного значения для undefined поведения C11 6.3.2.1p2:

  1. [...] Если lvalue обозначает объект с продолжительностью автоматического хранения, который мог быть объявлен с классом хранения регистров (никогда не был принят его адрес), и этот объект не инициализирован (не объявлен с инициализатором и не назначается к ней было выполнено до использования), поведение undefined.

(Обратите внимание, что сам стандарт здесь неправильно указывает содержимое в пояснительной скобке - он объявляется с инициализатором, но инициализатор не выполняется).