C指针转换

时间:2018-09-18 08:33:13

标签: c pointers

假设我有一个字节缓冲区:

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];

3 个答案:

答案 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_tuint64_t