假设我有一个字节缓冲区:
char buffer[] = { 0x11, 0x00, 0x00, 0x02, .... };
我想做的是从特定的偏移量获取一个qword。偏移量将以字节为单位进行计算。 这是我所做的:
unsigned long long *ptr = &buffer[16];
unsigned long long myqword = *ptr;
这似乎对我有用。我从头开始跳过16个字节,然后再取8个字节。
我的问题是为什么我会收到此警告消息:
warning: initialization of ‘long long unsigned int *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types]
unsigned long long *ptr = &buffer[16];
答案 0 :(得分:4)
这里有4个主要问题:
unsigned long long*
与char*
不兼容。这是导致编译器错误的原因。char
读取一堆unsigned long long*
会违反strict aliasing rule,从而引发未定义的行为错误。char
读取一堆unsigned long long*
可能还会导致未对齐的访问,从而引发未定义的行为错误。char
存储整数值是一个非常糟糕的主意,因为char
具有实现定义的签名,并且可以在某些计算机上签名。请改用uint8_t
。相反,您应该使用memcpy:
size_t n = sizeof(unsigned long long);
memcpy(&myqword, &buffer[i * n], n);
如果您实际上需要访问特定内存位置的内容而不进行复制,则别无选择,只能创建一些新类型,例如联合:
#include <inttypes.h>
#include <stdio.h>
typedef union
{
uint8_t byte [8];
uint64_t qword;
} qword_t;
int main (void)
{
qword_t q = {.byte = {0x11, 0x00, ...} };
printf("%"PRIu64, q.qword);
}
但是请注意,此代码取决于CPU的耐久性,并且不可移植。
答案 1 :(得分:3)
您可以自己将字节拼凑成一个整数。
如果您使用大端字节序主机,并且您当前的代码确实获得了正确的整数值,请使用以下代码:
/** De-serialize a uint64_t from a byte array in big endian format.
* @param r The byte array to containing the integer in. Must be at least of size 4.
* @param return The deserialized integer from the byte array
*/
static inline uint64_t uc_unpack_64_be(const uint8_t *r)
{
uint64_t v;
v = (uint64_t)r[0] << 56;
v |= (uint64_t)r[1] << 48;
v |= (uint64_t)r[2] << 40;
v |= (uint64_t)r[3] << 32;
v |= (uint32_t)r[4] << 24;
v |= r[5] << 16;
v |= r[6] << 8;
v |= r[7];
return v;
}
如果您当前使用的是小端格式的计算机,请使用以下计算机:
/** De-serialize a uint64_t from a byte array in little endian format.
* @param r The byte array to containing the integer in. Must be at least of size 8.
* @param return The deserialized integer from the byte array
*/
static inline uint64_t uc_unpack_64_le(const uint8_t *r)
{
uint64_t v;
v = r[0];
v |= r[1] << 8;
v |= r[2] << 16;
v |= (uint32_t)r[3] << 24;
v |= (uint64_t)r[4] << 32;
v |= (uint64_t)r[5] << 40;
v |= (uint64_t)r[6] << 48;
v |= (uint64_t)r[7] << 56;
return v;
}
使用它,例如as uint64_t myqword = uc_unpack_64_le(&buffer [16]);
请注意,您使用uint64_t uc_unpack_64_le
还是uint64_t uc_unpack_64_le
函数之一取决于buffer
中数据的格式是小字节序还是大字节序,而不是当前代码是否在小型或大型字节序机。
如果您坚持使用当前的long long和char类型,请相应地更改代码,但我建议您改用<stdint.h>
标头中的uint16_t和uint64_t类型。
答案 2 :(得分:1)
除了违反严格的别名规则(在问题注释中很好地发现)之外,另一个 该警告值得关注的原因是,您可能最终会访问未对齐的数据。
在某些架构上,它只会变慢,在其他架构上,您的应用程序可能无法生存。
如果显式转换它,它将关闭。但是我也要告诉编译器将char数组对齐到unsigned long long
的大小(为了清楚起见,我还将切换到uint8_t
和uint64_t
。