如何在C中不使用库函数将大端转换为小端?

问题描述 投票:0回答:14

我需要编写一个函数将大端整数转换为C中的小端整数。我无法使用任何库函数。我该怎么做?

c swap endianness
14个回答
199
投票

假设您需要的是一个简单的字节交换,请尝试类似的操作

无符号16位转换:

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

无符号32位转换:

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

这会将字节顺序从位置 1234 交换到 4321。如果您的输入是

0xdeadbeef
,则 32 位字节序交换可能会输出
0xefbeadde

上面的代码应该用宏或至少常量而不是幻数来清理,但希望它能按原样帮助

编辑:正如另一个答案指出的那样,有特定于平台、操作系统和指令集的替代方案,它们可以比上述方案快得多。在 Linux 内核中,有一些宏(例如 cpu_to_be32)可以很好地处理字节序。但这些替代方案是特定于其环境的。在实践中,最好使用可用方法的混合来处理字节顺序


122
投票

通过包括:

#include <byteswap.h>

您可以获得机器相关字节交换函数的优化版本。 然后,您可以轻松使用以下功能:

__bswap_32 (uint32_t input)

__bswap_16 (uint16_t input)

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);
}

更新:添加了64位字节交换

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);
}

19
投票

这是一个相当通用的版本;我还没有编译过,所以可能有错别字,但你应该明白了,

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));

注意:没有针对速度或空间进行优化。它的目的是清晰(易于调试)和便携。

更新2018-04-04 添加了断言()来捕获 n == 0 的无效情况,正如评论者 @chux 所发现的。


11
投票

如果您需要宏(例如嵌入式系统):

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))

10
投票

编辑:这些是库函数。遵循它们是手动执行此操作的方法。

我对有这么多人不知道 __byteswap_ushort、__byteswap_ulong 和 __byteswap_uint64 感到非常震惊。当然,它们是 Visual C++ 特定的,但它们在 x86/IA-64 架构上编译成一些美味的代码。 :)

这是

bswap
指令的显式用法,从此页面拉出请注意,上面的内在形式将总是比这更快,我添加它只是为了在没有库例程的情况下给出答案。

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}

5
投票

开个玩笑:


#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;
}

5
投票

这里有一种使用 SSSE3 指令 pshufb 的方法,使用其 Intel 内在函数,假设您有 4 的倍数

int

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;
}

3
投票

这会起作用/更快吗?

 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];

3
投票

此代码片段可以将 32 位 Little Endian 数字转换为 Big Endian 数字。

#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);    
}

1
投票

这是我一直在使用的一个函数 - 经过测试并适用于任何基本数据类型:

//  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;
    }

}

1
投票

编辑:此函数仅交换对齐的 16 位字的字节序。 UTF-16/UCS-2 编码经常需要的函数。 编辑结束。

如果您想更改内存块的字节序,您可以使用我的极快方法。 您的内存阵列的大小应为 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);
}

这种函数对于更改 Unicode UCS-2/UTF-16 文件的字节顺序很有用。


0
投票

https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable

偶然发现了在 5 * lg(N) 运算中并行反转 N 位数量的解决方案。

unsigned int v; // 32-bit word to reverse bit order

// swap odd and even bits
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
// swap consecutive pairs
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
// swap nibbles ... 
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
// swap bytes
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = ( v >> 16             ) | ( v               << 16);

在此提供了一种消除对常量依赖的变体,它也是 O(log(n))

    #define CHAR_BIT (8) 
    uint64_t reverse_bits(uint64_t v) {
        uint64_t mask = ~0;
        uint64_t s = sizeof(uint64_t) * CHAR_BIT;
        while((s >>= 1) > 0) {
            mask ^= (mask << s);
            v = (((v >> s) & mask) | ((v << s) & ~mask));
        }
        return v;
    }

如果我们仔细观察操作,我们会发现首先交换奇数位和偶数位,然后交换连续的对,然后是半字节,然后是字节和 2 字节,依此类推,代码以相反的顺序执行。

现在,如果我们只限制自己交换直到半字节,那么我们实际上就是交换字节!这正在改变字节序,因此 while 循环的一个小改变可以导致快速实现。 (将 (s >> 1) > 0 检查条件更改为 (s >> 1) > 2)

uint64_t reverse_endianess(uint64_t v) {
    uint64_t mask = ~0;
    uint64_t s = sizeof(uint64_t) * CHAR_BIT;
    while((s >>= 1) > 2) {
        mask ^= (mask << s);
        v = (((v >> s) & mask) | ((v << s) & ~mask));
    }
    return v;
}

-9
投票

如果您在 x86 或 x86_64 处理器上运行,则大尾数法是本机的。所以

对于 16 位值

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

对于 32 位值

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

这不是最有效的解决方案,除非编译器认识到这是字节级操作并生成字节交换代码。但它不依赖于任何内存布局技巧,并且可以很容易地变成宏。

© www.soinside.com 2019 - 2024. All rights reserved.