将二进制字节(char *)转换为uint64_t *,是否安全?

问题描述 投票:0回答:4

我正在编写这个从远程服务器接收包的C ++代码。由于包的大小事先是未知的,因此代码首先接收8个字节,它告诉整个包的大小。然后它分配一个足够大的缓冲区并将整个包接收到缓冲区中。

我的问题是从const char*uint64_t*部分的转换:是否可以安全地投射指针,然后将内容读作uint64_t?如果buf没有与8个字节对齐怎么办?

const char* buf;
RecvBytes(buf, sizeof(uint64_t));    // the first 8 bytes should tell us the size of the whole package
uint64_t pkg_size = *(uint64_t*)buf;  // is this safe??
const char* pkg = new char[pkg_size];
RecvBytes(pkg, pkg_size);
c++ casting memory-alignment
4个回答
2
投票

嗯,一般来说,这应该不行,但在一种情况下,它可能会起作用,这取决于从发送方传递的实际参数的类型。

换句话说,如果发送方正在发送指向uint64_t类型的指针,则仅在这种情况下,代码是可接受的。

引用C11,章节§6.3.2.3

指向对象类型的指针可以转换为指向不同对象类型的指针。如果对于引用的类型,结果指针未正确对齐(68),则行为未定义。否则,当再次转换回来时,结果将等于原始指针。


0
投票

一般情况下,如果指针是从该类型获得然后转换为const,那么将(char *char *转换为类型是安全的。

所以这里的防弹方式是:

uint64_t pkg_size;
RecvBytes((const char *) &pkg_size, sizeof(uint64_t));
const char* pkg = new char[pkg_size];
RecvBytes(pkg, pkg_size);

0
投票

我正在编写这个从远程服务器接收包的C ++代码。由于包的大小事先是未知的,因此代码首先接收8个字节,它告诉整个包的大小。然后它分配一个足够大的缓冲区并将整个包接收到缓冲区中。

我认为在包交换的上下文中,你不应该动态地分配一些东西,或者至少不是没有最大大小。 (并在必要时循环计算所有数据)

在你的情况下我会做这样的事情:

union {
  uint64_t size;
  char bytes[sizeof size];
} tmp;
RecvBytes(tmp.bytes, sizeof tmp.bytes);
if (tmp.size > SIZE_MAX) {
  // bad
}
size_t size = static_cast<size_t>(tmp.size); // or use tmp.size

char data[MAX]; // or use something more object
for (size_t i = 0; i < size; i += MAX) {
  RecvBytes(data, min(size - i, MAX));
  // compute data
}

0
投票

我的问题是从const char *到uint64_t * part的转换:是否可以安全地转换指针,然后将内容读取为uint64_t?

一般来说,它是不安全的,是undefined behavior,所以你应该是scared。例如,您的代码可能会在PowerPC机器上崩溃,其中指向uint64_t的指针应该对齐到8个字节(否则,在取消引用未对齐的指针时可能会出现SIGBUSSIGSEGV)。

如果buf没有与8个字节对齐怎么办?

x86处理器上,您可以使用它们:允许取消引用未对齐的指针,但效率低(因此您应该避免这样做)。

动态分配的指针(由malloc::operator new获得)保证足够对齐,因此new char[x]的结果可以安全地转换为uint64_t*

© www.soinside.com 2019 - 2024. All rights reserved.