Recently, in the book to see a data exchange function of the source code, found quite interesting, the specific code is as follows:
1 void swap (intint* b)2{3 *a ^= *b ^= *a ^= *b; 4 }
According to the calculation rules of the C-language XOR assignment operator (^=) and the algorithm of the XOR operator (^), the calculation is performed in a right-to-left order, which demonstrates the following:
1 *a = *a ^ *b; 2 *b = *b ^ *a = *b ^ (*a ^ *b) = *a; // the type 1 into 3 *a = *a ^ *b = (*a ^ *b) ^ *a = *b; // the Formula 1 and type 2 into
As you can see from the calculation process, the values of A and B are actually exchanged, so we check them out by specific procedures:
1#include <stdio.h>2 3 voidSwapintAint*b)4 {5*a ^= *b ^= *a ^= *b;6 }7 8 intMainintargcChar**argv)9 {Ten intA = -, B = the; One Aprintf"before exchange:a =%d, B =%d\n", A, b); -Swap (&a, &b); -printf"After exchange:a =%d, B =%d\n", A, b); the - return 0; -}
The author's computing environment is: LinuxMint 17.3 + gcc 4.8.4/clang 3.5.0. First compile with GCC to see how the results are:
$ gcc-o swap_gcc swap.c $. /0
The result is very surprising, only the value of B is exchanged, and the value of a is 0, why is it 0? We'll come back to the analysis later, and now we'll compile with clang to see how the results are:
$ clang-o swap_clang swap.c$. /Swap_clang
The results are still encouraging, so why are there two different outcomes? The intuitive feel must be related to the compiler, and in order to confirm the idea, we'll look at the differences by disassembling them:
$ objdump-d SWAP_GCC
The paragraphs that are related to the swap function are intercepted as follows:
1000000000040052d <swap>:240052d: -Push%RBP340052E: - theE5 mov%rsp,%RBP4 400531: - the7d F8 mov%rdi,-0x8(%RBP)//Save the value of a5 400535: - the theF0 mov%rsi,-0x10(%RBP)//Save the value of B6 400539: -8b $F8 mov-0x8(%RBP),%Rax740053d:8bTenMOV (%rax),%edx//Take the value of a and deposit into the register edx840053F: -8b $F0 mov-0x10(%RBP),%Rax9 400543: 8b ,MOV (%rax),%ECX//Take the value of B and deposit the register ECXTen 400545: -8b $F8 mov-0x8(%RBP),%Rax One 400549: 8b -MOV (%rax),%ESI//Take a value and deposit to the register ESI A40054B: -8b $F0 mov-0x10(%RBP),%Rax -40054f:8bxxMOV (%rax),%EAX//Take the value of B and deposit the register EAX - 400551: toC6 xor%eax,%ESI//registers the register EAX with the values in the ESI to be stored in the register esi, i.e. *a = *a ^ *b the 400553: -8b $F8 mov-0x8(%RBP),%Rax - 400557: the -mov%esi, (%Rax)//writes the value in the register ESI to the address where the value of a was originally stored, and at this point completes the calculation of the last XOR assignment expression *a ^= *b - 400559: -8b $F8 mov-0x8(%RBP),%Rax -40055d:8bxxMOV (%rax),%EAX//Take the new value of a (that is, *a ^ *b) and into the register eax, note that at this time the register EAX the first saved value is overwritten +40055f: toC1 XOR%eax,%ECX//registers the EAX with the value in ECX and stores it in the register ecx, i.e. *b = *b ^ (*a ^ *b) = *a - 400561: -8b $F0 mov-0x10(%RBP),%Rax + 400565: the ,mov%ecx, (%Rax)//writes the value in the register ECX to the address where the value of the original B was stored, and at this point completes the calculation of the intermediate XOR assignment expression *b ^= *a A 400567: -8b $F0 mov-0x10(%RBP),%Rax at40056b:8bxxMOV (%rax),%EAX//Take the new value of B (that is, *a) and into the register eax, note that this time the register EAX the first saved value is overwritten again -40056D: toC2 XOR%eax,%edx//The register EAX and edx in the value of the difference or operation after depositing into the register edx, found the problem??? -40056F: -8b $F8 mov-0x8(%RBP),%Rax - 400573: the Tenmov%edx, (%Rax)//write the value in the register edx to the address where the value of a is originally stored - 400575: 5d Pop%RBP - 400576: C3 RETQ
With the above assembly code and a brief analysis, do you find the problem? Obviously, there was a problem with the calculation of line 24th, because in this case the register edx is holding the most initial a value, while the register EAX is storing the new value of B (that is, the initial value of a), so the value of the computed register edx is 0 (that is, *a ^ *a). Know the reason, then how to fix this problem? Obviously, you just need to insert the following code between lines 23rd and 24th:
mov -0x8(%RBP),%Raxmov (%rax),%edx
The new value of a (i.e. *a ^ *b) can be retrieved from the above code. Next, let's look at another disassembly scenario:
$ objdmup-d Swap_clang
Similarly, the paragraphs that are related to the swap function are intercepted as follows:
100000000004004e0 <swap>:24004E0: -Push%RBP34004E1: - theE5 mov%rsp,%RBP44004e4: - the7d F8 mov%rdi,-0x8(%RBP)54004e8: - the theF0 mov%rsi,-0x10(%RBP)64004EC: -8b theF0 mov-0x10(%RBP),%RSI74004f0:8b .MOV (%rsi),%eax84004F2: -8b theF8 mov-0x8(%RBP),%RSI94004f6:8b 0e mov (%rsi),%ecxTen4004f8: toC1 XOR%eax,%ecx One4004FA: the0e mov%ecx, (%RSI) A4004FC: -8b theF0 mov-0x10(%RBP),%RSI - 400500: 8b .MOV (%rsi),%eax - 400502: toC8 XOR%ecx,%eax the 400504: the .mov%eax, (%RSI) - 400506: -8b theF8 mov-0x8(%RBP),%RSI -40050a:8b 0e mov (%rsi),%ecx -40050C: toC1 XOR%eax,%ecx +40050E: the0e mov%ecx, (%RSI) - 400510: 5d Pop%RBP + 400511: C3 RETQ
The above code analysis process can refer to the previous part, is also relatively clear and understandable. As can be seen from the operation process, before the XOR operation is to take out the latest value of a or B, that is to ensure that the calculation process strictly according to the steps of the beginning of the article, you will be able to draw the correct value.
Thus, our guess is correct, and the reason is actually related to the compiler. If you want the swap function to work in a different compilation environment, we can split an original or assignment expression into the following 3 XOR expressions.
1 *a ^= *b; 2 *b ^= *A; 3 *a ^= *b;
In addition, when using GCC compilation, if you add the Optimization option-o1/-o2/-o3/-os (which is not optimized by default), we can also get the correct answer. Interested readers can do their own verification, the author will not repeat.
Thinking caused by a data exchange function