【发布时间】:2017-06-07 09:50:26
【问题描述】:
我有一个结构数组,并且我有一个指向其中一个结构的成员的指针。我想知道数组的哪个元素包含该成员。这里有两种方法:
#include <array>
#include <string>
struct xyz
{
float x, y;
std::string name;
};
typedef std::array<xyz, 3> triangle;
// return which vertex the given coordinate is part of
int vertex_a(const triangle& tri, const float* coord)
{
return reinterpret_cast<const xyz*>(coord) - tri.data();
}
int vertex_b(const triangle& tri, const float* coord)
{
std::ptrdiff_t offset = reinterpret_cast<const char*>(coord) - reinterpret_cast<const char*>(tri.data());
return offset / sizeof(xyz);
}
这是一个测试驱动程序:
#include <iostream>
int main()
{
triangle tri{{{12.3, 45.6}, {7.89, 0.12}, {34.5, 6.78}}};
for (const xyz& coord : tri) {
std::cout
<< vertex_a(tri, &coord.x) << ' '
<< vertex_b(tri, &coord.x) << ' '
<< vertex_a(tri, &coord.y) << ' '
<< vertex_b(tri, &coord.y) << '\n';
}
}
这两种方法都产生了预期的结果:
0 0 0 0
1 1 1 1
2 2 2 2
但它们是有效的代码吗?
我特别想知道vertex_a() 是否可能通过将float* y 转换为xyz* 来调用未定义的行为,因为结果实际上并不指向struct xyz。这种担忧促使我写了vertex_b(),我认为这是安全的(是吗?)。
这是 GCC 6.3 使用 -O3 生成的代码:
vertex_a(std::array<xyz, 3ul> const&, float const*):
movq %rsi, %rax
movabsq $-3689348814741910323, %rsi ; 0xCCC...CD
subq %rdi, %rax
sarq $3, %rax
imulq %rsi, %rax
vertex_b(std::array<xyz, 3ul> const&, float const*):
subq %rdi, %rsi
movabsq $-3689348814741910323, %rdx ; 0xCCC...CD
movq %rsi, %rax
mulq %rdx
movq %rdx, %rax
shrq $5, %rax
【问题讨论】:
-
这对the strict aliasing rule 造成了严重破坏。
-
@Someprogrammerdude:你能澄清一下吗?我认为
vertex_b()不会破坏严格混叠。至于vertex_a(),我不确定,因为指针永远不会被取消引用。 -
@Someprogrammerdude 不,不是。
标签: c++ language-lawyer undefined-behavior pointer-arithmetic