将 unsigned char 移动超过 8 位
shifting an unsigned char by more than 8 bits
这段代码让我有点困扰:
typedef struct _slink{
struct _slink* next;
char type;
void* data;
}
假设这描述的是文件中的 link,其中数据为 4 字节长,表示地址或整数(取决于 link 的类型)
现在我正在考虑将文件中的数字从小端重新格式化为大端,所以我想做的是在写回文件之前更改字节的顺序,即
对于 0x01020304
,我想将它转换为 0x04030201
,所以当我写回它时,它的小端表示将看起来像 0x01020304
的大端表示,我通过乘以 i'th
byte by 2^8*(3-i)
,其中 i
介于 0 和 3 之间。现在这是实现它的一种方式,这里让我感到困扰的是,这是将字节移动超过 8 位。 . (L 是类型 _slink*)
int data = ((unsigned char*)&L->data)[0]<<24) + ((unsigned char*)&L->data)[1]<<16) +
((unsigned char*)&L->data)[2]<<8) + ((unsigned char*)&L->data)[3]<<0)
任何人都可以解释为什么这真的有效吗?没有明确地将这些字节转换为整数开始(因为它们只有 1 个字节,但最多移动 24 位)
提前致谢。
任何小于 int
的整数类型在表达式中使用时 提升 为类型 int
。
所以移位实际上应用于类型 int
而不是类型 char
的表达式。
Can anyone please explain why this actually works?
转变不是作为 unsigned char
发生,而是作为提升为 int
1 的类型发生。 .
代码仍然存在问题的原因。
32 位 int
将 int
1 移到符号位置是 未定义的行为 UB。另见 .
((unsigned char*)&L->data)[0]<<24) // UB
16 位 int
即使类型为 unsigned
,移位或更多位宽度也不够精确。 int
就是上面的UB。也许那时 OP 只想要一个 2 字节的字节序交换?
备选
const uint8_t *p = &L->data;
uint32_t data = (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | //
(uint32_t)p[2] << 8 | (uint32_t)p[3] << 0;
为迂腐
如果 int
使用非 2 的补码,从 ((unsigned char*)&L->data)[0]<<24)
添加负值会弄乱数据模式。 Endian 操作最好使用 unsigned 类型来完成。
from little-endian to big-endian
此代码不会在这 2 个字节序之间交换。它是本机字节序交换的大字节序。当此代码在 32 位 unsigned
小端计算机上为 运行 时,它实际上是 big/little 交换。在 32 位 unsigned
大端机器上,它可能是一个空操作。
1 ... 或者 select 平台上的 unsigned
UCHAR_MAX > INT_MAX
.
这段代码让我有点困扰:
typedef struct _slink{
struct _slink* next;
char type;
void* data;
}
假设这描述的是文件中的 link,其中数据为 4 字节长,表示地址或整数(取决于 link 的类型)
现在我正在考虑将文件中的数字从小端重新格式化为大端,所以我想做的是在写回文件之前更改字节的顺序,即
对于 0x01020304
,我想将它转换为 0x04030201
,所以当我写回它时,它的小端表示将看起来像 0x01020304
的大端表示,我通过乘以 i'th
byte by 2^8*(3-i)
,其中 i
介于 0 和 3 之间。现在这是实现它的一种方式,这里让我感到困扰的是,这是将字节移动超过 8 位。 . (L 是类型 _slink*)
int data = ((unsigned char*)&L->data)[0]<<24) + ((unsigned char*)&L->data)[1]<<16) +
((unsigned char*)&L->data)[2]<<8) + ((unsigned char*)&L->data)[3]<<0)
任何人都可以解释为什么这真的有效吗?没有明确地将这些字节转换为整数开始(因为它们只有 1 个字节,但最多移动 24 位) 提前致谢。
任何小于 int
的整数类型在表达式中使用时 提升 为类型 int
。
所以移位实际上应用于类型 int
而不是类型 char
的表达式。
Can anyone please explain why this actually works?
转变不是作为 unsigned char
发生,而是作为提升为 int
1 的类型发生。
代码仍然存在问题的原因。
32 位 int
将 int
1 移到符号位置是 未定义的行为 UB。另见
((unsigned char*)&L->data)[0]<<24) // UB
16 位 int
即使类型为 unsigned
,移位或更多位宽度也不够精确。 int
就是上面的UB。也许那时 OP 只想要一个 2 字节的字节序交换?
备选
const uint8_t *p = &L->data;
uint32_t data = (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | //
(uint32_t)p[2] << 8 | (uint32_t)p[3] << 0;
为迂腐
如果 int
使用非 2 的补码,从 ((unsigned char*)&L->data)[0]<<24)
添加负值会弄乱数据模式。 Endian 操作最好使用 unsigned 类型来完成。
from little-endian to big-endian
此代码不会在这 2 个字节序之间交换。它是本机字节序交换的大字节序。当此代码在 32 位 unsigned
小端计算机上为 运行 时,它实际上是 big/little 交换。在 32 位 unsigned
大端机器上,它可能是一个空操作。
1 ... 或者 select 平台上的 unsigned
UCHAR_MAX > INT_MAX
.