HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMajor

Swapping two integer numbers with no temporary variable

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
temporarywithnumbersswappingtwovariableinteger

Problem

I tried to swap 2 integer numbers without using an additional variable as a traditional swap.

Is it legal in C++? My VC compiler doesn't complain nor gives any warning about it. If so, how can I improve this script?

#include 

int main()
{
    int a = 20;
    int b = 66;

    // before swapping
    std::cout << a  << ' ' << b  << '\n';

    // swap
    a ^= b ^= a ^= b;

    // after swapping
    std::cout << a << ' ' << b << '\n';
}


For this code:

int a = 20;
int b = 66;
a ^= b ^= a ^= b;


Assembler output for VC++ 2013:

_b$ = -20                     ; size = 4
_a$ = -8                      ; size = 4

mov   DWORD PTR _a$[ebp], 20          ; 00000014H
mov   DWORD PTR _b$[ebp], 66          ; 00000042H

mov   eax, DWORD PTR _a$[ebp]
xor   eax, DWORD PTR _b$[ebp]
mov   DWORD PTR _a$[ebp], eax

mov   ecx, DWORD PTR _b$[ebp]
xor   ecx, DWORD PTR _a$[ebp]
mov   DWORD PTR _b$[ebp], ecx

mov   edx, DWORD PTR _a$[ebp]
xor   edx, DWORD PTR _b$[ebp]
mov   DWORD PTR _a$[ebp], edx


For this code:

int a = 20;
int b = 66;

int t = a;
a = b;
b = t;


Assembler output for VC++ 2013:

_t$ = -32                     ; size = 4
_b$ = -20                     ; size = 4
_a$ = -8                      ; size = 4

mov   DWORD PTR _a$[ebp], 20          ; 00000014H
mov   DWORD PTR _b$[ebp], 66          ; 00000042H

mov   eax, DWORD PTR _a$[ebp]
mov   DWORD PTR _t$[ebp], eax

mov   eax, DWORD PTR _b$[ebp]
mov   DWORD PTR _a$[ebp], eax

mov   eax, DWORD PTR _t$[ebp]
mov   DWORD PTR _b$[ebp], eax

Solution

You make assumptions which may not be true. Why do you believe that

int tmp = a;
a = b;
b = tmp;


actually is compiled down to using an actual variable? It is likely just a register used on the CPU.

Have you inspected it?

Further, why do you assume that:

a ^= b ^= a ^= b;


uses fewer registers than a swap?

Really, what you should do is:

#include 
#include 

int main() {

    int a = 20;
    int b = 66;

    // before swapping
    std::cout << a  << ' ' << b  << '\n';

    // swap
    std::swap(a,b);

    // after swapping
    std::cout << a << ' ' << b << '\n';

}


Which is also a reminder that having the a ^= b ^= a ^= b; 'naked' in your code is not good practice. Something like that should be embedded in a function, not directly in the main method.
Update - assembler output

For the code:

int a = 20;
int b = 66;

int t = a;
a = b;
b = t;

return a;


you get the assembler output:

movl    $20, -12(%rbp)
movl    $66, -8(%rbp)
movl    -12(%rbp), %eax
movl    %eax, -4(%rbp)
movl    -8(%rbp), %eax
movl    %eax, -12(%rbp)
movl    -4(%rbp), %eax
movl    %eax, -8(%rbp)
movl    -12(%rbp), %eax
popq    %rbp


For the code:

int a = 20;
int b = 66;

a ^= b ^= a ^= b;

return a;


you get

movl    $20, -8(%rbp)
movl    $66, -4(%rbp)
movl    -4(%rbp), %eax
xorl    %eax, -8(%rbp)
movl    -8(%rbp), %eax
xorl    %eax, -4(%rbp)
movl    -4(%rbp), %eax
xorl    %eax, -8(%rbp)
movl    -8(%rbp), %eax
popq    %rbp


What does that show?

  • It shows that both systems run in 12 instructions, including the copy to the stack (%rbp)



  • that both systems use the single register %eax



  • both systems use the stack as a temp store for the result (the XOR reuses -8 and -4 offsets in the stack, the tmp uses -12(%rbp)



Net result? Both systems use less than 16 bytes of the stack, they both use 1 register in addition to the stack, and they both have the same number of instructions.

I know which one is more readable....

Of course, with the above code, if I add -O2 to the optimization, I get the assembler:

movl    $66, %eax
ret


which, as you can imagine, is fast.

Code Snippets

int tmp = a;
a = b;
b = tmp;
a ^= b ^= a ^= b;
#include <algorithm>
#include <iostream>

int main() {

    int a = 20;
    int b = 66;

    // before swapping
    std::cout << a  << ' ' << b  << '\n';

    // swap
    std::swap(a,b);

    // after swapping
    std::cout << a << ' ' << b << '\n';

}
int a = 20;
int b = 66;

int t = a;
a = b;
b = t;

return a;
movl    $20, -12(%rbp)
movl    $66, -8(%rbp)
movl    -12(%rbp), %eax
movl    %eax, -4(%rbp)
movl    -8(%rbp), %eax
movl    %eax, -12(%rbp)
movl    -4(%rbp), %eax
movl    %eax, -8(%rbp)
movl    -12(%rbp), %eax
popq    %rbp

Context

StackExchange Code Review Q#76976, answer score: 21

Revisions (0)

No revisions yet.