如何重新排列整数的字节?

5
我的任务是使用C语言将一个数据文件从big endian转换为little endian,反之亦然。我已经在网上寻找了大约3个小时的其他示例并阅读了我的教科书,但我对如何开始这个函数感到非常困惑。
到目前为止,我已经正确排列了事件顺序(1到4),但在我的convert_and_save函数中,我是否需要使用char数组来创建→ char buffer [4];?
有人能帮帮我吗?即使你只是给我提示要查找什么,我也会非常感激。
我需要编写一个名为:
void convert_and_save(struct record item, FILE * output_handle, int number);

在这个函数内,我按照以下一系列步骤进行操作:
  1. Convert the integer into an array of chars using:

    int integer_to_characters(int number, char * buffer)
    {
       memcpy(buffer, &number, 4);
    }
    
  2. Reverse the order of the chars in that array.

  3. Convert the array of chars back to an integer using:

    int characters_to_integer(char * buffer)
    {
       int result;
       memcpy(&result, buffer, 4);
       return result;
    }
    
  4. write the converted record on the output file using:

    void save_record(FILE * file_handle, struct record a)
    {
       char output_buffer[size_of_record];
       integer_to_characters(a.age, &(output_buffer[0]));
       memcpy(&(output_buffer[4]), a.name, 12);
       integer_to_characters(a.department, &(output_buffer[16]));
       fwrite(output_buffer, sizeof(char), size_of_record, file_handle);
    }
    

5
你找到了htonl()吗? - Greg Hewgill
1
请记住,在主机字节顺序不同于网络字节顺序的平台上,htonl()是一个很好的示例,但在big endian平台上,htonl()是一个无操作/空宏#define htonl(x) x,所以请确保您查看英特尔小端实现(典型的Linux)。 - codenheim
1
是“逆序”还是“大端到小端转换”?它们并不是同一件事。 - FacundoGFlores
1
i = ((i & 0xFF) << 24) | ((i & 0xFF00) << 8) | ((i & 0xFF0000) >> 8) | ((i & 0xFF000000) >> 24); - Neil Kirk
5个回答

7

以下不是标准函数(这些是gcc扩展),但您可能能够使用它们:

— 内置函数:uint16_t __builtin_bswap16 (uint16_t x) 返回字节顺序翻转后的x;例如,0xaabb变成了0xbbaa。这里的字节始终表示精确的8位。

— 内置函数:uint32_t __builtin_bswap32 (uint32_t x) 类似于__builtin_bswap16,但参数和返回类型为32位。

— 内置函数:uint64_t __builtin_bswap64 (uint64_t x) 类似于__builtin_bswap32,但参数和返回类型为64位。

如果您能使用这些函数,那么很有可能会在您的平台上产生更优化的代码,否则,请提交一个补丁到gcc:)

Clang也有__builtin_bswap16() __builtin_bswap32() __builtin_bswap64()

Visual Studio

unsigned short _byteswap_ushort (
   unsigned short val
);
unsigned long _byteswap_ulong (
   unsigned long val
);
unsigned __int64 _byteswap_uint64 (
   unsigned __int64 val
);

ICC拥有_bswap16、_bswap和_bswap64 *需要进一步参考。


4
这是我为Parrot虚拟机编写的一个函数,您可以从parrotcode.org下载byteorder.c。可能有更短的方法来完成此操作,但这个方法适用于不同大小的整数,并且我们有一个宏来检测平台的字节顺序,即PARROT_BIGENDIAN,您可以放弃所有这些。另外,如上所述,您可以搜索htonl(),在bigendian硬件上是nop,在little endian上进行转换(只需要获取Linux x86实现)。
INTVAL
fetch_iv_le(INTVAL w)
{
    ASSERT_ARGS(fetch_iv_le)
#if !PARROT_BIGENDIAN
    return w; // No-op on little endian hardware
#else
#  if INTVAL_SIZE == 4
    return (w << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | (w >> 24);
#  else
#    if INTVAL_SIZE == 8
    INTVAL r;

    r = w << 56;
    r |= (w & 0xff00) << 40;
    r |= (w & 0xff0000) << 24;
    r |= (w & 0xff000000) << 8;
    r |= (w & 0xff00000000) >> 8;
    r |= (w & 0xff0000000000) >> 24;
    r |= (w & 0xff000000000000) >> 40;
    r |= (w & 0xff00000000000000) >> 56;
    return r;
#    endif
#  endif
#endif
}

2

请看这个答案:https://dev59.com/0HVD5IYBdhLWcg3wDXF3#105339

(该链接为英文网站,需要点击进入查看)

If you're using Visual C++ do the following: You include intrin.h and call the following functions:

For 16 bit numbers:

unsigned short _byteswap_ushort(unsigned short value);

For 32 bit numbers:

unsigned long _byteswap_ulong(unsigned long value);

For 64 bit numbers:

unsigned __int64 _byteswap_uint64(unsigned __int64 value);

8 bit numbers (chars) don't need to be converted.

Also these are only defined for unsigned values they work for signed integers as well.

For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order. You can get little-endian floats on big-endian machines and vice versa.

Other compilers have similar intrinsics as well.

In GCC for example you can directly call:

   uint16_t __builtin_bswap16 (uint16_t x)
   uint32_t __builtin_bswap32 (uint32_t x)
   uint64_t __builtin_bswap64 (uint64_t x)

1
这是我的示例。它可以反转整数的字节序。请注意,C语言没有指定int类型的大小。它可能是16位、32位、64位...如果要确保大小,可以使用(uint_16t、uint_32t、uint_64t)。
void reverse_endianess(int* number)
{
    char byte_arr[8] = {0};
    int i;

    for (i=0; i < sizeof(int); i++) {
        byte_arr[i] = (*number & 0xFF);
        *number = *number >> 8;
    }

    *number = 0;

    for (i=0; i < sizeof(int); i++) {
        *number |=  byte_arr[i];
        *number = *number << 8;
    }
}

-1

您可以看一下这个答案,它展示了如何进行双向转换,还有不同字节大小的转换。这是一种方法,但讨论串链中还有更高效的方法。

uint32_t num = 9;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;

b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;

res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;

printf("%d\n", res);

1
你复制到这个答案中的代码是不正确的。我建议你阅读你提供的问题,包括所有评论。OP发布了这段代码,但它不正确,并在评论中指出了几个缺陷。当然,如果你想倒序排列,你不能将所有字节(或半字节)向一个方向移动。位移不等于旋转。阅读@mrjoltcola的答案,你会发现需要在两个方向上移动字节才能完成任务。 - Robert Crovella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接