简体   繁体   English

为什么i ^ = j ^ = i ^ = j不等于* i ^ = * j ^ = * i ^ = * j

[英]why i^=j^=i^=j isn't equal to *i^=*j^=*i^=*j

In C, when there are variables (assume both as int ) i less than j , we can use the equation 在C中,当有变量(假设两者都是inti小于j ,我们可以使用等式

i^=j^=i^=j

to exchange the value of the two variables. 交换两个变量的值。 For example, let int i = 3 , j = 5 ; 例如,let int i = 3j = 5 ; after computed i^=j^=i^=j , I have i = 5 , j = 3 . 在计算i^=j^=i^=j ,我有i = 5j = 3

However, if I use two int pointers to re-do this, with *i^=*j^=*i^=*j , using the example above, what I have will be i = 0 and j = 3 . 但是,如果我使用两个int指针重新执行此操作,使用上面的示例,使用*i^=*j^=*i^=*j ,我所拥有的将是i = 0j = 3


In C 在C.

1 1

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

2 2

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

In JavaScript 在JavaScript中

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

the result in JavaScript makes this more interesting to me JavaScript中的结果使我对此更感兴趣

my sample code , on ubuntu server 11.0 & gcc 我的示例代码,在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=%dj=%d\\n", i, j); i=7, j=9; *pi^=*pj^=*pi^=*pj printf("i=%dj=%d\\n", *pi, *pj); } 


undefined behavior in c c中的未定义行为

Will the undefined behavior in c be the real reason leads to this question? c中未定义的行为是导致这个问题的真正原因吗?

1 1

code compiled use visual studio 2005 on windows 7 produce the expected result ( Output i = 7, j = 9 twice.) 代码编译使用Visual Studio 2005在Windows 7上产生预期的结果(输出i = 7,j = 9两次。)

2 2

code compiled use gcc on ubuntu ( gcc test.c ) produce the unexpected result ( Output i = 7, j = 9 then i = 0, j = 9 ) 代码编译在ubuntugcc test.c使用gcc产生意外结果(输出i = 7,j = 9然后i = 0,j = 9)

3 3

code compiled use gcc on ubuntu ( gcc -O test.c ) produce the expected result ( Output i = 7,j = 9 twice. ) 代码编译使用uccntu上的gccgcc -O test.c )产生预期的结果(输出i = 7,j = 9两次。)

i^=j^=i^=j is undefined behavior in C. i^=j^=i^=j是C中的未定义行为。

You are violating sequence points rules by modifying i two times between two sequence points. 您通过在两个序列点之间修改i两次来违反序列点规则。

It means the implementation is free to assign any value or even make your program crash. 这意味着实现可以自由分配任何值,甚至使程序崩溃。

For the same reason, *i^=*j^=*i^=*j is also undefined behavior. 出于同样的原因, *i^=*j^=*i^=*j也是未定义的行为。

(C99, 6.5p2) "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression." (C99,6.5p2)“在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估来修改一次。”

Consider the following code: 请考虑以下代码:

 #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;
    }

If you try to compile it you will see a warning: unsequenced modification for the first line. 如果您尝试编译它,您将看到一个warning: unsequenced modification第一行的warning: unsequenced modification As @ouath said it's not well-defined. 正如@ouath所说,它没有明确定义。 According to the C11 Standard the assignment-expressions work in a read-modify-write fashion. 根据C11标准 ,赋值表达式以读 - 修改 - 写方式工作。 This operation isn't atomic on all the CPU architectures. 此操作在所有CPU体系结构上都不是原子操作。 You can read more about the warning here . 您可以在此处阅读有关警告的更多信息。

It's interesting to see that for *pi^=*pj^=*pi^=*pj; 有趣的是,对于*pi^=*pj^=*pi^=*pj; there is no warning in my LLVM compiler. 我的LLVM编译器中没有警告。

As for the 'more interesting' aspect added by the Javascript result: 至于Javascript结果添加的“更有趣”方面:

While the expression is undefined in C as explained in ouah's answer , it is well-defined in Javascript. 虽然在ouah的答案中解释了C中的表达式未定义,但它在Javascript中定义良好。 However the rules for how the expression is evaluated in Javascript might not be what you expect. 但是,如何在Javascript中评估表达式的规则可能不是您所期望的。

The ECMAscript spec says that a compound assignment operator is evaluated like so (ECMA-262 11.13.2): ECMAscript规范说复合赋值运算符的评估如下(ECMA-262 11.13.2):

The production AssignmentExpression : LeftHandSideExpression @= AssignmentExpression,where @ represents one of the operators indicated above, is evaluated as follows: 生产AssignmentExpression:LeftHandSideExpression @= AssignmentExpression,其中@代表上面指出的一个运算符,评估如下:

  1. Evaluate LeftHandSideExpression. 评估LeftHandSideExpression。
  2. Call GetValue(Result(1)). 调用GetValue(Result(1))。
  3. Evaluate AssignmentExpression. 评估AssignmentExpression。
  4. Call GetValue(Result(3)). 调用GetValue(Result(3))。
  5. Apply operator @ to Result(2) and Result(4). 将operator @应用于Result(2)和Result(4)。
  6. Call PutValue(Result(1), Result(5)). 调用PutValue(结果(1),结果(5))。
  7. Return Result(5). 返回结果(5)。

So the expression i ^= j ^= i ^= j would be evaluated in the following steps: 因此,表达式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

Yet another reason to avoid the trick of using an xor operation to swap values. 避免使用xor操作交换值的技巧的另一个原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM