【问题标题】:Integer to byte array Little Endian and vice versa整数到字节数组 Little Endian,反之亦然
【发布时间】:2014-03-24 23:03:59
【问题描述】:

我想要将整数转换为字节数组(小端序)的方法, 以及从字节数组(以小端方式编码)到 C 中的整数; 无论我们是在 LE 还是 BE 机器上工作。

这些功能适合吗?

void Int2ByteArrayLE(int x,unsigned char *byteArray)
{

  byteArray[0]=x;
  byteArray[1]=x>>8;
  byteArray[2]=x>>16;
  byteArray[3]=x>>24;
}


void ByteArrayLE2Int(unsigned char *byteArray, int*x)
{
  *x=byteArray[0] | (byteArray[1]<<8) | (byteArray[2]<<16) | (byteArray[3]<<24);
}

附言。如果 x 已签名,这是否也有效?

【问题讨论】:

  • @harold:据我所知,当您将整数转换为 unsigned char 时,最重要的位会被丢弃(当 int 大于 unsigned char 时)-这是您的意思吗?
  • 你能解释一下你为什么这样做吗?我怀疑有更好的解决方案。
  • @DavidHeffernan:我认为这很常见,不是吗?特别是如果您想通过网络发送整数,而服务器需要小端格式的整数,例如
  • 所以在这种情况下你做错了!不要试图从小端转换为大端。从主机字节顺序转换为网络字节顺序。使用套接字库中众所周知的函数。
  • @DavidHeffernan 我相信他在考虑负面价值观。

标签: c bit-manipulation


【解决方案1】:

您实际上要做的是在主机字节顺序和网络字节顺序之间进行转换。您需要的是函数htonlntohl

要从主机转换为您使用的网络字节顺序:

uint32_t n = (uint32_t)htonl((uint32_t)h);

反方向:

int h = (int)ntohl((uint32_t)n);

这些函数知道主机平台的字节顺序。所以如果你在一个大端机器上执行代码,这些函数什么都不做。但是在小端机器上,函数会反转字节。

在 cmets 中,您暗示您的通信协议要求信息以 little endian 字节顺序传递。这肯定是一个错误。您应该遵循标准协议并以网络字节顺序在线传输数据。

如果您真的无法更改协议,并且希望通过网络传输小端序,那么您只需要一个memcpy。您的值已经是小端序了,显然反转字节无济于事。

如果您发现自己使用的是大端客户端,那么您现在当然需要反转字节。为此使用此功能:

uint32_t reverseBytes32(uint32_t v){
  return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) 
         | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
}

您需要使用强制转换将已签名的int 重新解释为未签名的uint32_t

【讨论】:

  • David 不,服务器期望字节数组(表示 int)以小端序排列
  • 好的,如果我的机器也是小端,我想通过套接字发送负值,我只是将整数到字节数组的memcpy 可以吗? (给定服务器需要小端整数)
  • 我认为您需要修复您的通信协议并开始使用网络字节顺序。
  • 我无法控制服务器
  • 难以置信有人设计了客户端/服务器系统并使用了小端字节序。为那个人感到羞耻!
【解决方案2】:

您的代码有一些问题:

  • 在具有 16 位 int 的系统上,该代码将不起作用。
  • 在 99% 的情况下,对有符号整数使用按位运算符没有任何意义。如果有符号整数包含负数,如果左移,您甚至会遇到未定义的行为。

所以代码通常是不可移植的。您需要将代码更改为以下内容:

#include <stdint.h>

void uint32_to_ByteArrayLE (uint32_t x, uint8_t* byteArray)
{
  // explicit casts will prevent implicit conversion warnings:
  byteArray[0] = (uint8_t)(x >>  0);
  byteArray[1] = (uint8_t)(x >>  8);
  byteArray[2] = (uint8_t)(x >> 16);
  byteArray[3] = (uint8_t)(x >> 24);
}


void ByteArrayLE_to_uint32 (const uint8_t* byteArray, uint32_t* x)
{
  /* casts -before- shifting are necessary to prevent integer promotion 
     and to make the code portable no matter integer size: */

  *x = (uint32_t)byteArray[0] <<  0 | 
       (uint32_t)byteArray[1] <<  8 | 
       (uint32_t)byteArray[2] << 16 | 
       (uint32_t)byteArray[3] << 24;
}

如果不是第二个函数中的强制转换,则转换将发生在 int 上,这不是您想要的。

【讨论】:

  • 谢谢,但是人们如何为负数编写类似的函数呢?例如,当 x 为负数时?
  • @dmcr_code 以同样的方式先转换为 uint32_t,然后再转换回有符号数。
  • 所以你的意思是我将x 转换为uint32_t 之前 我将它提供给uint32_to_ByteArrayLE 函数?然后我也将xByteArrayLE_to_uint32返回到int
【解决方案3】:

让我们看一下标准(5.8 Shift 运算符):

"E1

“E1 的值 >> E2 s E1 右移 E2 位位置。...如果 E1 具有带符号类型和负值,则结果值是实现定义的。”

因此,当 x 为负数时,> 的行为是实现定义的。一般来说,对于有符号的 x,只要 x 是非负的,代码就可以工作。

【讨论】:

  • 在前一种情况下,只有不适合目标类型的值的行为才会未定义。
【解决方案4】:

是的。它也适用于有符号整数,因为此代码独立于 MSB 通过 >> shift 的符号位扩展。即,无论是否实现了符号位,它都不会影响代码,因为它只涉及按原样处理 X 的 32 位。只需存储和检索 32 位。因此将不考虑在 MSB 处由 >> 添加的位。要了解考虑,
如果 x 是 int x; //即那么有符号整数

x=-1 即 11111111 11111111 11111111 11111111

字节数组[0]=x; 11111111 11111111 11111111 11111111
字节数组[1]=x>>8; xxxxxxxx 11111111 11111111 11111111
字节数组[2]=x>>16; xxxxxxxx xxxxxxxx 11111111 11111111
字节数组[3]=x>>24; xxxxxxxx xxxxxxxx xxxxxxxx 11111111

由 >> xxxxxxxx xxxxxxxx xxxxxxxx 添加的位不会被存储/使用,因此它们不会影响代码。
因此,移位运算符的符号位扩展为done or not 不会影响代码,因为从上面的示例中可以清楚地看出,移位x 插入的位不用于存储。因此代码可以成功地将数字存储在 binartarray 中,并且可以返回到int x

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-09
    • 2014-03-05
    • 2013-02-14
    • 2010-11-10
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 2011-05-16
    相关资源
    最近更新 更多