繁体   English   中英

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

[英]Manipulating byte vector through float pointer

是否可以通过其数据指针操作std::vector<unsigned char> ,就好像它是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;
}

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

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

法律答案

不,这是不允许的。

C++ 不仅仅是“字节负载”——编译器(以及更抽象的语言)被告知您有一个unsigned char容器,而不是float容器。 不存在float ,你不能假装他们存在。

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

相反会起作用,因为允许(通过同一段中的特殊规则)通过unsigned char*检查任何类型的字节。 但是在您的情况下,从以unsigned char开始生命的事物中“获取” float的最快安全和正确方法是将std::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)
);

只要这些字节在您的系统上形成有效的float表示,就可以了。 当然,它看起来有点笨拙,但是一个快速的包装器 function 将很快完成它。

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

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

应该注意的是,在野外有很多代码使用您的原始方法(特别是在具有准系统 C 遗产的旧项目中)。 在许多实现中,它似乎可以工作。 但一个常见的误解是它是有效的和/或安全的,如果你依赖它,你很容易指令“重新排序”(或其他优化)。


对冲下注答案

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

是否可以通过它的数据指针来操作一个 std::vector,就好像它是一个浮点容器一样?

不完全的。 你的例子确实有UB。

但是,您可以重用这些字节的存储来在那里创建浮点数。 例子:

float* ptr = std::launder(reinterpret_cast<float*>(bytes.data()));
std::uninitialized_fill_n(ptr, 2, 0.0f);

在此之后, unsigned char 对象的生命周期已经结束,那里有浮点数。 使用ptr定义明确。

这对您是否有用是另一回事。 首先从更简单的设计开始:为什么不简单地使用std::vector<float>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM