I recently wrote a macro to do this in C, but it's equally valid in C++:
#define REVERSE_BYTES(...) do for(size_t REVERSE_BYTES=0; REVERSE_BYTES<sizeof(__VA_ARGS__)>>1; ++REVERSE_BYTES)\
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES],\
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES],\
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES] ^= ((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES];\
while(0)
It accepts any type and reverses the bytes in the passed argument.
Example usages:
int main(){
unsigned long long x = 0xABCDEF0123456789;
printf("Before: %llX\n",x);
REVERSE_BYTES(x);
printf("After : %llX\n",x);
char c[7]="nametag";
printf("Before: %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
REVERSE_BYTES(c);
printf("After : %c%c%c%c%c%c%c\n",c[0],c[1],c[2],c[3],c[4],c[5],c[6]);
}
Which prints:
Before: ABCDEF0123456789
After : 8967452301EFCDAB
Before: nametag
After : gateman
The above is perfectly copy/paste-able, but there's a lot going on here, so I'll break down how it works piece by piece:
The first notable thing is that the entire macro is encased in a do while(0)
block. This is a common idiom to allow normal semicolon use after the macro.
Next up is the use of a variable named REVERSE_BYTES
as the for
loop's counter. The name of the macro itself is used as a variable name to ensure that it doesn't clash with any other symbols that may be in scope wherever the macro is used. Since the name is being used within the macro's expansion, it won't be expanded again when used as a variable name here.
Within the for
loop, there are two bytes being referenced and XOR swapped (so a temporary variable name is not required):
((unsigned char*)&(__VA_ARGS__))[REVERSE_BYTES]
((unsigned char*)&(__VA_ARGS__))[sizeof(__VA_ARGS__)-1-REVERSE_BYTES]
__VA_ARGS__
represents whatever was given to the macro, and is used to increase the flexibility of what may be passed in (albeit not by much). The address of this argument is then taken and cast to an unsigned char
pointer to permit the swapping of its bytes via array []
subscripting.
The final peculiar point is the lack of {}
braces. They aren't necessary because all of the steps in each swap are joined with the comma operator, making them one statement.
Finally, it's worth noting that this is not the ideal approach if speed is a top priority. If this is an important factor, some of the type-specific macros or platform-specific directives referenced in other answers are likely a better option. This approach, however, is portable to all types, all major platforms, and both the C and C++ languages.
short swap(short x)
code, since it will break if you move to a platform with different endianness. Matthieu M has the only right answer below. – Chimney