【问题标题】:Understanding Code example with reinterpret_cast of POD-struct使用 POD-struct 的 reinterpret_cast 理解代码示例
【发布时间】:2018-11-28 07:11:18
【问题描述】:

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

我找到的代码是这样的:

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 的第一个成员? 然后用

替换像素
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]);

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

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

【问题讨论】:

  • 我认为你应该调用一个新的展示位置是正确的,但是,std::vector 解决了完整的问题
  • @JVApen 调用placementy new 究竟要做什么?在向量提供的空间中创建一个新像素?这不是这段代码试图做的。
  • 为了解决您的实际问题,您可能应该将datasize 包装到一个类实例中,并使用为您提供所需访问权限的方法,并完全避免reinterpret_cast。一般来说,如果你可以在没有reinterpret_cast 的情况下做到这一点,你可能应该这样做。或者,用 C 编写代码(或使用 C++ 的相关 GNU 扩展来允许这样做),并使用带有联合或其他东西的类型双关语......
  • @hyde 考虑用例,该类实例必须是 random_access_iterator。我必须有两个不同的类像素类:一个使用数据,它通过指针知道并由迭代器返回。另一个单独保存数据,可用于保存像素。

标签: c++ c++14 standard-layout


【解决方案1】:

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

您的代码中没有任何内容会创建 PixelPixel2 对象。

【讨论】:

  • 所以如果我有一个像素 p,那么我可以将它重新解释为第一个成员,因为它是 POD 类型,在这种情况下,第一个成员与对象具有相同的地址。反之未定义?
  • 访问不存在的对象是未定义的。一个Pixel 包含三个ints,所以如果你有一个Pixel,你就有这三个ints。如果你有三个ints,那并不意味着你有一个Pixel
【解决方案2】:

我在这里可能完全错了,但在我看来,你的两个结构实际上都是 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 行的 cmets(将 std::vector&lt;int&gt; data; 替换为 static_vector&lt;int, 128&gt; data;)。

一些值得提及的 SO 答案:

【讨论】:

  • UB 不依赖于对齐方式。 UB 基本上允许它的可能性,并具有与严格混叠和优化有关的其他后果,等等。所以这要么是 UB,要么不是 UB。
  • @LightnessRacesinOrbit 在这种情况下我不知道如何正确表达它。我会说它是 UB,但与以往完全不同......
  • 现在措辞更好了。 UB是一回事;您的程序具有 UB 的潜在实际后果是另一回事 :)
  • 有了现代的、热心优化的编译器,任何 UB 都是危险的 UB。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-24
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
  • 1970-01-01
相关资源
最近更新 更多