c-使用指定的初始化程序初始化数组时出现奇怪的值



当我初始化下面的数组时,除了values[3]之外,所有输出看起来都正常。由于某种原因,初始化为values[0]+values[5]values[3]正在输出一个非常大的数字。我的猜测是,我试图在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] = %in", 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

从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

我最初没有注意到这一点,并认为全表达式上的语句应用于上述语法中的顶部元素。

我现在相信,与C++一样,完整表达式适用于初始值设定项列表中的每个初始化项,这使得我之前的分析不正确。

缺陷报告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宏时,变量i都会递增三次。在情况1和情况2中,没有未定义的行为,因为增量在不确定顺序的表达式中关于彼此,不是没有顺序的。

因此CCD_ 10中的每个初始化器都是全表达式

由于该缺陷报告是针对C11的,因此值得注意的是,在这个问题的规范文本中,C11比C99更详细,它说:

初始化列表表达式的求值为相对于彼此不确定地排序,因此任何副作用发生的顺序未指明152)

values中的各个元素被分配给之前,对以下表达式进行评估的情况下,存在未定义的行为:

 values[0] + values[5]

或:

 values[5]/10

这是未定义行为,因为使用不确定值会调用未定义行为。

在这种特殊情况下,最简单的解决方法是手动执行计算:

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

还有其他替代方案,例如在初始化之后对元件39进行分配。

这与指定的初始化程序无关。这和你尝试这样的东西时遇到的错误是一样的:

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];
  ...

作为一个副作用,您的程序现在也将更加可读。

这是我第一次看到以这种方式初始化的东西,但我认为您看到的行为与访问尚未初始化的数组部分有关。因此,我使用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

我在上面的输出中确定了一个关键行,我认为它标记了您生成的内容和我生成的内容之间的差异(标记为<=====)。在用您指定的值初始化特定的数组元素之前,我的方法是将数组的内容清零。数组元素的特定初始化在此之后发生。

考虑到上述行为,我认为假设您在初始化数组的特定元素之前没有将数组内容清零是不合理的。至于为什么行为上有差异?我只能推测;但我的第一个猜测是,我们使用了两个不同的编译器版本。

希望这能有所帮助。

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]这就是您的情况
  • 两者
  • 没有一个

尝试以下代码:

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

然后像以前一样打印数组。

相关内容

  • 没有找到相关文章

最新更新