Luk*_*ray 18 c arrays runtime-error designated-initializer unspecified-behavior
当我初始化下面的数组时,所有输出看起来都没问题,除了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;
}
Run Code Online (Sandbox Code Playgroud)
输出如下:
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
Run Code Online (Sandbox Code Playgroud)
Sha*_*our 16
看起来您在此处受到未指定的行为,因为初始化列表表达式的评估顺序未指定,来自草案C99标准部分6.7.8
:
未指定初始化列表表达式中出现任何副作用的顺序.133)
并注释133说:
特别是,评估顺序不必与子对象初始化的顺序相同.
据我所知,支持备注的规范性文本133
将来自以下部分6.5
:
除非后面指出[...],否则子表达式的评估顺序和副作用的发生顺序都是未指定的.
我们可以看到一个初始化器是一个完整的表达式6.8
(强调我的):
完整表达式是不属于另一个表达式或声明符的表达式. 以下每个都是完整表达式:初始化器 ; [...]
回顾一下我的旧C++答案,其中涵盖了初始化程序中的序列点,并将完整表达式置于不同的位置,然后我最初得出结论,我在6.7.8
包含的初始化程序中实现了两次语法:
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
Run Code Online (Sandbox Code Playgroud)
我最初没有注意到这一点,并认为全表达式的语句应用于上述语法中的top元素.
我现在相信像C++一样,full-expression适用于初始化列表中的每个初始化程序,这使得我之前的分析不正确.
缺陷报告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
}
Run Code Online (Sandbox Code Playgroud)
它说:
在每次使用INITIALIZERS宏时,变量i增加三次.在情况1和2中,没有未定义的行为,因为增量是在相对于彼此不确定地排序的表达式中,而不是未排序的.
所以每个初始化器内INITIALIZERS
是全表达.
由于此缺陷报告针对C11,因此值得注意的是C11在此问题的规范性文本中比C99更详细,它说:
初始化列表表达式的评估相对于彼此不确定地排序,因此未指定任何副作用发生的顺序.152)
在将各个元素values
分配给以下表达式之前评估以下表达式的情况下存在未定义的行为:
values[0] + values[5]
Run Code Online (Sandbox Code Playgroud)
要么:
values[5]/10
Run Code Online (Sandbox Code Playgroud)
这是未定义的行为,因为使用不确定的值会调用未定义的行为.
在这种特定情况下,最简单的解决方法是手动执行计算:
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]= 197 + 350,
[9]= 350/10
};
Run Code Online (Sandbox Code Playgroud)
还有其他替代方法,例如对元素进行分配3
以及9
初始化之后.
这与指定的初始化器无关.尝试这样的事情时,你会得到同样的错误:
int array[10] = {5, array[0]};
Run Code Online (Sandbox Code Playgroud)
执行初始化列表表达式的顺序只是未指定的行为.这意味着它是特定于编译器的,没有文档记录,永远不应该依赖:
C11 6.7.9/23
初始化列表表达式的评估相对于彼此不确定地排序,因此未指定任何副作用发生的顺序.
由于您使用数组项初始化其他数组成员,这意味着您必须将代码更改为运行时分配而不是初始化.
int values[10];
values[2] = -100;
values[5] = 350;
values[3] = values[0] + values[5];
...
Run Code Online (Sandbox Code Playgroud)
作为副作用,您的程序现在也将更具可读性.
这是我第一次看到以这种方式初始化的东西,但我认为你看到的行为与访问尚未初始化的数组有关.所以我在32位Ubuntu 12.04系统上使用GCC 4.6.3构建它.在我的环境中,我得到的结果与你不同.
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
Run Code Online (Sandbox Code Playgroud)
我已经在上面的输出中确定了一个关键线,我认为这标志着你生成的和我生成的内容之间的区别(标有<======).在使用您指定的值初始化特定数组元素之前,我正在将数组的内容归零.在此之后发生数组元素的特定初始化.
鉴于上述行为,我认为在初始化数组的特定元素之前假设你的数组内容没有归零是不合理的.至于为什么行为上的差异?我只能推测; 但我的第一个猜测是我们使用两个不同的编译器版本.
希望这可以帮助.
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]=values[0] + values[5],
[9]= values[5]/10
};
Run Code Online (Sandbox Code Playgroud)
编辑:
ISO C99 标准第 6.7.8 节(初始化)指定
初始化应按初始值设定项列表顺序进行,为特定子对象提供的每个初始值设定项将覆盖同一子对象的任何先前列出的初始值设定项;132)所有未显式初始化的子对象应隐式初始化,与具有静态存储持续时间的对象相同
但正如Shafik指出的,评估顺序不必与初始化顺序匹配
这意味着values[0] + values[5]
可能会从以下位置读取垃圾值:
values[0]
values[5]
(这就是你的情况发生的情况) 归档时间: |
|
查看次数: |
1331 次 |
最近记录: |