Странные значения при инициализации массива с использованием назначенных инициализаторов
Когда я инициализирую массив ниже всего вывода, выглядит нормально, за исключением 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
Ответы
Ответ 1
Похоже, вы подвержены неуказанному поведению здесь, так как порядок оценки выражений списка инициализации не указан, из стандартного раздела проекта C99 6.7.8
:
Порядок, в котором возникают любые побочные эффекты между инициализацией список выражений неуказан. 133)
и примечание 133 говорит:
В частности, порядок оценки не должен совпадать с порядком инициализации субобъекта.
Насколько я могу судить, нормативный текст, подтверждающий примечание 133
, будет из раздела 6.5
:
За исключением случаев, указанных ниже [...], порядок оценки подвыражения и порядок, в котором происходят побочные эффекты, оба не определено.
и мы видим, что intializer является полным выражением от 6.8
(выделение мое):
Полное выражение - это выражение, которое не является частью другого выражения или декларатора. Каждое из следующего является полным выражением: инициализатор; [...]
Оглядываясь назад на один из моих старых ответов на С++, которые охватывали точки последовательности в инициализаторе и который помещает полное выражение в другое место, то я изначально сделал вывод, Я понял, что грамматика в 6.7.8
содержит инициализатор дважды:
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
Я изначально не заметил этого и подумал о заявлении о полных выражениях, примененных к верхнему элементу в приведенной выше грамматике.
Теперь я считаю, что С++ полное выражение применяется к каждому инициализатору в списке инициализаторов, что делает мой предыдущий анализ неправильным.
Отчет о дефектах 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
}
и он говорит:
При каждом использовании макроса INITIALIZERS переменная я увеличивается три раза. В случаях 1 и 2 поведение undefined отсутствует, потому что приращения находятся в выражениях, которые неопределенно секвенированы по отношению друг к другу, не без последствий.
поэтому каждый intializer в INITIALIZERS
является полным выражением.
Поскольку этот отчет о дефектах против C11, стоит отметить, что C11 более подробный, чем C99 в нормативном тексте по этой проблеме, и он говорит:
Оценки выражений списка инициализации неопределенно упорядочены по отношению друг к другу и, следовательно, порядок, в котором происходят какие-либо побочные эффекты, неуказан. 152)
Существует поведение undefined в том случае, когда следующие выражения вычисляются до того, как соответствующим элементам в values
присвоены:
values[0] + values[5]
или
values[5]/10
Это поведение undefined, поскольку использование неопределенного значения вызывает поведение undefined.
В этом конкретном случае самым простым способом было бы выполнить вычисления вручную:
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]= 197 + 350,
[9]= 350/10
};
Существуют другие альтернативы, такие как выполнение присвоений элементу 3
и 9
после инициализации.
Ответ 2
Это не имеет никакого отношения к назначенным инициализаторам как таковым. Это та же ошибка, что и при попытке сделать что-то вроде этого:
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];
...
В качестве побочного эффекта ваша программа также будет более читаема.
Ответ 3
Это первый случай, когда я видел что-то инициализированное таким образом, но я решил, что поведение, которое вы видите, связано с доступом к части массива, которая еще не была инициализирована. Поэтому я построил его с помощью 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 обнуляет содержимое массива. После этого происходит конкретная инициализация элементов массива.
Учитывая вышеизложенное поведение, я не думаю, что было бы необоснованно выдвигать гипотезу о том, что ваш пользователь не обнулял содержимое массива до инициализации определенных элементов массива. Какая разница в поведении? Я могу только догадываться; но я предполагаю, что мы используем две разные версии компилятора.
Надеюсь, что это поможет.
Ответ 4
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]=values[0] + values[5],
[9]= values[5]/10
};
изменить
стандарт ISO C99, раздел 6.7.8 (Инициализация) указывает, что
Инициализация должна выполняться в порядке списка инициаторов, каждый инициализатор, предусмотренный для конкретного подобъекта, ранее указанный инициализатор для того же подобъекта; 132) все субобъекты, которые не инициализируются явно, должны быть инициализированы неявно то же, что и объекты, имеющие статическую продолжительность хранения
Но, как отметил Shafik, порядок оценки не должен соответствовать порядку инициализации
Это означает, что values[0] + values[5]
может считывать значения мусора из:
-
values[0]
-
values[5]
(это то, что происходит в вашем случае)
- оба
- ни один из них
Ответ 5
Попробуйте этот код:
int values[10];
values[0]=197;
values[2]=-100;
values[5]=350;
values[3]=values[0]+values[5];
values[9]=values[5]/10;
И затем вы печатаете массив, как вы это делали.