通过浮点指针操作字节向量

Sta*_*zer 2 c++ c++11

是否可以std::vector<unsigned char>通过其数据指针操作 an ,就好像它是 的容器一样float

这是一个编译和(似乎?)按需要运行的示例(GCC 4.8,C++11):

#include <iostream>
#include <vector>

int main()
{
    std::vector<unsigned char> bytes(2 * sizeof(float));
    auto ptr = reinterpret_cast<float *>(bytes.data());
    ptr[0] = 1.1;
    ptr[1] = 1.2;
    std::cout << ptr[0] << ", " << ptr[1] << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

此代码段成功地从字节缓冲区写入/读取数据,就好像它是一个float. 从阅读有关 reinterpret_cast我担心这可能是未定义的行为。我对理解类型别名细节的信心太小,无法确定。

代码片段是否如上文所述的未定义行为?如果是这样,是否有另一种方法来实现这种字节操作?

Ast*_*ngs 5

法律答复

不,这是不允许的。

C++ 不仅仅是“一堆字节”——编译器(以及更抽象的语言)被告知你有一个unsigned chars 的容器,而不是一个floats的容器。没有floats 存在,你不能假装它们存在。

您正在寻找的规则(称为严格别名)可以在 下找到[basic.lval]/8

相反会起作用,因为允许(通过同一段落中的特殊规则)通过unsigned char*. 但是,在你的情况下,最快的安全和正确的方式来“搞定”一个float来自东西开始生活unsigned charstd::memcpystd::copy字节数据到一个实际的float存在:

std::vector<unsigned char> bytes(2 * sizeof(float));
float f1, f2;

// Extracting values
std::memcpy(
   reinterpret_cast<unsigned char*>(&f1),
   bytes.data(),
   sizeof(float)
);

std::memcpy(
   reinterpret_cast<unsigned char*>(&f2),
   bytes.data() + sizeof(float),
   sizeof(float)
);

// Putting them back
f1 = 1.1;
f2 = 1.2;

std::memcpy(
   bytes.data(),
   reinterpret_cast<unsigned char*>(&f1),
   sizeof(float)
);

std::memcpy(
   bytes.data() + sizeof(float),
   reinterpret_cast<unsigned char*>(&f2),
   sizeof(float)
);
Run Code Online (Sandbox Code Playgroud)

只要这些字节float在您的系统上形成有效的表示,就可以了。诚然,它看起来有点笨拙,但快速的包装器功能将使它的工作变得简单。

假设您只关心floats 并且不需要可调整大小的缓冲区,一个常见的替代方法是生成一些std::aligned_storage然后在结果缓冲区中做一堆放置。从 C++17 开始,您也可以使用std::launder,尽管在这种情况下调整向量的大小(读取:重新分配其缓冲区)也是不可取的。

此外,这些方法非常复杂,会导致复杂的代码,并非所有读者都能理解。如果您可以清洗您的数据,使其“是”一系列floats,那么您不妨一开始就让自己变得友善std::vector<float>。根据上述内容,unsigned char*如果您愿意,可以获取和使用该缓冲区的 。

应该注意的是,在野外有很多代码使用您的原始方法(特别是在具有准系统 C 传统的旧项目中)。在许多实现中,它似乎有效。但是,人们普遍认为它是有效和/或安全的,如果您依赖它,您很容易“重新排序”(或其他优化)指令。


对冲投注答案

对于它的价值,如果您禁用严格别名(GCC 允许将其作为扩展,而 LLVM 甚至没有实现它),那么您可能可以摆脱原始代码。小心点