2

C99标准是否允许将变量分配给自己?例如,以下是否有效:

int a = 42;

/* Case 1 */
a = a;

/* Case 2 */
int *b = &a;
a = *b;

虽然我怀疑案例 1 是有效的,但对于案例 2,我很犹豫。

在赋值的情况下,在将值分配给左侧的变量之前,右侧是否已完全评估- 或者在取消引用指向被分配变量的指针时是否引入了竞争条件?

4

5 回答 5

4

这两种情况都是完全有效的,因为 的值a仅用于确定要存储的值,而不是确定要存储该值的对象。

本质上,在作业中,您必须区分三种不同的操作

  • 确定要存储值的对象
  • 评估 RHS
  • 将确定的值存储在确定的对象中

这三个操作中的前两个可以按任何顺序进行,甚至可以并行进行。第三个显然是其他两个的结果,所以它会在之后发生。

于 2013-07-17T15:32:21.803 回答
3

这是完全有效的,您仅使用先前的值来确定要存储的值。这在草案 C99 标准部分中有介绍6.5.2,其中说:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。此外,应只读先前的值以确定要存储的值。

有效代码示例之一如下:

i = i + 1;

此处的 C 和 C++ 部分涵盖了可能出现序列点的不同位置。

于 2013-07-17T15:24:18.510 回答
2

假设编译器没有通过简单地删除第一条指令来优化它,这里甚至存在竞争条件。在大多数架构上,如果 a 存储在内存中,a = a将在两个移动指令中编译(mem => reg,reg => mem),因此不是原子的。

这是一个例子:

int a = 1;
int main() 
{ a = a; }

在带有 gcc 4.7.1 的 Intel x86_64 上的结果

4004f0:       8b 05 22 0b 20 00       mov    0x200b22(%rip),%eax        # 601018 <a>
4004f6:       89 05 1c 0b 20 00       mov    %eax,0x200b1c(%rip)        # 601018 <a>
于 2013-07-17T15:29:36.467 回答
2

C99 6.5.16.1 简单赋值

3 如果存储在一个对象中的值是从另一个对象读取的,该对象以任何方式与第一个对象的存储重叠,则重叠应准确,并且两个对象应具有兼容类型的合格或不合格版本;否则,行为未定义。

我认为示例代码符合“重叠”条件。由于它们确实具有兼容类型的合格版本,因此结果是有效的。

还有 6.5.16 赋值运算符

4 操作数的求值顺序未指定。如果尝试修改赋值运算符的结果或在下一个序列点之后访问它,则行为未定义。

尽管如此,没有“尝试修改结果”,所以结果是有效的。

于 2013-07-17T15:34:05.407 回答
0

我看不到 C 编译器不允许a = a. 这种分配可能由于程序员不知道的宏而偶然发生。它甚至可能不会生成任何代码,因为这是一个优化问题。

#define FOO (a)
...
a = FOO;

示例代码很容易编译,我对 C 标准的审查表明没有禁止。

至于比赛条件@Yu Hao 回答得很好:没有比赛条件。

于 2013-07-17T15:37:43.163 回答