C语言 将大端字节序转换为小端字节序,反之亦然

2exbekwf  于 2023-11-16  发布在  其他
关注(0)|答案(3)|浏览(108)

更新:实现在C中
我正在尝试编写一个endianess转换函数
我的理解是,两种方式的endianess转换使用相同的函数,因为它是字节反转。
我有两个实现。
方法1
在lsb处获取每个字节,并与结果进行OR运算,然后将结果左移

unsigned int convert_endian(unsigned int num)
{
       unsigned int res=0;
       int i = 0;
       while(i < sizeof(int))
       {
            res = res << 8;
            res |= num & 0xFF;
            num = num >> 8;
            i++;
       }
       
       return res;
}

字符串
方法2
使用一个字符指针来获取每个字节,并将其OR到结果中

unsigned int convert_endian_char(unsigned int num)
{
    unsigned int res=0;
    char *ptr = (char*)&num;
    int i=0;
    while(i<sizeof(int))
    {
        res = res << 8;
        res |= *ptr & 0xFF;
        i++;
        ptr++;
    }
    
    return res;
}


这段代码似乎可以工作,例如:对于num = 0x12345678,我得到res = 0x78563412
但是在代码审查期间,我被告知这不起作用,而且在轮班期间,我会在方法1中丢失字节。

fsi0uk1n

fsi0uk1n1#

方法1的候选人问题。
所有的问题都是模糊的。

  • res = res << 8; res |= num & 0xFF;char不是8位的机器上失败。(罕见。)如果试图交换本地定义的“字节”,则与res = res << CHAR_BIT; res |= num & UCHAR_MAX;一样好。
  • 如果unsigned既不是像PDP-11-endian那样的大字节序,也不是小字节序,那么实现交换大/小字节序的代码的正确性是值得怀疑的(非常罕见)。
  • 如果sizeof(char) == sizeof(unsigned),(非常罕见)我们会遇到麻烦,因为不需要循环-取决于目标。
  • 这里不是一个真正的问题,但最好不要混合类型符号:int i = 0; while(i < sizeof(int))--> unsigned /* or size_t */ i = 0; while(i < sizeof(int))
  • 在这里,sizeof(int) == sizeof(unsigned)并不是一个真正的问题,但最好不要在这段代码中的任何地方引入int

没有任何内容与“在移位期间方法1中丢失字节”Assert匹配。
或许:
1.面试官 * 错误地 * 认为res << 8在转换到符号位时会导致UB(未定义的行为)。然而,由于unsigned,没有符号位,也没有UB。

  1. num = num >> 8;破坏了num的原始值。但那又怎样?num不需要保存。
    1.采访者故意用一个没有证据的说法来挑战OP,以评估OP如何处理它。
    1.发布的代码与面试中的代码并不完全相同。令人惊讶的是,即使是一个字符的差异也意味着很多。
    我怀疑是3或4个。
    备选方案:
#include <limits.h>
#include <stddef.h>

unsigned swap_endian_big_little(unsigned num) {
  unsigned res = 0;

  for (size_t i = 0; i < sizeof res; i++) {
    res <<= CHAR_BIT;
    res |= num & UCHAR_MAX;
    num >>= CHAR_BIT;
  }

  return res;
}

字符串
除了@John Bollinger和char *ptr之外,没有评论方法2,因为unsigned char *ptr更好地处理各种 * 有符号 * 编码。
我的理解是,两种方式的endianess转换使用相同的函数,因为它是字节反转。
对于big/little endian为真,但其他endian也是可能的。

  • 通常 * 编码的目标不是从big-to-little或little-to-big交换端序,而是从native-to-big、native-to-little、big-to-native或little-to-native交换端序。
lztngnrs

lztngnrs2#

不需要考虑太多。像这样的东西应该在大/小端之间是可移植的:

#define IS_LITTLE ( *(uint8_t*)&(uint16_t){0xAABB} == 0xBB)

uint32_t flip_endian32 (uint32_t in)
{
  const uint8_t* n = (const uint8_t*)&in;

  return ((uint32_t)n[IS_LITTLE ? 0u : 3u] << 24u) |
         ((uint32_t)n[IS_LITTLE ? 1u : 2u] << 16u) |
         ((uint32_t)n[IS_LITTLE ? 2u : 1u] <<  8u) |
         ((uint32_t)n[IS_LITTLE ? 3u : 0u] <<  0u) ;
}

字符串
gcc 12.3 -O3:

flip_endian32:
    mov     eax, edi
    bswap   eax
    ret


应该有点符合MISRA,适合小型微控制器等。

gjmwrych

gjmwrych3#

似乎,在普通的内存中,一个内存地址可以存储1个字节的内容.
所以要转换endian(在big和little之间),你不需要做任何按位的操作,只要把你的原始类型处理成byte数组,然后反转它,就可以了。

#include <iostream>
#include <array>
#include <algorithm>
#include <inttypes.h>

template<typename T>
union MemAlias{
    T buff;
    std::array<uint8_t, sizeof(T)> mem;
}; //union is good for aliasing

template<typename T>
T convert_endian(T num)
{
    MemAlias<T> in{num}; // member order matter here, it will construct to first member
   
    MemAlias<T> out;

    std::copy(in.mem.rbegin(), in.mem.rend(), out.mem.begin());// just reverse

    return out.buff;
}

template<typename T>
void print_mem(T num){
    MemAlias<T> m{num};
    for(auto a:m.mem){
        std::cout << std::hex 
                  << static_cast<int>(a) // cast to int because cout try to print uint8_t as character
                  << " ";
    }
    std::cout << '\n';
}

int main(){
   unsigned int i = 0xaabbccdd;
   print_mem(i); //mostly little endian
   auto j = convert_endian(i);
   print_mem(j); //mostly big endian
   /*
   result:
   dd cc bb aa 
   aa bb cc dd 
   */
}

字符串

相关问题