在指定指针时使用指向内容

Kyl*_*yle 5 c variable-assignment compound-literals c11

我一直认为,在赋值中读取正确表达式后缺少序列点会产生如下示例,产生未定义的行为:

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.
Run Code Online (Sandbox Code Playgroud)

但是,这是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到第一个元素也有副作用.

所以,我没有看到我们在标准中有什么样的gaurantees,在将数组地址写入p之前,对数组的其他未初始化的第一个元素的修改将被排序.这个修改(将*p写入第一个元素)与将i + 1写入i的修改有何不同?

换句话说,假设一个实现将示例中的兴趣语句看作三个任务:第一,为复合文字对象分配空间; 第二步:将指向所述空间的指针分配给p; 3rd:将*p写入新分配空间中的第一个元素.在分配之前,RHS和LHS的值计算将被排序,因为计算RHS的值仅需要地址.这个假设的实现以何种方式不符合标准?

Jen*_*edt 6

您需要查看6.5.16.1中赋值运算符的定义

在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序.对操作数的评估是不确定的.

所以在这里你清楚地看到,它首先以任何顺序或甚至同时评估两侧的表达式,然后将右边的值存储到左边指定的对象中.

此外,您应该知道分配的LHS和RHS的评估方式不同.引文有点太长了,所以这里有一个总结

  • 对于LHS,评估留下"左值",即p未触及的对象 .特别是它不会查看对象的内容.

  • 对于RHS,存在"左值转换",即对于在那里找到的任何对象(例如*p)加载该对象的内容.

  • 如果RHS包含数值类型的左值,则此数组将转换为指向其第一个元素的指针.这就是你的复合文字所发生的事情.

编辑:您添加了另一个问题

这个修改(将*p写入第一个元素)与将i + 1写入i的修改有何不同?

不同之处仅在于i分配的LHS,因此必须进行更新.复合文字中的数组不在LHS中,因此不关心更新.


Eri*_*hil 1

在 中(int [2]){*p}*p为复合文字提供初始值。这不是作业,也不是副作用。初始值是对象创建时的一部分。数组在任何时候都存在且未初始化。

\n\n

在 中p = (int [2]){*p},我们知道更新的副作用p在右侧的计算之后排序,因为 C 2011 [N1570] 6.5.16 3 表示 \xe2\x80\x9c更新左侧操作数的存储值的副作用在右侧操作数的计算之后排序左右操作数的值计算。\xe2\x80\x9d

\n