C语言 多对象声明中的逗号是否会像逗号操作符那样引入一个序列点?



我正在读C编程语言,发现了这句话:

分隔…的逗号声明中的变量…不是逗号操作符,也不保证从左到右求值。

如果是,在这段代码中它们是逗号操作符吗?

int a=1, b=a+1, c=b+a+1, d=c+b+a+1;

我很确定它会起作用。但是如果它们不是逗号操作符,并且不能保证从左到右的顺序,那么上面的语句可能会失败,对吧?

声明中的逗号不是表达式中的逗号操作符(声明不是表达式,尽管声明中的初始化是表达式)。当问题中的引号指出分隔声明的逗号不是逗号操作符时,它是准确的。

但是,每个声明符在后面的逗号或分号处都是完整的,因此问题中的变量定义是完全定义的行为。当它暗示不能保证从左到右的求值时,引用是不正确的——尽管它是一个微妙的语言剖析。如果逗号是逗号操作符,那么这个事实将保证从左到右的求值;因为它们不是逗号操作符,所以从左到右的保证不是由逗号操作符的定义产生的。但是,因为每个声明符后面都有序列点,所以从左到右的赋值是分开保证的。

在标准中找到正确的措辞来证明这一说法比我想象的要难。它实际上在声明器部分。

§6.7 声明

语法

声明:
           declaration-specifiers init-declarator-list <子>选择,
          ...

init-declarator-list:
           init-declarator
           init-declarator-list, init-declarator

init-declarator:
          说明符
          说明符 = 初始化

¶6声明说明符由一系列说明符组成,这些说明符表示声明符所表示的实体的链接、存储持续时间和部分类型。init-declarator-list是一个用逗号分隔的声明符序列,每个声明符都可以有附加的类型信息,或者一个初始化器,或者两者都有。声明符包含被声明的标识符(如果有的话)。

如果对象的标识符声明时没有链接,则该对象的类型应在其声明符的末尾完成,如果它有初始化式,则在其初始化声明符的末尾完成;在函数参数(包括原型)的情况下,它是调整的要求完整的类型(见6.7.6.3)。

§6.7.6 声明符

完整声明符是不属于另一个声明符的声明符。完整声明符的结尾是一个序列点。

AFAICS,§6.7.9 初始化器没有添加任何相关的内容。

序列点至关重要;这意味着左边的所有东西都经过了充分评估,副作用已经消除,然后才继续;它意味着从左到右的顺序,因此问题中的初始化是完全定义的。

序列点在完整声明符之后,而不是初始化式之后,这有点奇怪;我不认为这是重要的。

int a=1, b=a+1, c=b+a+1, d=c+b+a+1;中的初始化式是按照它们出现的顺序排序的,因为C 2018(和C 2011)§6.8¶3说:

具有自动存储持续时间的对象的初始化项,以及具有块作用域的普通标识符的变长数组声明项,在每次按照执行顺序到达声明项时都计算值,并将值存储在对象中(包括在没有初始化项的对象中存储不确定的值),就像它是一个语句一样,,并按照声明项出现的顺序在每个声明中。(重点补充道。)

同样,§6.8¶4告诉我们,每个初始化式都是一个完整表达式,在一个完整表达式的求值和下一个表达式的求值之后有一个序列点:

完整表达式不是另一个表达式的一部分,也不是声明符或抽象声明符的一部分。还有一个隐式完整表达式,其中计算变量修改类型的非恒定大小表达式;在该完整表达式中,不同大小表达式的求值相对于其他表达式是无序的。在一个完整表达式的求值和下一个要求值的完整表达式的求值之间有一个序列点。

给定上述两个条件,初始化式按其出现的顺序排列。a首先初始化,因此用于b时有值,ab用于c时有值,依此类推

§6.8¶3有点缺乏,原因有两个:

  • 初始化器不是语法标记声明器的一部分(它们是初始化声明器的一部分,这是一个包含声明器的标记)。然而,这似乎是一个措辞问题,我们可以将初始化式与其声明器相关联。
  • 它不指定声明器(如可变长度数组的大小)及其初始化器中的表达式之间的顺序。

还要注意,并非所有初始化式都按照它们在声明中出现的顺序求值。§6.7.9¶23讨论了聚合和联合的初始化式,并说:

初始化列表表达式的求值彼此之间的顺序是不确定的,因此任何副作用发生的顺序是未指定的。

不,它可能不是,因为在声明中逗号充当序列点,保证将按照所提到的顺序执行求值。你可以在这里找到更多信息:序列点

相关内容

  • 没有找到相关文章