我正在编写这个从远程服务器接收包的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);
嗯,一般来说,这应该不行,但在一种情况下,它可能会起作用,这取决于从发送方传递的实际参数的类型。
换句话说,如果发送方正在发送指向uint64_t
类型的指针,则仅在这种情况下,代码是可接受的。
引用C11
,章节§6.3.2.3
指向对象类型的指针可以转换为指向不同对象类型的指针。如果对于引用的类型,结果指针未正确对齐(68),则行为未定义。否则,当再次转换回来时,结果将等于原始指针。
一般情况下,如果指针是从该类型获得然后转换为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);
我正在编写这个从远程服务器接收包的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
}
我的问题是从const char *到uint64_t * part的转换:是否可以安全地转换指针,然后将内容读取为uint64_t?
一般来说,它是不安全的,是undefined behavior,所以你应该是scared。例如,您的代码可能会在PowerPC机器上崩溃,其中指向uint64_t
的指针应该对齐到8个字节(否则,在取消引用未对齐的指针时可能会出现SIGBUS
或SIGSEGV
)。
如果buf没有与8个字节对齐怎么办?
在x86处理器上,您可以使用它们:允许取消引用未对齐的指针,但效率低(因此您应该避免这样做)。
动态分配的指针(由malloc
或::operator new
获得)保证足够对齐,因此new char[x]
的结果可以安全地转换为uint64_t*