【问题标题】:convert big endian to little endian in C [without using provided func] [closed]在C中将大端转换为小端[不使用提供的函数] [关闭]
【发布时间】:2011-01-12 00:36:58
【问题描述】:

我需要在 C 中编写一个将大端转换为小端的函数。我不能使用任何库函数。

【问题讨论】:

  • 16 位值? 32位值?漂浮?一个数组?
  • 也许是时候选择答案了?
  • 投票重新开放。与 C++ 的 stackoverflow.com/questions/105252/… 相同。我们可以进行编辑以使其更清晰。
  • 我觉得已经够清楚了。请取消关闭问题。
  • gcc 和 g++ 正在正确检测此类交换并将它们转换为一两条指令,但您可能需要使用 -O3 或至少 -O2。因此,您应该编写一个简单的函数来与inline 进行交换,它会自动为您完成这项工作。

标签: c swap endianness


【解决方案1】:

如果您在 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);

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

【讨论】:

  • 在 x86 和 x86_64 架构上,小端方案是原生方案。
【解决方案2】:

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

无符号 16 位转换:

swapped = (num&gt;&gt;8) | (num&lt;&lt;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)可以很好地处理字节序。但是这些替代方案是特定于它们的环境的。在实践中,最好使用多种可用方法来处理字节序

【讨论】:

  • +1 用于提及特定于平台/硬件的方法。程序总是在某些硬件上运行,而硬件功能总是最快的。
  • 如果 16 位转换以 ((num &amp; 0xff) &gt;&gt; 8) | (num &lt;&lt; 8) 完成,gcc 4.8.3 生成单个 rol 指令。如果 32 位转换写成((num &amp; 0xff000000) &gt;&gt; 24) | ((num &amp; 0x00ff0000) &gt;&gt; 8) | ((num &amp; 0x0000ff00) &lt;&lt; 8) | (num &lt;&lt; 24),同样的编译器会生成一条bswap 指令。
  • 我不知道这有多有效,但我已经用这样的位域交换了字节顺序: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;} 其中这是一个位域,有 8 个域,每个域 1 位。但我不确定这是否和其他建议一样快。对于整数,使用union { int i; byte_t[sizeof(int)]; } 在整数中逐字节反转。
  • 我认为表达式必须是:(num >> 8) | (num > 8) | (num
【解决方案3】:

编辑:这些是库函数。跟随它们是手动的方法。

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

这是bswap 指令pulled from this page 的显式用法。 请注意,上面的内在形式总是比这更快,我只是添加它来给出答案,而不需要库例程。

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

【讨论】:

  • 对于 C 问题,您是在建议一些特定于 Visual C++ 的内容?
  • @Alok:Visual C++ 是 Microsoft 的产品。它适用于编译 C 代码。 :)
  • 为什么很多人不知道 Microsoft 特定的字节交换实现让您感到震惊?
  • 酷,这对于开发不需要便携或符合标准的封闭源代码产品的任何人来说都是很好的信息。
  • @Alok,OP 没有提到编译器|操作系统。允许一个人根据他使用特定工具集的经验给出答案。
【解决方案4】:

开个玩笑:


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

【讨论】:

  • 哈哈哈哈哈哈。哈哈哈。哈。哈? (开什么玩笑?)
  • 您是从某个 Windows 源代码库中提取的吗? :)
  • Nodejs 使用了这种技术! github.com/nodejs/node/blob/…
  • 很好奇使用int i, size_t sizeofInt 并且两者的类型不同。
【解决方案5】:

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

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 更新 添加了 assert() 以捕获 n == 0 的无效情况,正如评论者 @chux 所发现的那样。

【讨论】:

  • 您可以使用 xorSwap 以获得更好的性能。比所有特定尺寸的更喜欢这个通用版本...
  • 我测试了它,结果证明这比 xorSwap... 在 x86 上更快。 stackoverflow.com/questions/3128095/…
  • @nus -- 非常简单的代码的优点之一是编译器优化器有时可以使其非常快。
  • @MichaelJ OTOH,上面 chmike 的答案中的 32 位版本被启用优化的体面的 X86 编译器编译为单个 bswap 指令。这个带有尺寸参数的版本无法做到这一点。
  • 我希望SwapBytes() 能够很好地处理SwapBytes(pv, 0) 的极端情况。使用此代码,SwapBytes(pv, 0) 会导致 UB。
【解决方案6】:

编辑:此函数仅交换对齐的 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 文件的字节顺序很有用。

【讨论】:

  • CHAR_BIT #define 缺失以使代码完整。
  • 好的,我添加了缺少的包含。
  • 这里是 C++ 中交换的链接,我不t know if its 和建议一样快,但它工作:github.com/heatblazer/helpers/blob/master/utils.h
  • CHAR_BIT 而不是8 很奇怪,因为0xFF00FF00FF00FF00ULL 依赖于CHAR_BIT == 8。请注意,常量中不需要LL
  • 你说得对。只写了CHAR_BIT 以增加该宏的曝光率。至于 LL,它更像是一个注解。这也是我很久以前用错误的编译器(标准前)养成的习惯,它不会做正确的事情。
【解决方案7】:
#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);
}

【讨论】:

  • 对于int32_tint64_t 变体,隐藏... &amp; 0xFFFF... &amp; 0xFFFFFFFFULL 背后的原因是什么?我没有看到这里的符号扩展有什么问题吗?另外,为什么swap_int64 会返回uint64_t?不应该是int64_t吗?
  • swap_int64 返回 uint64 确实是一个错误。使用带符号的 int 值进行掩码确实是为了删除符号。右移会在左边注入符号位。我们可以通过简单地调用 unsigned int 交换操作来避免这种情况。
  • 谢谢。您可能希望在答案中更改 swap_int64 的返回值类型。 +1 有用的答案,顺便说一句!
  • 位和值端是否依赖?
  • LL(u)swap_uint64() 中是不必要的,就像在(u)swap_uint32() 中不需要Luswap_uint64() 中不需要 U,就像 uswap_uint32() 中不需要 U 一样
【解决方案8】:

这会工作/更快吗?

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

【讨论】:

  • 我想你的意思是char,而不是byte
  • 使用此策略,与您相比得票最多的解决方案是等效的,并且是最有效和最便携的。然而,我提出的解决方案(第二多票)需要更少的操作并且应该更有效。
【解决方案9】:

通过包括:

#include <byteswap.h>

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

__bswap_32 (uint32_t input)

__bswap_16 (uint16_t input)

【讨论】:

  • 感谢您的回答,但我无法使用任何库函数
  • 应阅读#include &lt;byteswap.h&gt;,请参阅 .h 文件本身的注释。这篇文章包含有用的信息,因此尽管作者忽略了不使用 lib 函数的 OP 要求,但我还是投了赞成票。
  • 事实上,__bswap_32/__bswap_16 函数实际上是宏而不是库函数,这是投票的另一个原因。
  • 我的理解是,不保证所有架构上的所有操作系统都存在此标头。我还没有找到一种可移植的方式来处理字节序问题。
  • 在 Windows 上不存在 - 至少在使用 mingw 32 或 64 位从 linux 交叉编译时不存在
【解决方案10】:

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

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

}

【讨论】:

  • 代码依赖于一个非常合理的假设:source 根据需要对齐 - 但如果该假设不成立,则代码为 UB。
【解决方案11】:

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

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

【讨论】:

  • 这些宏没问题,但是当有符号整数在 0x80000000 和 0xffffffff 之间时 ((x) >> 24) 会失败。在这里使用按位与是个好主意。注意: ((x) > 8) 如果高 16 位非零(或提供有符号的 16 位值)也会失败。
  • @PacMan-- 这些宏仅用于交换 无符号 整数。这就是为什么他们的名字中有UINT
  • 是的,真的,抱歉打扰了。嵌入类型转换不是最好的吗?
【解决方案12】:

此代码sn-p 可以将32bit 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);    
}

【讨论】:

  • 谢谢@YuHao 我是新来的,不知道如何格式化文本。
  • 在某些平台上使用 ((i&gt;&gt;24)&amp;0xff) | ((i&gt;&gt;8)&amp;0xff00) | ((i&amp;0xff00)&lt;&lt;8) | (i&lt;&lt;24); 可能会更快(例如,回收 AND 掩码常量)。不过,大多数编译器都会这样做,但一些简单的编译器无法为您优化它。
【解决方案13】:

这是一种使用 SSSE3 指令 pshufb 的方法,假设您有 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;
}

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 2017-12-31
    • 1970-01-01
    • 2013-10-17
    • 1970-01-01
    • 2018-10-19
    • 2019-12-03
    相关资源
    最近更新 更多