在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);