关于C语言中的数组初始化的困惑 [英] Confusion about array initialization in C

查看:115
本文介绍了关于C语言中的数组初始化的困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In C language, if initialize an array like this:

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

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

then all the elements of the array that are not initialized explicitly will be initialized implicitly with zeros.

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

But, if I initialize an array like this:

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

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

输出:

1 0 1 0 0

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

I don't understand, why does a[0] print 1 instead of 0? Is it undefined behaviour?

注意:该问题是在采访中提出的.

Note: This question was asked in an interview.

推荐答案

TL; DR:我认为,至少在C99中,int a[5]={a[2]=1};的行为没有得到很好的定义.

TL;DR: I don't think the behavior of int a[5]={a[2]=1}; is well defined, at least in C99.

有趣的是,对我来说唯一有意义的部分是您要询问的部分:a[0]设置为1,因为赋值运算符返回已赋值.其他所有不清楚的地方.

The funny part is that the only bit that makes sense to me is the part you're asking about: a[0] is set to 1 because the assignment operator returns the value that was assigned. It's everything else that's unclear.

如果代码是int a[5] = { [2] = 1 },一切都会变得很容易:这是将a[2]设置为1并将其他所有内容设置为0的指定的初始值设定项.但是,使用{ a[2] = 1 }时,我们有了一个未指定的初始化程序,该初始化程序包含一个赋值表达式,并且掉进了一个兔子洞.

If the code had been int a[5] = { [2] = 1 }, everything would've been easy: That's a designated initializer setting a[2] to 1 and everything else to 0. But with { a[2] = 1 } we have a non-designated initializer containing an assignment expression, and we fall down a rabbit hole.

这是我到目前为止发现的内容:

Here's what I've found so far:

  • a必须是局部变量.

6.7.8初始化

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

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

a[2] = 1 is not a constant expression, so a must have automatic storage.

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

a is in scope in its own initialization.

6.2.1标识符范围

  1. Structure,union和枚举标签的作用域始于 声明标签的类型说明符中的标签.每个枚举常量的范围如下: 在其定义的枚举器出现在枚举器列表中之后立即开始. 任何 其他标识符的范围仅在其声明符完成之后开始.
  1. Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.

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

The declarator is a[5], so variables are in scope in their own initialization.

a在其自身的初始化中仍然有效.

a is alive in its own initialization.

6.2.4对象的存储期限

  1. 一个其标识符声明为无链接且没有存储类的对象 说明符static具有自动存储期限.

  1. An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration.

对于这样一个没有可变长度数组类型的对象,其生存期会延长 从进入与之关联的块直到该块的执行结束 反正. (输入封闭的块或调用函数会暂停,但不会结束, 执行当前块.)如果以递归方式输入该块,则该块的新实例 每次创建一个对象.对象的初始值不确定.如果 为对象指定了初始化,每次初始化时都会执行 在执行该块时达到;否则,该值将变得不确定 到达声明的时间.

For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate. If an initialization is specified for the object, it is performed each time the declaration is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.

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

  • There is a sequence point after a[2]=1.

    6.8语句和块

    1. 完整表达式是不属于另一个表达式或声明符的表达式. 以下每个都是完整的表达式:初始化程序;表达式中的表达式 陈述;选择语句的控制表达式(ifswitch);这 控制whiledo语句的表达;的每个(可选)表达式 for语句; return语句中的(可选)表达式. 完整结尾 表达式是一个序列点.
    1. A full expression is an expression that is not part of another expression or of a declarator. Each of the following is a full expression: an initializer; the expression in an expression statement; the controlling expression of a selection statement (if or switch); the controlling expression of a while or do statement; each of the (optional) expressions of a for statement; the (optional) expression in a return statement. The end of a full expression is a sequence point.

    请注意,例如在int foo[] = { 1, 2, 3 }中,{ 1, 2, 3 }部分是一个用大括号括起来的初始化列表,每个初始化之后都有一个序列点.

    Note that e.g. in int foo[] = { 1, 2, 3 } the { 1, 2, 3 } part is a brace-enclosed list of initializers, each of which has a sequence point after it.

    初始化是按照初始化列表的顺序进行的.

    Initialization is performed in initializer list order.

    6.7.8初始化

    1. 每个用大括号括起来的初始化程序列表都有一个关联的当前对象.当没有 存在指定,当前对象的子对象按以下顺序初始化 当前对象的类型:下标顺序增加的数组元素,声明顺序增加的结构成员,以及联合的第一个命名成员. [...]
    1. Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union. [...]

     

    1. 初始化应以初始化列表的顺序进行,每个初始化提供一个 覆盖同一子对象的所有先前列出的初始化程序的特定子对象;全部 未显式初始化的子对象应隐式初始化为与 具有静态存储持续时间的对象.
    1. The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

  • 但是,初始化表达式不一定按顺序求值.

  • However, initializer expressions are not necessarily evaluated in order.

    6.7.8初始化

    1. 初始化列表表达式中发生任何副作用的顺序是 未指定.
    1. The order in which any side effects occur among the initialization list expressions is unspecified.

  • 但是,仍然有一些问题无法解答:

    However, that still leaves some questions unanswered:

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

    • Are sequence points even relevant? The basic rule is:

    6.5表达式

    1. 在上一个序列点和下一个序列点之间,对象应具有其存储值 最多可以通过对表达式的求值进行修改.此外,先验价值 应该是只读的,以确定要存储的值.
    1. Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

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

    a[2] = 1 is an expression, but initialization is not.

    这与附件J有点矛盾:

    J.2未定义的行为

    • 在两个序列点之间,一个对象被多次修改或被修改 并读取先验值,而不是确定要存储的值(6.5).
    • Between two sequence points, an object is modified more than once, or is modified and the prior value is read other than to determine the value to be stored (6.5).

    附件J表示任何修饰计数,而不仅仅是表达式修饰.但是鉴于附件是非规范性的,我们可能会忽略这一点.

    Annex J says any modification counts, not just modifications by expressions. But given that annexes are non-normative, we can probably ignore that.

    关于初始化程序表达式,子对象初始化如何排序?是否首先评估所有初始化程序(以某种顺序),然​​后用结果初始化子对象(以初始化程序列表的顺序)?还是可以将它们交错?

    How are the subobject initializations sequenced with respect to initializer expressions? Are all initializers evaluated first (in some order), then the subobjects are initialized with the results (in initializer list order)? Or can they be interleaved?

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

    I think int a[5] = { a[2] = 1 } is executed as follows:

    1. a的存储空间在进入其包含块时分配.此时的内容不确定.
    2. (仅)初始化程序被执行(a[2] = 1),后跟序列点.这样会将1存储在a[2]中,并返回1.
    3. 1用于初始化a[0](第一个初始化程序初始化第一个子对象).
    1. Storage for a is allocated when its containing block is entered. The contents are indeterminate at this point.
    2. The (only) initializer is executed (a[2] = 1), followed by a sequence point. This stores 1 in a[2] and returns 1.
    3. That 1 is used to initialize a[0] (the first initializer initializes the first subobject).

    但是这里的事情变得模糊了,因为应该将其余元素(a[1]a[2]a[3]a[4])初始化为0,但是尚不清楚何时:它是否在<之前发生? c12>被评估?如果是这样,a[2] = 1将获胜"并覆盖a[2],但是该赋值是否具有未定义的行为,因为在零初始化和赋值表达式之间没有序列点?序列点是否相关(请参见上文)?还是在对所有初始化程序求值后进行零初始化?如果是这样,a[2]应该最终是0.

    But here things get fuzzy because the remaining elements (a[1], a[2], a[3], a[4]) are supposed to be initialized to 0, but it's not clear when: Does it happen before a[2] = 1 is evaluated? If so, a[2] = 1 would "win" and overwrite a[2], but would that assignment have undefined behavior because there is no sequence point between the zero initialization and the assignment expression? Are sequence points even relevant (see above)? Or does zero initialization happen after all initializers are evaluated? If so, a[2] should end up being 0.

    由于C标准并未明确定义此处发生的情况,因此我认为行为是不确定的(忽略).

    Because the C standard does not clearly define what happens here, I believe the behavior is undefined (by omission).

    这篇关于关于C语言中的数组初始化的困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆