在C中将大端转换为小端[不使用提供的函数]

Ale*_*der 87 c swap endianness

我需要在C中编写一个将big endian转换为little endian的函数.我不能使用任何库函数.

Sam*_*ost 153

假设你需要的是一个简单的字节交换,尝试类似的东西

无符号16位转换:

swapped = (num>>8) | (num<<8);
Run Code Online (Sandbox Code Playgroud)

无符号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
Run Code Online (Sandbox Code Playgroud)

这将字节顺序从位置1234交换到4321.如果输入是0xdeadbeef,则32位字节序交换可能具有输出0xefbeadde.

应该使用宏或至少常量而不是幻数来清除上面的代码,但希望它有所帮助

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

  • 如果16位转换完成为`((num&0xff)>> 8)| (num << 8)`,gcc 4.8.3生成一条`rol`指令.如果32位转换写为`((num&0xff000000)>> 24)| ((num&0x00ff0000)>> 8)| ((num&0x0000ff00)<< 8)| (num << 24)`,同一个编译器生成一个`bswap`指令. (19认同)
  • +1提及平台/硬件特定方法.程序总是在某些硬件上运行,硬件功能总是最快的. (4认同)

小智 93

包括:

#include <byteswap.h>
Run Code Online (Sandbox Code Playgroud)

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

__bswap_32 (uint32_t input)
Run Code Online (Sandbox Code Playgroud)

要么

__bswap_16 (uint16_t input)
Run Code Online (Sandbox Code Playgroud)

  • 事实上,__bswap_32/__ bswap_16函数实际上是宏而不是库函数,这是投票的另一个原因. (29认同)
  • 我的理解是,并不保证所有体系结构上的所有操作系统都存在此标头.我还没有找到一种处理endian问题的便携方式. (7认同)
  • 谢谢你的回答,但我不能使用任何库函数 (3认同)
  • 应阅读`#include <byteswap.h>`,请参阅.h文件本身的注释.这篇文章包含有用的信息,所以尽管作者忽略了不要使用lib函数的OP要求,但我还是进行了投票. (3认同)
  • Windows 上不存在 - 至少在使用 mingw 32 或 64 位从 linux 交叉编译时不存在 (2认同)
  • `#include &lt;byteswap.h&gt;` 不是 C 标准库的一部分。 (2认同)

chm*_*ike 58

#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);
}
Run Code Online (Sandbox Code Playgroud)

更新:添加了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);
}
Run Code Online (Sandbox Code Playgroud)


Mic*_*l J 12

这是一个相当通用的版本; 我没有编译它,所以可能有拼写错误,但你应该明白,

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));
Run Code Online (Sandbox Code Playgroud)

注意:不是针对速度或空间进行优化的.它旨在清晰(易于调试)和便携.

更新2018-04-04 添加了assert()来捕获n == 0的无效情况,由评论者@chux发现.


kol*_*kol 8

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

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
Run Code Online (Sandbox Code Playgroud)

  • @ PacMan--这些宏仅用于交换*unsigned*整数.这就是为什么他们的名字中有'UINT`的原因. (2认同)

Sam*_*ell 6

编辑:这些是库函数.遵循它们是手动方式.

我完全被不了解__byteswap_ushort,__ timeswap_ulong和__byteswap_uint64的人数惊呆了.当然它们是特定于Visual C++的,但是它们可以在x86/IA-64架构上编译成一些美味的代码.:)

这是从该页面中提取bswap指令的明确用法.请注意,上面的内在形式总是比这更快,我只添加它来给出没有库例程的答案.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很酷,对于任何开发不需要便携或符合标准的封闭源产品的人来说,这都是一个很好的信息. (33认同)
  • 对于C问题,您建议使用特定于Visual C++的内容吗? (21认同)
  • 为什么许多人不了解特定于Microsoft的byteswapping实现会让你感到震惊? (19认同)
  • @Alok,OP没有提到编译器| OS.允许一个人根据他对一组特定工具的经验给出答案. (6认同)
  • @Alok:Visual C++是Microsoft的产品.它适用于编译C代码.:) (3认同)

dre*_*lax 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 哈哈哈哈哈.哈哈哈.哈.哈?(什么玩笑?) (7认同)
  • 你从一些Windows源代码库中提取了这个吗?:) (3认同)

jco*_*ctx 5

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

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;
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ler -8

如果系统是大端:

对于 16 位值:

unsigned short big = value;
unsigned short little = ((big & 0xFF) << 8) | (big >> 8);
Run Code Online (Sandbox Code Playgroud)

对于 32 位值:

unsigned int big = value;
unsigned int little = ((big & 0xFF) << 24)
                    | ((big & 0xFF00) << 8)
                    | ((big >> 8) & 0xFF00)
                    | (big >> 24);
Run Code Online (Sandbox Code Playgroud)

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

  • 在 x86 和 x86_64 架构上,小端模式是本机模式。 (28认同)