How do I convert between big-endian and little-endian values in C++?

前端 未结 30 2580
难免孤独
难免孤独 2020-11-21 23:18

How do I convert between big-endian and little-endian values in C++?

EDIT: For clarity, I have to translate binary data (double-precision floating point values and 3

30条回答
  •  鱼传尺愫
    2020-11-21 23:54

    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>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.

提交回复
热议问题