18

Когда я инициализирую массив ниже всего вывода, выглядит нормально, за исключением values[3]. По какой-то причине values[3], инициализированный как values[0]+values[5], выводит очень большое число. Я предполагаю, что я пытаюсь назначить values[0]+values[5], прежде чем они будут правильно сохранены в памяти, но если кто-нибудь сможет объяснить, что это было бы здорово.Странные значения при инициализации массива с использованием назначенных инициализаторов

int main (void) 
{ 

    int values[10] = { 
     [0]=197,[2]=-100,[5]=350, 
     [3]=values[0] + values[5], 
     [9]= values[5]/10 
    }; 

    int index; 

    for (index=0; index<10; index++) 
     printf("values[%i] = %i\n", index, values[index]); 


    return 0; 
} 

Выход выглядит следующим образом:

values[0] = 197 
values[1] = 0 
values[2] = -100 
values[3] = -1217411959 
values[4] = 0 
values[5] = 350 
values[6] = 0 
values[7] = 0 
values[8] = 0 
values[9] = 35 
+11

W-что это за синтаксис синтаксиса сумасшедшего? Как же это законно и компилируется? – tux3

+8

@ tux3: https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Designated-Inits.html – Mat

+1

Это специфичный компилятор gcc: http://stackoverflow.com/questions/201101/how -to-initialize-a-array-in-c второй тент. – hetepeperfan

ответ

4
int values[10] = { 
    [0]=197,[2]=-100,[5]=350, 
    [3]=values[0] + values[5], 
    [9]= values[5]/10 
}; 

редактировать:

ISO C99 standard, section 6.7.8 (Initialization) указывает, что

Инициализация должна происходить в списке инициализатора порядке, каждый инициализатора, предусмотренный конкретный подобъект перезаписывает любой ранее перечисленный инициализатор для одной и того же подобъекта; 132) всех подобъектов, которые не инициализированы явным образом должен быть инициализированы неявно тот же как объекты, которые имеют статическую продолжительность хранения

Но Shafik указал, порядок оценки оленья кожа должна совпадать с порядком инициализации

Это означает values[0] + values[5] может считывать значения от мусора:

  • values[0]
  • values[5] (это то, что происходит в вашем случае)
  • как
  • ни один из них
+0

Является ли поведение неуказанным или неопределенным? – Bathsheba

+0

@ Батшеба Unspecified. Но на все это не следует полагаться, поскольку компилятор может вести себя по-разному в зависимости от случая, когда дело доходит до неопределенного поведения. – Lundin

16

Похоже, вы подлежат неустановленный поведение здесь, так как порядок вычисления выражений списка инициализации не задан, из проекта C99 стандартной секции 6.7.8:

Порядок, в котором какие-либо побочные эффекты встречаются среди списка выражений инициализации не определен , 133)

и отметить 133 говорит:

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

Насколько я могу судить, нормативное текст, который поддерживает записку 133 бы из раздела 6.5:

За исключением указанных позже [...] порядок оценки подвыражений и порядок, в котором происходят побочные эффекты, равны как неуказанным.

и мы можем видеть, что intializer является полным выражением из 6.8 (курсив):

Полное выражение является выражением, которое не является частью другого выражения или декларатора. Каждое из следующих вариантов является полным выражением: инициализатор; [...]

Посмотрев назад на одном из моих old C++ answers, который покрывал точки последовательности в инициализаторе и который помещает полного выражение в другом месте, то я первоначально пришел к выводу, я понял, что грамматика в 6.7.8 содержала инициализатору дважды:

initializer: 
    assignment-expression 
    { initializer-list } 
    { initializer-list , } 
initializer-list: 
    designationopt initializer 
    initializer-list , designationopt initializer 

первоначально я не заметил этого и мысли заявление на полные выражения применяется к верхнему элементу в указанной выше грамматике.

Сейчас я считаю, как C++ полного выражение относится к каждому инициализатору в инициализаторе-листе, которые делают мой предыдущий анализ неверен.

Defect report 439 подтвердил мое подозрение, что это было действительно так, то он содержит следующий пример:

#include <stdio.h> 

#define ONE_INIT  '0' + i++ % 3 
#define INITIALIZERS  [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT 

int main() 
{ 
    int i = 0; 
    char x[4] = { INITIALIZERS }; // case 1 
    puts(x); 
    puts((char [4]){ INITIALIZERS }); // case 2 
    puts((char [4]){ INITIALIZERS } + i % 2); // case 3 
} 

и говорит:

В каждом использовании макроса Инициализаторы, переменная я есть увеличился три раза. В случаях 1 и 2 нет неопределенного поведения, потому что приращения находятся в выражениях, которые неопределенно секвенированы по отношению друг к другу, а не нелогичны.

поэтому каждый intializer в INITIALIZERS является полного выражения.

Поскольку этот отчет дефекта против C11, стоит отметить, что C11 более многословный, то C99 в нормативном тексте по этому вопросу, и он говорит:

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

Существует неопределенное поведение в случае, когда следующие выражения вычисляются до соответствующих элементов в values назначены:

values[0] + values[5] 

или:

values[5]/10 

Эта это неопределенное поведение, поскольку используется indeterminate value invokes undefined behavior.

В данном конкретном случае простейших обходный будет выполнять расчеты вручную:

int values[10] = { 
    [0]=197,[2]=-100,[5]=350, 
    [3]= 197 + 350, 
    [9]= 350/10 
}; 

Есть и другие варианты, такие как делать задание к элементу 3 и 9 после инициализации.

+0

Даже если порядок лево-направо, выражение эквивалентно: 'Int значений [10] = { \t 197, \t 0, \t -100, \t значение [0] + значение [5], \t 0, \t 350, \t 0, \t 0, \t 0, \t значения [5]/10 \t}; ', а здесь зависимые значения вычисляются до зависимостей. –

+0

@EugeneSh. хорошо '6.7.8' [говорит] (http://stackoverflow.com/a/25386873/1708801) следующее« обозначение заставляет следующий инициализатор начать инициализацию подобъекта, описанного указателем » –

+2

@KubaOber. программисту выбирать свои пути. Вы можете играть в хардкор и использовать некоторые «острые» функции, а не рисковать своим здравомыслием. Но вы можете делать то же самое очень хорошо, даже не приближаясь к нему. –

7

Это не имеет никакого отношения к назначенным инициализаторам как таковым. Это та же ошибка, как вы получите при попытке что-то вроде этого:

int array[10] = {5, array[0]}; 

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

C11 6.7.9/23

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

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

int values[10]; 

    values[2] = -100; 
    values[5] = 350; 
    values[3] = values[0] + values[5]; 
    ... 

В качестве побочного эффекта ваша программа также будет более читаема.

+0

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

+0

@KubaOber Действительно. Такая оптимизация весьма вероятна. – Lundin

+0

Другим возможным обходным решением будет «enum {V0 = 107, V5 = 350}; int [10] = {[0] = V0, [2] = -100, [5] = V5, [3] = V0 + V5, [9] = V5/10}, ' –

5

Это первый случай, когда я увидел что-то инициализированное таким образом, но я решил, что поведение, которое вы видите, связано с доступом к части массива, которая еще не была инициализирована. Поэтому я построил его с помощью GCC 4.6.3 в 32-разрядной системе Ubuntu 12.04. В моей среде я получил разные результаты, чем вы.

gcc file.c -o file 

./file 
values[0] = 197 
values[1] = 0 
values[2] = -100 
values[3] = 197 
values[4] = 0 
values[5] = 350 
values[6] = 0 
values[7] = 0 
values[8] = 0 
values[9] = 35 


objdump -d file > file.asm 

cat file.asm  (relevant portion posted below) 

080483e4 <main>: 
80483e4: 55      push %ebp 
80483e5: 89 e5     mov %esp,%ebp 
80483e7: 57      push %edi 
80483e8: 53      push %ebx 
80483e9: 83 e4 f0    and $0xfffffff0,%esp 
80483ec: 83 ec 40    sub $0x40,%esp 
80483ef: 8d 5c 24 14    lea 0x14(%esp),%ebx 
80483f3: b8 00 00 00 00   mov $0x0,%eax 
80483f8: ba 0a 00 00 00   mov $0xa,%edx 
80483fd: 89 df     mov %ebx,%edi 
80483ff: 89 d1     mov %edx,%ecx 
8048401: f3 ab     rep stos %eax,%es:(%edi) <===== 
8048403: c7 44 24 14 c5 00 00 movl $0xc5,0x14(%esp) 
804840a: 00 
804840b: c7 44 24 1c 9c ff ff movl $0xffffff9c,0x1c(%esp) 
8048412: ff 
8048413: 8b 54 24 14    mov 0x14(%esp),%edx 
8048417: 8b 44 24 28    mov 0x28(%esp),%eax 
804841b: 01 d0     add %edx,%eax 
804841d: 89 44 24 20    mov %eax,0x20(%esp) 
8048421: c7 44 24 28 5e 01 00 movl $0x15e,0x28(%esp) 
8048428: 00 
8048429: 8b 4c 24 28    mov 0x28(%esp),%ecx 
804842d: ba 67 66 66 66   mov $0x66666667,%edx 
8048432: 89 c8     mov %ecx,%eax 
8048434: f7 ea     imul %edx 
8048436: c1 fa 02    sar $0x2,%edx 
8048439: 89 c8     mov %ecx,%eax 
804843b: c1 f8 1f    sar $0x1f,%eax 

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

Учитывая приведенное выше поведение, я не думаю, что необоснованно предполагать, что ваше значение не равно нулю для содержимого массива до инициализации определенных элементов массива. Какая разница в поведении? Я могу только догадываться; но я предполагаю, что мы используем две разные версии компилятора.

Надеюсь, что это поможет.

+2

Это неуказанное поведение, поэтому результаты могут оказаться разными. Здесь нет ничего загадочного. Просто не пишите код, который опирается на него. – Lundin

1

Попробуйте этот код:

int values[10]; 
values[0]=197; 
values[2]=-100; 
values[5]=350; 
values[3]=values[0]+values[5]; 
values[9]=values[5]/10; 

А потом напечатать массив, как вы сделали.

+2

Когда я начал писать свой ответ, не было вашего ответа. Затем я перезагрузите страницу и увижу это. – keras

+0

Ну, может быть, вы захотите рассмотреть вопрос о том, почему, во всяком случае ... – Lundin