114

I need to write a function to convert a big endian integer to a little endian integer in C. I cannot use any library function. How would I do this?

user16217248
  • 3,119
  • 19
  • 19
  • 37
Alex Xander
  • 3,903
  • 14
  • 36
  • 43

13 Answers13

198

Assuming what you need is a simple byte swap, try something like

Unsigned 16 bit conversion:

swapped = (num>>8) | (num<<8);

Unsigned 32-bit conversion:

swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
                    ((num<<8)&0xff0000) | // move byte 1 to byte 2
                    ((num>>8)&0xff00) | // move byte 2 to byte 1
                    ((num<<24)&0xff000000); // byte 0 to byte 3

This swaps the byte orders from positions 1234 to 4321. If your input was 0xdeadbeef, a 32-bit endian swap might have output of 0xefbeadde.

The code above should be cleaned up with macros or at least constants instead of magic numbers, but hopefully it helps as is

EDIT: as another answer pointed out, there are platform, OS, and instruction set specific alternatives which can be MUCH faster than the above. In the Linux kernel there are macros (cpu_to_be32 for example) which handle endianness pretty nicely. But these alternatives are specific to their environments. In practice endianness is best dealt with using a blend of available approaches

Leandros
  • 16,805
  • 9
  • 69
  • 108
Sam Post
  • 3,721
  • 3
  • 17
  • 14
  • 5
    +1 for mentioning platform/hardware-specific methods. Programs are always run on some hardware, and hardware features are always fastest. – eonil Jul 31 '12 at 06:47
  • 23
    if the 16 bit conversion is done as `((num & 0xff) >> 8) | (num << 8)`, gcc 4.8.3 generates a single `rol` instruction. And if 32 bit conversion is written as `((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)`, same compiler generates a single `bswap` instruction. – user666412 Apr 30 '15 at 16:51
  • I dont know how efficient this is but Ive swapped the byte order with bitfields like this: `struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}` where this is a bitfield with 8 fields 1 bit each. But I am not sure if thats as fast as the other suggestions. For ints use the `union { int i; byte_t[sizeof(int)]; }` to reverse byte by byte in the integer. – Ilian Zapryanov Sep 26 '16 at 22:47
  • I think the expression must be: (num >> 8) | (num << 8) to reverse the byte order and NOT: ((num & 0xff) >> 8) | (num << 8), The wrong example gets zero in the low-byte. – j.s.com Feb 24 '17 at 17:14
  • @IlianZapryanov Maybe +1 for clarity but using bitfields in C like that is probably the least efficient way to do it. – sherrellbc Sep 26 '19 at 22:47
122

By including:

#include <byteswap.h>

you can get an optimized version of machine-dependent byte-swapping functions. Then, you can easily use the following functions:

__bswap_32 (uint32_t input)

or

__bswap_16 (uint16_t input)
BenC
  • 8,729
  • 3
  • 49
  • 68
Amir Mgh
  • 1,369
  • 1
  • 8
  • 3
  • 4
    Thanks for your answer, but I can not use any library function – Mark Ransom Aug 05 '11 at 19:03
  • 4
    Should read `#include `, see comment in the .h file itself. This post contains helpful information so I up-voted despite the author ignoring the OP requirement to not use a lib function. – Eli Rosencruft May 20 '12 at 12:48
  • 33
    In fact, the __bswap_32/__bswap_16 functions are in fact macros and not library functions, another reason to up-vote. – Eli Rosencruft May 20 '12 at 13:56
  • Where can I find this header? The only I got was LGPL, and doesn't look one of standard header. It means it may not available on LGPL-incompatible system and I can't copy it into the system. – eonil Jul 31 '12 at 06:44
  • 7
    My understanding is that this header is not guaranteed to exist for all operating systems on all architectures. I have yet to find a portable way to deal with endian issues. – Edward Falk Feb 18 '13 at 23:04
  • While I prefer the accepted answer, no constraint on portability was requested, so this answer is legit in my books. – Cloud May 01 '14 at 20:19
  • 2
    doesn't exist on windows - at least not when cross compiling from linux with mingw 32 or 64 bit – bph Jan 20 '16 at 12:44
  • 1
    Doesn't exist on mac too. – Evgenia Karunus Dec 17 '17 at 01:09
  • 9
    `#include ` is not part of the C standard library. – chux - Reinstate Monica Apr 02 '18 at 13:39
  • This solution is non-portable and also ignores the request that the solution is not a library function. – user16217248 Mar 28 '23 at 17:20
76
#include <stdint.h>


//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val ) 
{
    return (val << 8) | (val >> 8 );
}

//! Byte swap short
int16_t swap_int16( int16_t val ) 
{
    return (val << 8) | ((val >> 8) & 0xFF);
}

//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
    val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | (val >> 16);
}

//! Byte swap int
int32_t swap_int32( int32_t val )
{
    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); 
    return (val << 16) | ((val >> 16) & 0xFFFF);
}

Update : Added 64bit byte swapping

int64_t swap_int64( int64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}

uint64_t swap_uint64( uint64_t val )
{
    val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
    val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
    return (val << 32) | (val >> 32);
}
chmike
  • 20,922
  • 21
  • 83
  • 106
  • For the `int32_t` and `int64_t` variants, what is the reasoning behind the masking of `... & 0xFFFF` and `... & 0xFFFFFFFFULL`? Is there something going on with sign-extension here I'm not seeing? Also, why is `swap_int64` returning `uint64_t`? Shouldn't that be `int64_t`? – bgoodr Nov 02 '12 at 14:57
  • 1
    The swap_int64 returning a uint64 is indeed an error. The masking with signed int values is indeed to remove the sign. Shifting right injects the sign bit on the left. We could avoid this by simply calling the unsigned int swapping operation. – chmike Nov 03 '12 at 15:37
  • Thanks. You might want to change the type of the return value for `swap_int64` in your answer. +1 for the helpful answer, BTW! – bgoodr Nov 04 '12 at 17:18
  • Is the bitwise and value endian dependent? – MarcusJ May 04 '15 at 21:57
  • 1
    The `LL` are unnecessary in `(u)swap_uint64()` much like an `L` is not needed in `(u)swap_uint32()`. The `U` is not needed in `uswap_uint64()` much like the `U` is not needed in `uswap_uint32()` – chux - Reinstate Monica Apr 02 '18 at 13:10
19

Here's a fairly generic version; I haven't compiled it, so there are probably typos, but you should get the idea,

void SwapBytes(void *pv, size_t n)
{
    assert(n > 0);

    char *p = pv;
    size_t lo, hi;
    for(lo=0, hi=n-1; hi>lo; lo++, hi--)
    {
        char tmp=p[lo];
        p[lo] = p[hi];
        p[hi] = tmp;
    }
}
#define SWAP(x) SwapBytes(&x, sizeof(x));

NB: This is not optimised for speed or space. It is intended to be clear (easy to debug) and portable.

Update 2018-04-04 Added the assert() to trap the invalid case of n == 0, as spotted by commenter @chux.

Michael J
  • 7,631
  • 2
  • 24
  • 30
  • 1
    you can use xorSwap for better performance. Prefer this generic version above all the size specific ones... –  Jun 27 '10 at 02:14
  • I tested it, it turns out this is faster than xorSwap... on x86. http://stackoverflow.com/questions/3128095/best-way-to-test-code-speed-in-c-without-profiler-or-does-it-not-make-sense-to –  Aug 16 '10 at 13:39
  • 1
    @nus -- One of the advantages of very simple code is that the compiler optimiser can sometimes make it very fast. – Michael J Dec 24 '13 at 06:31
  • @MichaelJ OTOH, the 32 bit version above in chmike's answer gets compiled to a single `bswap` instruction by a decent X86 compiler with optimisation enabled. This version with a parameter for the size couldn't do that. – Alnitak Apr 21 '17 at 13:37
  • @Alnitak - As I said, I made no effort tp optimise my code. When user nus found that the code ran very fast (in one case) I just mentioned the general idea that simple code can often be highly optimised by a compiler. My code works for a wide variety of cases and it is pretty easy to understand and thus easy to debug. That met my objectives. – Michael J Apr 22 '17 at 13:21
  • 1
    I'd expect `SwapBytes()` to nicely handle the corner case of `SwapBytes(pv, 0)`. With this code, `SwapBytes(pv, 0)` leads to UB. – chux - Reinstate Monica Apr 02 '18 at 13:18
  • @chux -- That is not exaactly an edge case as an out of range, but I agree it is a problem. Perhaps the root of the problem is that I made an assumption (n must be >0) without documenting or testing that assumption. I shall shortly amend the code. It is a great reminder of the value of code review. Even very simple code can contain subtle problems. – Michael J Apr 03 '18 at 14:32
  • Perhaps `for(lo=0, hi=n; lo – chux - Reinstate Monica Apr 03 '18 at 17:52
  • declaration of char temp inside the for loop, not the efficient way. – Ayush joshi Jun 12 '18 at 01:27
  • @ Ayush joshi - I was not trying to be efficient, but simple and easy to understand. Notwithstanding that, I would expect an optimising compiler to handle that without any loss of efficiency. Only on an embedded system or similarly constrained environment would I worry about that. – Michael J Jun 13 '18 at 06:02
11

If you need macros (e.g. embedded system):

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
kol
  • 27,881
  • 12
  • 83
  • 120
  • These macros are fine, but ((x) >> 24) will fail when a signed integer is between 0x80000000 and 0xffffffff. It's a good idea to use bitwise AND here. Note: ((x) << 24) is perfectly safe. (x) >> 8) will also fail if high 16 bits are nonzero (or a signed 16-bit value is provided). –  Nov 19 '14 at 14:59
  • 2
    @PacMan-- These macros are intended to be used to swap *unsigned* integers only. That's why there is the `UINT` in their name. – kol Nov 19 '14 at 19:06
  • Yes, true, sorry for the noise. Wouldn't it be best to embed a typecast ? –  Nov 19 '14 at 20:34
10

Edit: These are library functions. Following them is the manual way to do it.

I am absolutely stunned by the number of people unaware of __byteswap_ushort, __byteswap_ulong, and __byteswap_uint64. Sure they are Visual C++ specific, but they compile down to some delicious code on x86/IA-64 architectures. :)

Here's an explicit usage of the bswap instruction, pulled from this page. Note that the intrinsic form above will always be faster than this, I only added it to give an answer without a library routine.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • 23
    For a C question, you're suggesting something that's specific to Visual C++? – Alok Singhal Feb 02 '10 at 06:29
  • 3
    @Alok: Visual C++ is a product by Microsoft. It works just fine for compiling C code. :) – Sam Harwell Feb 02 '10 at 06:30
  • 20
    Why does it stun you that many people aren't aware of Microsoft-specific implementations of byteswapping? – dreamlax Feb 02 '10 at 06:32
  • 1
    @dreamlax: Because I've worked in commercial environments that use Visual Studio as the official IDE for the better part of a decade at quite a few companies (contract work). – Sam Harwell Feb 02 '10 at 06:34
  • 36
    Cool, that's good info for anyone developing a closed source product which doesn't need to be portable or standards compliant. – Sam Post Feb 02 '10 at 06:38
  • @280Z28: OK, but I think that the question is more general than your answer, and I hope you agree. Not that the question is very good to begin with :-) – Alok Singhal Feb 02 '10 at 06:42
  • @280Z28: I guess non-portable methods of byteswapping has been a very common task at the places you have worked. – dreamlax Feb 02 '10 at 06:55
  • the question says no library functions – John Knoeller Feb 02 '10 at 06:58
  • @John Knoeller: which is why I added the manual implementation. :) It says no library functions but doesn't rule out inline assembly. – Sam Harwell Feb 02 '10 at 08:06
  • @280z28: can't beat bswap. If you are going to go MS specific, might as well go whole hog `uint32 __fastcall(uint32 a) { __asm { mov eax, edx; bswap eax; } }` – John Knoeller Feb 02 '10 at 08:52
  • @John Knoeller: the intrinsics *annihilate* the performance of that call, which is why I didn't bother to do anything past what's up there. – Sam Harwell Feb 02 '10 at 09:06
  • 6
    @Alok, OP did not mention the compiler|OS. A person is allowed to give answers according to his experience with a particular set of tools. – Aniket Inge Nov 05 '12 at 09:04
5

here's a way using the SSSE3 instruction pshufb using its Intel intrinsic, assuming you have a multiple of 4 ints:

unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
    int i;
    __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
    for (i = 0; i < length; i += 4) {
        _mm_storeu_si128((__m128i *)&destination[i],
        _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
    }
    return destination;
}
jcomeau_ictx
  • 37,688
  • 6
  • 92
  • 107
5

As a joke:


#include <stdio.h>

int main (int argc, char *argv[])
{
    size_t sizeofInt = sizeof (int);
    int i;

    union
    {
        int x;
        char c[sizeof (int)];
    } original, swapped;

    original.x = 0x12345678;

    for (i = 0; i < sizeofInt; i++)
        swapped.c[sizeofInt - i - 1] = original.c[i];

    fprintf (stderr, "%x\n", swapped.x);

    return 0;
}
dreamlax
  • 93,976
  • 29
  • 161
  • 209
3

This code snippet can convert 32bit little Endian number to Big Endian number.

#include <stdio.h>
main(){    
    unsigned int i = 0xfafbfcfd;
    unsigned int j;    
    j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);    
    printf("unsigned int j = %x\n ", j);    
}
  • Thanks @YuHao I am new here, don't know how to format the Text. – Kaushal Billore Jul 02 '13 at 11:21
  • 2
    Using `((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);` might be faster on some platforms (eg. recycling the AND mask constants). Most compilers would do this, though, but some simple compilers are not able to optimize it for you. –  Nov 19 '14 at 15:10
3

Will this work / be faster?

 uint32_t swapped, result;

((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Paul
  • 31
  • 1
  • 2
    I think you mean `char`, not `byte`. – dreamlax Aug 04 '10 at 21:42
  • Using this strategy, the solution with most votes compared to yours is equivalent and the most efficient and portable. However the solution I propose (second most votes) needs less operations and should be more efficient. – chmike Dec 05 '12 at 09:07
1

Here's a function I have been using - tested and works on any basic data type:

//  SwapBytes.h
//
//  Function to perform in-place endian conversion of basic types
//
//  Usage:
//
//    double d;
//    SwapBytes(&d, sizeof(d));
//

inline void SwapBytes(void *source, int size)
{
    typedef unsigned char TwoBytes[2];
    typedef unsigned char FourBytes[4];
    typedef unsigned char EightBytes[8];

    unsigned char temp;

    if(size == 2)
    {
        TwoBytes *src = (TwoBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[1];
        (*src)[1] = temp;

        return;
    }

    if(size == 4)
    {
        FourBytes *src = (FourBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[3];
        (*src)[3] = temp;

        temp = (*src)[1];
        (*src)[1] = (*src)[2];
        (*src)[2] = temp;

        return;
    }

    if(size == 8)
    {
        EightBytes *src = (EightBytes *)source;
        temp = (*src)[0];
        (*src)[0] = (*src)[7];
        (*src)[7] = temp;

        temp = (*src)[1];
        (*src)[1] = (*src)[6];
        (*src)[6] = temp;

        temp = (*src)[2];
        (*src)[2] = (*src)[5];
        (*src)[5] = temp;

        temp = (*src)[3];
        (*src)[3] = (*src)[4];
        (*src)[4] = temp;

        return;
    }

}
ticketman
  • 81
  • 1
  • 4
1

EDIT: This function only swaps the endianness of aligned 16 bit words. A function often necessary for UTF-16/UCS-2 encodings. EDIT END.

If you want to change the endianess of a memory block you can use my blazingly fast approach. Your memory array should have a size that is a multiple of 8.

#include <stddef.h>
#include <limits.h>
#include <stdint.h>

void ChangeMemEndianness(uint64_t *mem, size_t size) 
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;

size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
  *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}

This kind of function is useful for changing the endianess of Unicode UCS-2/UTF-16 files.

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
-9

If you are running on a x86 or x86_64 processor, the big endian is native. so

for 16 bit values

unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);

for 32 bit values

unsigned int   iBigE = value;
unsigned int   iLittleE = ((iBigE & 0xFF) << 24)
                        | ((iBigE & 0xFF00) << 8)
                        | ((iBigE >> 8) & 0xFF00)
                        | (iBigE >> 24);

This isn't the most efficient solution unless the compiler recognises that this is byte level manipulation and generates byte swapping code. But it doesn't depend on any memory layout tricks and can be turned into a macro pretty easily.

John Knoeller
  • 33,512
  • 4
  • 61
  • 92