【发布时间】:2018-07-23 02:30:31
【问题描述】:
我一直认为,在赋值中读取正确表达式后缺少序列点会导致如下示例产生未定义的行为:
void f(void)
{
int *p;
/*...*/
p = (int [2]){*p};
/*...*/
}
// p is assigned the address of the first element of an array of two ints, the
// first having the value previously pointed to by p and the second, zero. The
// expressions in this compound literal need not be constant. The unnamed object
// has automatic storage duration.
但是,这是 C11 标准委员会草案中“6.5.2.5 复合文字”下的示例 2,版本标识为 n1570,我理解为最终草案(我无法访问最终版本)。
那么,我的问题是:标准中有什么东西可以提供这种定义和指定的行为吗?
编辑
我想详细说明我认为的问题,以回应一些已经出现的讨论。
我们有两个条件可以明确声明一个赋值具有 未定义的行为,根据 dbush 给出的答案中引用的标准的 6.5p2:
1) 标量对象的副作用相对于另一侧是无序的 对同一个标量对象的影响。
2) 标量对象的副作用相对于值是无序的 使用相同标量对象的值进行计算。
第 1 项的示例是“i = ++i + 1”。在这种情况下,副作用 由于 ++i 将值 i+1 写入 i 相对于将 RHS 分配给 LHS 的副作用是无序的。在每一侧的值计算和将 RHS 分配给 LHS 之间存在一个序列点,如下面 Jens Gustedt 的回答中给出的 6.5.16.1 中所述。但是,由于 ++i 对 i 的修改不受该序列点的影响,否则行为将 被定义。
在我上面给出的例子中,我们有类似的情况。有一个值计算,它涉及创建一个数组并将该数组转换为指向其第一个元素的指针。将值写入该数组的一部分还有一个副作用,*p 写入第一个元素。
所以,我看不出我们在修改的标准中有什么保证 数组的其他未初始化的第一个元素将被排序 在将数组地址写入 p 之前。这个修改(写*p到第一个元素)和写的修改有什么不同呢 i+1 到 i?
换句话说,假设一个实现将示例中感兴趣的语句视为三个任务:第一,为复合文字对象分配空间;第二:将指向所述空间的指针分配给p;第 3 步:将 *p 写入新分配空间中的第一个元素。 RHS 和 LHS 的值计算将在赋值之前进行排序,因为计算 RHS 的值只需要地址。这种假设的实现在哪些方面不符合标准?
【问题讨论】:
-
你为什么认为它会产生未定义的行为?例如,
i++等价于定义明确的i = i + 1;。 -
没有副作用,所以不需要序列点。
-
副作用是将值写入 p。我认为这与 i++ 示例不同,我在评论中描述了下面 dbush 给出的答案。
-
赋值本身的副作用只有在计算出 RHS 之后才会发生。
-
@Barmar,是的,但是在写入数组内容之前,可以计算右手边(可以得到数组的地址)。
标签: c variable-assignment c11 compound-literals