在C中不使用第三个变量而交换两个变量的值?
我发现了以下代码片段:在C中不使用第三个变量而交换两个变量的值?,c,expression,operator-precedence,sequence-points,C,Expression,Operator Precedence,Sequence Points,我发现了以下代码片段: #include <stdio.h> int main(void) { int x=10,y=15; x=x+y-(y=x); printf("x=%d y=%d",x,y); return 0; } 此算法不起作用,因为它调用此行中未定义的行为: x=x+y-(y=x); ^ ^ 您正在修改y,并根据C99标准草案第6.5节的规定,在同一序列点内使用其值: 在上一个序列点和下一个序列点之间,对象应具有 通过计算
#include <stdio.h>
int main(void) {
int x=10,y=15;
x=x+y-(y=x);
printf("x=%d y=%d",x,y);
return 0;
}
此算法不起作用,因为它调用此行中未定义的行为:
x=x+y-(y=x);
^ ^
您正在修改y
,并根据C99标准草案第6.5节的规定,在同一序列点内使用其值:
在上一个序列点和下一个序列点之间,对象应具有
通过计算
表达式72)此外,先前值应仅读取至
确定要存储的值。73)
还存在未指定行为的问题,因为子表达式的计算顺序未指定:
运算符和操作数的分组由语法指示。74)
除非稍后指定(对于函数调用()、&&、| |、?:、和
逗号运算符),子表达式的求值顺序和
副作用发生的顺序都未明确
在这种情况下,如果您使用的是clang
,它将提供以下警告:
warning: unsequenced modification and access to 'y' [-Wunsequenced]
x=x+y-(y=x);
~ ^
据我所知,默认情况下。通过使用-Wall
,您可以从gcc
收到类似的警告
例如,有几个SO问题涉及到如何在没有临时变量的情况下进行交换。这实际上是由于未定义的行为造成的,这是一种称为序列点的行为。基本上,在这种情况下,C标准不要求在表达式求值期间以任何顺序存储值。事实上,以下情况当然是可能的:
计算y=x
,并将值存储在y
中。在这种情况下,x
仍然是10,y
现在也是10。表达式本身的计算结果为10(赋值的左侧)
计算x=x+y-(y=x)
,这相当于x=x+y-10
x
和y
都是10,所以这是x=10+10-10
,所以x
现在是10
现在,x
和y
都是10,原始值15现在丢失
编辑:就如何交换而言,可能是由于优化:
计算y=x
,但不要将值存储在y
中y
然后仍然保持值15,而赋值表达式的计算结果为10
现在,x=x+y-10
计算为x=10+15-10
,得到正确的值
这两种方案都是有效的,但会产生不同的结果
我以为括号会先执行
那是一个无效的假设。括号影响表达式中运算符的优先级。它们没有规定执行顺序。在
a = b + c + (d * e);
很可能b+c
是在(d*e)
之前计算的。也可能不是。C语言标准未指定子表达式的求值顺序。您发布的代码调用了未定义的行为,原因是在没有插入序列点的情况下读取和写入y
。再次请注意,括号中没有引入序列点(分号有)。我相信这只是偶然的交换-这看起来像是未定义的行为(特别是与序列点相关的行为)。这绝对是未定义的行为。使用gcc-Wall编译时给出:so.c:5:13:警告:“y”上的操作可能未定义[-Wsequence point]
@Tryin,你在哪里找到这个片段的?值得一提的是,有两种方法可以实现这一点,一种是专用的平台特定指令,另一种是“异或交换技巧”。前者不可移植/标准,后者也不安全。如果手动引入序列点,即x^=y;y^=x;x^=y;
,那么xor交换技巧是安全的,但您可能还是最好只使用第三个变量。(当然,这假设变量是整数类型).@FiddlingBits,对包括我在内的许多人来说都是一样的。不过我倾向于分别使用方括号和圆括号。@AndrewMedico,我现在觉得作为一个加拿大人不合适,但这发生在美国和英国的每一场辩论中,所以meh。这不一定是优化;它只是先执行x+y
位。也就是说,x=10+15-(y=10)
。这是未定义的行为,是的,但这并不是因为任何优化,它碰巧在某些编译器上工作。但这样的优化是可以发生的;这就是UB的全部意义。是的,它可以;只是想指出另一个您遗漏的替代方案。可能还有更多的替代方案,我只想指出两个不同的结果
a = b + c + (d * e);