在C语言中,如果像这样初始化数组:

int a[5] = {1,2};

然后,数组中所有未显式初始化的元素都将隐式初始化为零.

但是,如果我像这样初始化一个数组:

int a[5]={a[2]=1};

printf("%d %d %d %d %d\n", a[0], a[1],a[2], a[3], a[4]);

output:

1 0 1 0 0

我不明白,为什么a[0]打印的是1而不是0?这是未定义的行为吗?

这个问题是在一次采访中提出的.

推荐答案

戴利:我不认为int a[5]={a[2]=1};的行为有很好的定义,至少在C99中是这样.

有趣的是,对我来说唯一有意义的部分是您询问的部分:a[0]被设置为1,因为赋值操作符返回赋值的值.其他一切都不清楚.

如果代码是int a[5] = { [2] = 1 },一切都会很简单:这是一个指定的初始值设定项,将a[2]设置为1,其他设置为0.但对于{ a[2] = 1 },我们有一个包含赋值表达式的非指定初始值设定项,我们掉进了兔子洞.


以下是我迄今为止的发现:

  • a必须是局部变量.

    6.7.8 Initialization

    1. 具有静态存储持续时间的对象的初始值设定项中的所有表达式应为常量表达式或字符串文字.

    a[2] = 1不是一个常量表达式,所以a必须具有自动存储.

  • a在其自身的初始化范围内.

    6.2.1 Scopes of identifiers

    1. struct 、联合和枚举标记的作用域在

    声明符是a[5],因此变量在其自身初始化的范围内.

  • a在它自己的初始化中是活动的.

    6.2.4 Storage durations of objects

    1. 其标识符声明时没有链接且没有存储类的对象 说明符staticautomatic storage duration.

    2. 对于这样一个没有可变长度数组类型的对象,its lifetime extends from entry into the block with which it is associated until execution of that block ends英寸

  • a[2]=1之后有一个序列点.

    6.8 Statements and blocks

    1. full expression是一个不属于另一个表达式或声明符的表达式.

    请注意,例如在int foo[] = { 1, 2, 3 }中,{ 1, 2, 3 }部分是一个括号内的初始值设定项列表,每个初始值设定项后面都有一个序列点.

  • 初始化按初始值设定项列表顺序执行.

    6.7.8 Initialization

    1. 每个括号内的初始值设定项列表都有一个关联的current object.当没有

     

    1. 初始化应按初始值设定项列表顺序进行,每个初始值设定项提供一个
  • 但是,初始值设定项表达式不一定按顺序计算.

    6.7.8 Initialization

    1. 初始化列表表达式中出现任何副作用的顺序为 未指明.

然而,仍有一些问题没有得到解答:

  • 序列点是否相关?基本规则是:

    6.5 Expressions

    1. 在上一个序列点和下一个序列点之间,对象应有其存储值

    a[2] = 1是一个表达式,但初始化不是.

    这与附件J略有矛盾:

    J.2 Undefined behavior

    • 在两个序列点之间,一个对象被多次修改,或者被修改

    附件J表示,任何修改都是重要的,而不仅仅是表达式的修改.但鉴于附件是非规范性的,我们可能可以忽略这一点.

  • 关于初始值设定项表达式,子对象初始化是如何排序的?是否先判断所有初始值设定项(以某种顺序),然后使用结果(以初始值设定项列表顺序)初始化子对象?或者它们可以交错?


我认为int a[5] = { a[2] = 1 }的执行如下:

  1. 输入包含a的块时,会分配a的存储.内容目前还不确定.
  2. 执行(唯一)初始值设定项(a[2] = 1),后跟一个序列点.它在a[2]中存储1,然后返回1.
  3. 1用于初始化a[0](第一个初始化器初始化第一个子对象).

但是这里的情况变得模糊了,因为剩下的元素(a[1]a[2]a[3]a[4])应该初始化为0,但不清楚什么时候:在计算a[2] = 1之前会发生吗?如果是这样,a[2] = 1将"赢"并覆盖a[2],但该赋值是否会有未定义的行为,因为零初始化和赋值表达式之间没有序列点?序列点是否相关(见上文)?还是在所有初始值设定项都被计算之后,零初始化就发生了?如果是这样,a[2]应该是0.

因为C标准没有明确定义这里发生了什么,我认为行为是未定义的(通过省略).

C++相关问答推荐

为什么下面的递归基本情况在C中不起作用?

不同到达时间的轮询实现

如何设置指针指向在函数中初始化的复合文字中的整数?

在编译时参数化类型定义

Clang:如何强制运行时错误的崩溃/异常由于-fsanitize=undefined

为什么memcpy进入缓冲区和指向缓冲区的指针工作相同?

如何将另一个数组添加到集合中,特别是字符串?

为什么Fread()函数会读取内容,然后光标会跳到随机位置?

为什么GCC-O1优化破解了这个代码,为了一个GameBoy高级只读存储器而修改了VRAM的循环?

S,在 struct 中创建匿名静态缓冲区的最佳方式是什么?

具有正确标头的C struct 定义问题

为什么我的半数组测试和奇数组测试不起作用?(我使用Assert进行调试)

在git补丁中自动添加C的宏

在C中使用字符串时是否不需要内存分配?

在同一范围内对具有相同类型的变量执行的相同操作在同一C代码中花费的时间不同

使用 strtok 多次分割一个字符串会导致意外行为

const struct 成员的 typedef 中的灵活数组大小

定义 int a = 0, b = a++, c = a++;在 C 中定义了行为吗?

Zig 中 C 的system函数的惯用替代方案

为什么转换为 uint64_t 会改变此代码中的结果?