以字节为单位递增指针以索引跨步数组

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

我编写了一个库,其接口引用来自客户端的坐标数据。客户需要提供一系列点,每个点包含 3 个连续的

float
。我决定使用跨步数组表示,以便客户端可以继续使用自己的数据类型。

假设客户端在内部使用以下

Point
结构(它对库不可见):

struct Point {
    float[3] xyz;
    CustomData data;
    // more data
};

客户端应将一些

Point
存储在连续的存储中,并将相关信息传递到我的库:

struct Interface {
    const float* first_x;  // Pointer to xyz[0] in first Point
    std::ptrdiff_t stride; // Set to sizeof(Point)
    std::ptrdiff_t len;    // Number of contiguous Point
};

我可以用标准 C++ 安全地访问每个点的坐标吗?在我看来,某种形式的字节指针运算是不可避免的。

现在,我像这样读取坐标:

void Interface::process_points()
{
    for (std::ptrdiff_t i = 0; i < len; ++i) {
        const float* const pf = reinterpret_cast<const float*>(reinterpret_cast<const char*>(first_x) + i * stride);
        float x = pf[0];
        float y = pf[1];
        float z = pf[2];
        // do things with x, y, z
    }
}

通过增加别名

char
指针来跳过步幅。目前它似乎工作正常,但是我不知道指针算术和
char
别名的这种混合是否是 C++ 中定义良好的行为。

c++ pointer-arithmetic stride pointer-aliasing
1个回答
0
投票

只有当您的库可以在编译时获取

struct Point
的大小时,才可以以明确定义的方式执行此操作。可以按如下方式完成:

#include <bit>

static constexpr auto POINT_SIZE = ...;
using point_sized_float_array_t = std::array<float, POINT_SIZE / sizeof(float)>;
using point_sized_byte_array_t = std::array<std::byte, POINT_SIZE / sizeof(std::byte)>;

void process_points(const float* points, std::size_t point_count)
{
    const auto points_bytes_ptr = reinterpret_cast<const std::byte*>(points);

    for (std::ptrdiff_t i = 0; i < point_count; ++i)
    {
        const auto point_bytes_ptr = points_bytes_ptr + i * POINT_SIZE;
        point_sized_byte_array_t point_bytes{};

        std::copy_n(points_bytes_ptr, point_bytes.size(), point_bytes.begin());

        const auto point_as_floats = std::bit_cast<point_sized_float_array_t>(point_bytes);

        const float x = point_as_floats[0];
        const float y = point_as_floats[1];
        const float z = point_as_floats[2];
    }
}

要点

  1. 唯一使用的

    reinterpret_cast
    reinterpret_cast<const std::byte*>()
    ,以后可以安全地访问。这是类字节类型(
    char
    unsigned char
    std::byte
    )所特有的,因为从
    reinterpret_cast
    的结果读取值到(几乎)任何其他类型都会导致未定义的行为。

  2. std::bit_cast
    用于将字节数组转换为浮点数组。只要源类型和目标类型具有相同的大小并且可以轻松复制,这就是安全且定义良好的。可以安全地读取转换的结果,因为它创建值的副本,而不是引用,因此避免与严格的别名规则发生冲突。

  3. 因此,可以读取

    x
    y
    z
    中保留的值,并且程序不会导致未定义的行为。然而,重要的是要记住,这些值的正确性实际上可能并不能得到保证。这就是说,如果您正在运行一些奇异的编译器或操作系统,您可能会得到垃圾值。

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