为什么i^=j^=i^=j不是't等于*i^=*j^=*i^=*j

why i^=j^=i^=j isn't equal to *i^=*j^=*i^=*j

本文关键字:等于 不是 为什么      更新时间:2023-09-26

在C中,当变量(假设两者都为inti小于j时,我们可以使用方程

i^=j^=i^=j

以交换两个变量的值。例如,设int i = 3j = 5;计算出i^=j^=i^=j后,我得到了i = 5j = 3

然而,如果我使用两个int指针来重新执行此操作,那么对于*i^=*j^=*i^=*j,使用上面的示例,我将得到i = 0j = 3


在C中

1

    int i=3, j=5;
    i^=j^=i^=j; // after this i = 5, j=3

2

    int i = 3, j= 5;
    int *pi = &i, *pj = &j;
    *pi^=*pj^=*pi^=*pj; // after this, $pi = 0, *pj = 5

在JavaScript中

    var i=3, j=5;
    i^=j^=i^=j; // after this, i = 0, j= 3

JavaScript中的结果让我更感兴趣

我的示例代码,在ubuntu服务器11.0&gcc

    #include <stdio.h>
    int main(){
        int i=7, j=9;
        int *pi=&i, *pj=&j;
        i^=j^=i^=j;
        printf("i=%d j=%d'n", i, j);
        i=7, j=9;
        *pi^=*pj^=*pi^=*pj
        printf("i=%d j=%d'n", *pi, *pj);
    }


c中的未定义行为

c中未定义的行为会是导致这个问题的真正原因吗?

1

使用visualstudio2005在windows7上编译的代码产生预期结果(输出i=7,j=9两次)

2

在ubuntu上使用gcc编译的代码(gcc-test.c)会产生意外结果(输出i=7,j=9,然后i=0,j%9)

3

在ubuntu上使用gcc编译的代码gcc-O test.c)产生预期结果(输出i=7,j=9两次。)

i^=j^=i^=j是C.中未定义的行为

您在两个序列点之间修改i两次,违反了序列点规则。

这意味着实现可以自由地分配任何值,甚至使程序崩溃。

出于同样的原因,*i^=*j^=*i^=*j也是未定义的行为。

(C99,6.5p2)"在上一个和下一个序列点之间,对象的存储值应通过表达式的评估最多修改一次。"

考虑以下代码:

 #include <stdio.h>
    int main() {
        int i=7, j=9;
        int *pi=&i, *pj=&j;
        i^=j^=i^=j;
        printf("i=%d j=%d'n", i, j);
        i=7, j=9;
        *pi^=*pj^=*pi^=*pj;
        printf("i=%d j=%d'n", *pi, *pj);
        return 0;
    }

如果您尝试编译它,您将看到第一行的warning: unsequenced modification。正如@ouath所说,它没有明确定义。根据C11标准,赋值表达式以读-修改-写的方式工作。这个操作并不是所有CPU体系结构上的原子操作。您可以在此处阅读有关警告的更多信息。

有趣的是,对于*pi^=*pj^=*pi^=*pj;,我的LLVM编译器中没有任何警告。

至于Javascript结果添加的"更有趣"方面:

正如ouah的回答中所解释的,这个表达式在C中是未定义的,但在Javascript中是定义良好的。然而,在Javascript中如何计算表达式的规则可能不是您所期望的。

ECMAscript规范规定,复合赋值运算符的求值方式如下(ECMA-262 11.13.2):

生产赋值表达式:LeftHandSideExpression @=AssignmentExpression,其中@表示运算符之一如上所述,评估如下:

  1. 评估LeftHandSideExpression
  2. 调用GetValue(Result(1))
  3. 评估AssignmentExpression
  4. 调用GetValue(Result(3))
  5. 将运算符@应用于Result(2)和Result(4)
  6. 调用PutValue(Result(1),Result(5))
  7. 返回结果(5)

因此,表达式i ^= j ^= i ^= j将在以下步骤中进行评估:

i = (3 ^ (j ^= i ^= j))
i = (3 ^ (j = (5 ^ i ^= j)))
i = (3 ^ (j = (5 ^ (i = 3 ^ j)))))
i = (3 ^ (j = (5 ^ (i = 3 ^ 5)))))
i = (3 ^ (j = (5 ^ (i = 6)))))
i = (3 ^ (j = (5 ^ 6))))
i = (3 ^ (j = 3))   // j is set to 3
i = (3 ^ 3)
i = 0               // i is set to 0

避免使用xor操作交换值的另一个原因。