使用 POD 结构的 reinterpret_cast 理解代码示例

Understanding Code example with reinterpret_cast of POD-struct

我找到了一些代码并想确保我理解正确。 用例是由值数组表示的打包图像。 在此示例中,三个值代表一个像素。

我找到的代码是这样的:

struct Pixel{ 
  int[3] data
  int x(){return data[0];}
  int y(){return data[1];}
  int z(){return data[2];}
};

void main(){

  std::vector<int> img(300);
  Pixel* access = reinterpret_cast<Pixel*>(img.data()+3*5);
  foo(access->x());
}

据我阅读 POD and standard layout 的理解,我认为代码示例是有效的,因为我们只使用了 Pixel 的第一个成员? 然后将 Pixel 替换为

struct Pixel2{
  int red;
  int green;
  int blue;
};

会导致未定义的行为吗?

编辑: 我使用 cuda 并找到了另一个例子: 将 unsigned char 指针(数组)强制转换为 uchar3 指针。 uchar3 类型定义等于第二个像素定义。 这是否意味着第二个也有效? 或者这只适用于 nvcc 编译的代码吗? 如果第二个 Pixel 定义有效,那为什么?

编辑: 为了进一步强调代码试图做什么,我重命名了上面的一些字段: 我有一组原始数据。在我的例子中,这是一个打包的图像。我想要一种访问像素及其值的好方法。所以我可以这样做:

void bar(int* data,size_t size)
{
   Pixel2* img = reinterpret_cast<Pixel*>(data);
   std::cout << "Pixel 13 has blue value: " << img[13].blue;
}

我在 cuda 中看到过使用它的代码并且它有效,但我想知道它是否总是好的,因为它似乎没有包含在我读到的关于 POD 的内容中。 我只是错过了关于 POD 的一些东西还是这可能会失败?

编辑: 是不是有区别:

  foo(access->x());
  foo(access->data[0]);

我认为第二个应该是合法的,因为对于 POD 类型,第一个成员变量与对象具有相同的地址?

编辑:我从答案中得到的是:在我提到的所有情况下都是 UB。 然后要走的路是一个随机访问迭代器,它给我我想要的访问权限。

我在这里可能完全错了,但对我来说,你的两个结构实际上都是 UB。然而,这不太可能永远发生。

它可能造成一些伤害的(不太可能)用例是当您的向量对齐(例如可以从库中提供)和结构不同时。如果代码是使用相同的编译器和相同的设置编译的,则不会发生这种情况,除非您自己对其进行对齐。考虑向量的对齐方式不同,两个结构都会导致 UB。或者你的结构对齐方式不同,这里相同,UB。然而,未定义的行为不是来自对齐,而是来自 reinterpret_cast 对此一无所知的事实。

举个简单粗暴的例子:

struct Pixel2 {
    alignas(8) int red;
    alignas(8) int green;
    alignas(8) int blue;
};

会给你错误的像素值。使用 int 数组的结构也可以这样做。

See this example 哪里可以玩。这里两个结构都无法获得正确的值。对于向量对齐方式不同的变体,替换第 69/70 行的注释(将 std::vector<int> data; 替换为 static_vector<int, 128> data;)。

一些值得一提的 SO 答案:

  • How is a vector's data aligned?
  • How is an array aligned in C++ compared to a type contained?

对不存在的对象调用非静态成员函数和对不存在的对象的非静态数据成员执行class成员访问都是未定义的行为。

您的代码中没有创建 PixelPixel2 对象。