我有一个函数,它从 4 个 uint8_t
值构造一个 int32_t
并使用以下测试来确信我的结果是预期的,因为我依赖于(我相信的)实现定义的行为。
我在做什么有意义吗? 有没有更好的方法来构建 int32_t?
int32_t expected = -1;
int32_t res = 0;
uint8_t b0 = 0xFF;
uint8_t b1 = 0xFF;
uint8_t b2 = 0xFF;
uint8_t b3 = 0xFF;
res |= b0;
res |= b1 << 8;
res |= b2 << 16;
/* This is IDB, this value cannot be represented in int32_t */
res |= ((uint32_t) b3) << 24;
ck_assert(res == expected);
最佳答案
这几乎是最好的方法,除了您应该在每一行都强制转换为 uint32_t
,以避免隐式转换和签名问题。
这里的主要问题是对有符号操作数执行按位运算往往会调用定义不明确的行为。尤其要注意这一点:
- 将一位左移到有符号数的符号位会调用未定义的行为。这包括移动比类型可以容纳的更多的位。
- 左移具有负值的有符号变量会调用未定义的行为。
- 右移具有负值的有符号变量会调用实现定义的行为(您最终会得到算术或逻辑移位)。
通过确保始终使用无符号类型(如 uint32_t
)进行转换,这些问题都可以避免。例如:
res |= (int32_t) ((uint32_t)b1 << 8;)
上面的代码是坚固耐用的好实践,因为它不包含任何隐式促销。
- 在这种情况下无需考虑字节顺序,因为您使用位移位。如果您使用任何其他方法(类型双关、指针运算等),它就会成为一个问题。
- 在这种情况下,签名格式无关紧要。
stdint.h
类型保证使用 2 的补码。他们绝不会使用 1 的补码或符号和大小。
关于c - 从 4 个 uint8_t 值构建 int32_t,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46199507/