C99标准是否允许为自己分配变量?

时间:2013-07-17 15:20:52

标签: c pointers c99 language-lawyer

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

int a = 42;

/* Case 1 */
a = a;

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

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

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

5 个答案:

答案 0 :(得分:4)

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

从本质上讲,你必须区分三种不同的操作

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

这三个操作中的前两个操作可以按任何顺序完成,即使是并行操作。第三个显然是另外两个人的结果,所以它将会出现。

答案 1 :(得分:3)

这完全有效,您只使用之前的值来确定要存储的值。 draft C99 standard部分6.5.2中介绍了这一点:

  

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

有效代码的一个例子如下:

i = i + 1;

C和C ++部分here涵盖了序列点可能发生的不同位置。

答案 2 :(得分: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>

答案 3 :(得分:2)

C99 6.5.16.1简单分配

  

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

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

另外6.5.16分配操作员

  

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

但是,没有“尝试修改结果”,因此结果是有效的。

答案 4 :(得分:0)

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

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

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

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