如何正确将无符号字符数组转换为 uint32_t?

13

我想将一个unsigned char数组转换为uint32_t,但每次都得到不同的结果:

unsigned char buffer[] = {0x80, 0x00, 0x00, 0x00};;
uint32_t num = (uint32_t*)&buffer;

现在,我一直收到这个警告:

警告:初始化使指针成为整数而不需要强制转换

当我将num更改为*num时,我不再收到该警告,但实际上这并不是真正的问题(更新:嗯,现在我想起来了,它们可能是相关的),因为每次运行代码都会产生不同的结果。其次,一旦正确转换num,应该是128,但如果我需要更改缓冲区的字节顺序,我可以自己处理,我认为

谢谢!


你正在将num设置为缓冲区数组的地址。你应该使用uint32_t num = *(uint32_t*)buffer;来获取数字。 - Neil
6个回答

23

你试过这个了吗?

num = (uint32_t)buffer[0] << 24 |
      (uint32_t)buffer[1] << 16 |
      (uint32_t)buffer[2] << 8  |
      (uint32_t)buffer[3];

这样你可以控制字节序等内容。

char 指针强制转换为更大的类型并解释是不安全的。有些机器希望指向整数的指针对齐。


+1, 这是一种明智且可移植的做法。当然,前提是buffer以大端格式保存了uint32_t,但楼主没有具体说明。 - Fred Foo
如果您将一个8位的buffer[i]强制转换为uint32_t,这不会引起问题吗? - Foo Bah
1
@Foo Bah:不,将“unsigned char”转换为“uint32_t”没有任何问题 - 后者类型必须至少与前者一样大,因此值将保持不变。 - caf

10

cnicutar的回答是最好的,假设您想要特定的固定字节序。如果您想要主机字节序,请尝试:

uint32_t num;
memcpy(&num, buffer, 4);

或者应用 ntohl 到 cnicutar 的回答上。任何基于类型游戏的方法都是错误和危险的。


非常感谢,这正是我正在寻找并尝试使用转换运算符实现的! - MaxC

3

首先,你需要写成 num = *(uint32_t *)&buffer

如果要改变字节序,你可以使用类似于在Linux中调用的bswap_32(在byteswap.h中)或在OSX中调用的OSSwapInt64(在libkern / OSByteOrder.h中)。


1
由于对齐问题,这会导致未定义的行为。在一些RISC处理器上,这可能会使程序崩溃。 - Fred Foo
1
@larsmans 如果没有明确强制单字符打包方案,静态数组不会被正确对齐吗? - Foo Bah
2
这并不是C标准所保证的。事实上,我认为你的例子也违反了别名规则,这意味着即使缓冲区正确对齐,由于优化的原因它也可能会出现错误。 - Fred Foo
1
这是传统的方法,但它不是有效的C语言。 - R.. GitHub STOP HELPING ICE

1
警告是因为&buffer返回了一个指向指针的指针。即使没有引用运算符&,警告也不会消失,因为强制转换只改变了指针类型。指针进一步转换为整数,因此出现了警告。
如果字节序不重要,我认为显而易见的解决方案是
unsigned char buffer[] = {0x80, 0x00, 0x00, 0x00};
uint32_t num = *(uint32_t *)buffer;

这意味着取消引用强制转换为 char 数组的指针。

这是双重错误。首先,给定unsigned char buffer []uint32_t num = *(uint32_t *)buffer;违反了严格别名规则,因此是未定义的行为。它还可能违反6.3.2.3指针,第7段的任何对齐限制:“对象类型的指针可以转换为指向不同对象类型的指针。如果所得到的指针没有正确对齐所引用的类型,则其行为是未定义的。” - Andrew Henle
@AndrewHenle,请您能进一步详细解释为什么这违反了严格别名规则吗?我很难理解别名问题。uint8_tuint32_t不是兼容的类型吗? - earthling
@earthling 在C或C++中,严格别名规则(如果不是完全相同的话)很简单 - 你不能将一个内存位置引用为与其实际类型不同的类型 - 除非你可以将任何东西引用为一个[[un]signed] char数组。通过将一个unsigned char数组引用为一个uin32_t,违反了这个规则,因为数组不是uint32_t,所以会产生未定义行为(UB)。请参见什么是严格别名规则?以了解更多信息。 - Andrew Henle
使用memcpy()是一种便携解决方案,例如memcpy(&num, buffer, sizeof(num));。任何合理的优化编译器都会优化掉memcpy()并替换为适当的直接赋值,这样不会破坏代码。编译器有权这样做,但如果你的代码这样做,就会导致未定义行为。尤其在高级优化级别下,编译器可能会将你的代码编译成不符合你要求的二进制代码。你可以将它看作是编译器先对你的代码进行优化,然后用赋值替换memcpy() - Andrew Henle
@AndrewHenle 非常感谢你写下这个答案!非常感激。它帮助我更好地理解了。祝好! - earthling

0
借鉴@Mr. R.的方法,我将在结构体中将3字节大端无符号字符数组转换为小端无符号整数...
struct mystruct {
  int stuff;
  int stuff2;
  unsigned char x[3]    // big endian
} 
mystruct z;
unsigned int y  // little endian

memcpy(&y, z->x, 3);
y=be32toh(y<<8);`

0

假设它是相同的字节序,联合体将是最佳选择。

union
{
    uint32_t u32;
    float flt;  
    uin8_T bytes[4];

} converter;

// 使用上述联合体
converter.bytes = your_byte_array;
uint32_t u32_output = converter.u32;
float float_output = converter.flt;

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