我正在尝试将设备输入(始终为介于1和600000之间的整数)转换为四个8位整数。
例如,
如果输入为32700,则希望得到188 127 00 00
。
我通过使用以下方法实现了这一点:
32700 % 256
32700 / 256
以上代码可正常工作直到32700。从32800开始,我收到了错误的转换结果。
我是完全新手,希望得到一些帮助,了解如何正确地完成此操作。
根据澄清后的说明,进行了重大编辑:
鉴于已经有人提到了移位和掩码方法(无可否认地是正确的方法),我将给出另一种方法,严谨地说,它不具有可移植性、机器相关性,并且可能表现出未定义的行为。但在我看来它仍是一个很好的学习练习。
由于各种原因,您的计算机将整数表示为8位值的组(称为字节);请注意,尽管极其常见,但这并不总是这种情况(参见CHAR_BIT
)。因此,使用超过8位的值使用多个字节(因此使用8的倍数的位数)。对于32位值,您使用4个字节,在内存中,这些字节始终紧随其后。
我们将一个包含另一个值内存地址的值称为指针。在这种情况下,一个字节被定义为可以由指针引用的最小值(就比特数而言)。例如,覆盖4个字节的32位值将具有4个“可寻址”的单元格(每个字节一个),其地址被定义为这些地址中的第一个:
|==================|
| MEMORY | ADDRESS |
|========|=========|
| ... | x-1 | <== Pointer to byte before
|--------|---------|
| BYTE 0 | x | <== Pointer to first byte (also pointer to 32-bit value)
|--------|---------|
| BYTE 1 | x+1 | <== Pointer to second byte
|--------|---------|
| BYTE 2 | x+2 | <== Pointer to third byte
|--------|---------|
| BYTE 3 | x+3 | <== Pointer to fourth byte
|--------|---------|
| ... | x+4 | <== Pointer to byte after
|===================
int32_t value;
其地址由&value
给出。同时,一个由4个8位字节组成的数组可以表示为:
uint8_t arr[4];
uint8_t
数组的指针被定义为:uint8_t (*ptr)[4];
如果我将我们32位值的地址分配给这样一个数组,我就能够逐个索引每个字节,这意味着我将直接读取每个字节,避免任何繁琐的移位和掩码操作!
uint8_t (*bytes)[4] = (void *) &value;
我需要强制转换指针 ("(void *)
"),因为&value
的类型是"指向int32_t
的指针",而我正在将其分配给"指向4个uint8_t
数组的指针",这种类型不匹配被编译器捕获并受到标准的严格警告; 这是第一个警告,说明我们正在做的事情并不理想!
最后,我们可以通过索引直接从内存中读取来访问每个字节:(*bytes)[n]
读取value
的第n
个字节!
将所有内容结合起来,假设有一个send_can(uint8_t)
函数:
for (size_t i = 0; i < sizeof(*bytes); i++)
send_can((*bytes)[i]);
同时为了测试目的,我们定义:
void send_can(uint8_t b)
{
printf("%hhu\n", b);
}
当 value
为 32700
时,在我的机器上会打印出以下内容:
188
127
0
0
unsigned long x = 600000UL; // you need UL to indicate it is unsigned long
unsigned int b1 = (unsigned int)(x & 0xff);
unsigned int b2 = (unsigned int)(x >> 8) & 0xff;
unsigned int b3 = (unsigned int)(x >> 16) & 0xff;
unsigned int b4 = (unsigned int)(x >> 24);
使用移位运算比乘法、除法或模运算更快。这取决于你想要实现的字节序。你可以使用 b1 的公式来反转 b4 等的赋值。
我在打包和解包大量自定义数据包以进行传输/接收时遇到了类似的情况,我建议您尝试以下方法:
typedef union
{
uint32_t u4_input;
uint8_t u1_byte_arr[4];
}UN_COMMON_32BIT_TO_4X8BIT_CONVERTER;
UN_COMMON_32BIT_TO_4X8BIT_CONVERTER un_t_mode_reg;
un_t_mode_reg.u4_input = input;/*your 32 bit input*/
// 1st byte = un_t_mode_reg.u1_byte_arr[0];
// 2nd byte = un_t_mode_reg.u1_byte_arr[1];
// 3rd byte = un_t_mode_reg.u1_byte_arr[2];
// 4th byte = un_t_mode_reg.u1_byte_arr[3];
您可以使用一些位掩码。
600000 是 0x927C0
600000 / (256 * 256)
可以得到 9,还没有进行掩码操作。
((600000 / 256) & (255 * 256)) >> 8
可以得到 0x27 == 39。使用一个由 8 个设置为 1 的位组成的 8 位移位掩码 (256 * 255)
并右移 8 位,即 >> 8
,也可以使用另一个 / 256
。
600000 % 256
可以得到 0xC0 == 192,就像您所做的那样。掩码操作将是 600000 & 255
。
unsigned char bytes[4];
unsigned long n;
n = (unsigned long) sensore1 * 100;
bytes[0] = n & 0xFF;
bytes[1] = (n >> 8) & 0xFF;
bytes[2] = (n >> 16) & 0xFF;
bytes[3] = (n >> 24) & 0xFF;
CAN_WRITE(0x7FD,8,01,sizeof(n),bytes[0],bytes[1],bytes[2],bytes[3],07,255);
uint32_t
替代 unsigned long
。 - Antti Haapala -- Слава Україні16位有符号整数可以存储的最大正值为32767。如果你强制使用比这更大的数字,你将得到一个负数作为结果,因此%
和/
返回的值是意外的。
使用无符号16位整数可以获得范围高达65535,或者使用32位整数类型。
%
是打错了吗?我期望MOD 256
可以得到 188,而/256
可以得到 127。令人困惑的是,%
是 C 运算符取模的意思,你肯定是指MOD
。也就是说,在我看来,你展示的两行代码在语义上是相同的。请展示一下你得到的两行代码的结果。哪一个可以得到所需的 188,哪一个可以得到 127? - Yunnosch32700
本身就是一个 32 位有符号整数。输入是 32700,我想要 188 127 00 00
,即将有符号的 32 位整数转换为四个 8 位整数。我通过使用以下方法实现了这一点:
我不明白,那只是两个数字。我开始得到错误的转换结果
如果您在处理一些实际代码时遇到问题,请展示代码,而不是解释。请发布一个 [MCVE]。代码胜过千言万语。您的描述可能与您面临的问题完全无关。