C99标准是否允许将变量分配给自己?举例来说,有以下几种有效:C99标准是否允许为自己分配一个变量?
int a = 42;
/* Case 1 */
a = a;
/* Case 2 */
int *b = &a;
a = *b;
虽然我怀疑病例1是有效的,我很犹豫,说同样的情况2
在转让的情况下,是右侧完全评估之前将值分配给左侧的变量 - 或者是取消引用指向所分配的变量的指针时引入的竞争条件?
C99标准是否允许将变量分配给自己?举例来说,有以下几种有效:C99标准是否允许为自己分配一个变量?
int a = 42;
/* Case 1 */
a = a;
/* Case 2 */
int *b = &a;
a = *b;
虽然我怀疑病例1是有效的,我很犹豫,说同样的情况2
在转让的情况下,是右侧完全评估之前将值分配给左侧的变量 - 或者是取消引用指向所分配的变量的指针时引入的竞争条件?
两种情况都是完全有效的,因为a
的值仅用于确定要存储的值,而不是确定要存储该值的对象。
在你必须区分三种不同的操作
这三个操作中的前两个操作可以以任何顺序完成,即使并行。第三个显然是另外两个的结果,所以它会出现。
这是完全有效的,你只使用以前的值来确定要存储的值。这是包括在draft C99 standard部6.5.2
它说:
之前和下一序列点之间的对象应具有由 expression.Furthermore的评价中,前一个值改性至多一次其 储值只读为 确定要存储的值。
之一的有效代码的示例如下:
i = i + 1;
C和C++部here涵盖可以发生的序列点不同的地方。
假设编译器不会通过简单地删除第一条指令来优化第一条指令,这里甚至存在竞争条件。在大多数体系结构中,如果a被存储在内存中,a = a
将被编译为两条移动指令(mem => reg,reg => mem),因此不是原子性的。
下面是一个例子:
int a = 1;
int main()
{ a = a; }
在Intel x86_64的用gcc结果4.7.1
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>
但你是*假设*,你试过吗?为什么没有任何编译器不能删除它?它很容易做到检测。这是你真实的例子吗?哪个编译器? –
虽然@GrijeshChauhan可能是正确的,现代编译器很可能不会这样做,但我对C99标准是否允许这种行为感兴趣 - 毕竟,编译器不必*实用*成为*符合标准*。 –
hmm hivert推出了它会是如果编译器不优化它,所以它的好答案。 –
C99 6.5.16。1简单赋值
3如果被存储在一个对象的值是从以任何方式 第一个对象的存储重叠的另一目的读取,则重叠应为精确和两个对象应 有资格或兼容类型的非限定版本;否则,行为是未定义的 。
我认为示例代码限定了“重叠”条件。由于它们具有兼容类型的限定版本,因此结果是有效的。
另外6.5.16赋值运算符
4的操作数的评价的顺序是不确定的。如果尝试修改赋值运算符的结果或在下一个序列点之后访问它,则 行为未定义。
仍然没有“尝试修改结果”,所以结果是有效的。
关于段落4('如果试图修改赋值操作符的结果或在下一个序列点之后访问它,行为是未定义的。因为'a'在评估之前被修改:'a = ++ a'?什么是*序列点*? –
@VilhelmGray这是一个关于序列点的很好的参考:http://en.wikipedia.org/wiki/Sequence_point它有一个关于C和C++的部分,什么是序列点。 –
@VilhelmGray是的,这是正确的,幻灯片197有这个例子:http://www.slideshare.net/olvemaudal/deep-c和这个页面有更多这样的例子:https://www.securecoding.cert.org /confluence/display/seccode/EXP30-C.+Do+not+depend+on+order+of+evaluation+between+sequence+points –
我看不到C编译器而不是允许a = a
。由于没有程序员知道它的宏,这种分配可能偶然发生。它甚至可能不会生成任何代码,这是一个优化问题。
#define FOO (a)
...
a = FOO;
示例代码很容易编译,我对C标准的审查显示不禁止。
至于比赛条件@于皓回答得好:没有比赛条件。
'a = a;'是高级语言中的死指令,我非常肯定优化阶段会将其删除 –
@GrijeshChauhan并不总是如果a在内存中并且不需要优化。看我下面的例子。 – hivert
只要变量已经正确初始化,你应该没问题。如果变量未初始化,则复制未初始化的值不会使其更好地初始化。如果变量是volatile,它可能不是空操作(编译器必须读取该值并写入再次读回的值)。当变量不符合volatile时,它应该是no-op。 –