0

这里已经确定,将 T 类型的紧密包装的连续结构成员视为 T 的数组是非法的。

但是复制底层表示呢?

鉴于:

struct vec {
    float x, y, z;
};

具有相同的约束:

static_assert(sizeof(vec) == 3 * sizeof(float));

如下:

int main() {
    vec v = {1.9f, 2.5f, 3.1f};

    float a[3];
    std::memcpy(&a, &v, 3 * sizeof(float));
    assert(a[0] == v.x);
    assert(a[1] == v.y);
    assert(a[2] == v.z);

    vec u;
    std::memcpy(&u, &a, 3 * sizeof(float));
    assert(u.x == a[0]);
    assert(u.y == a[1]);
    assert(u.z == a[2]);
}

合法的?

4

2 回答 2

1

只要您的结构类型没有任何填充,标准中就没有明确支持它,但可以推断出支持非常接近它的东西。

给定一个可简单复制的 type ,明确允许的是将其表示复制到(or )T的数组中并返回。charunsigned char

不要求数组的内容保存在数组本身中。内容可以存储在文件中,并在程序的后续执行中重新读取。或者存储在不同类型的对象中,只要该类型允许。为此,memcpy当这些表示不是来自T同一运行中的类型对象时,实现必须允许将表示放入对象中。

结果,至少,

int main() {
    vec v = {1.9f, 2.5f, 3.1f};

    float a[3];

    assert(sizeof v == sizeof a);

    { char tmp[3 * sizeof(float)];
      std::memcpy(tmp, &v, 3 * sizeof(float));
      std::memcpy(a, tmp, 3 * sizeof(float)); }
    assert(a[0] == v.x);
    assert(a[1] == v.y);
    assert(a[2] == v.z);

    vec u;
    { char tmp[3 * sizeof(float)];
      std::memcpy(tmp, a, 3 * sizeof(float));
      std::memcpy(&u, tmp, 3 * sizeof(float)); }
    assert(u.x == a[0]);
    assert(u.y == a[1]);
    assert(u.z == a[2]);
}

应该要么在第一个失败assert,要么通过。对于任何会失败的表示,创建一个恰好以明确有效的方式提出精确表示的函数是微不足道的,因此它不能失败。

现在,在这里省略tmp有点不确定。

std::memcpy只是对单个字节的重复分配,可以明确说明。运算符的语义=意味着对于普通可复制类型,a = b;并且{ auto tmp = b; a = tmp; }是等价的。与a = b; c = d;和相同{ auto tmp1 = b; auto tmp2 = d; a = tmp1; c = tmp2; },依此类推。前者是直接memcpy做的,后者是两个memcpy通过tmp做的。

另一方面,复制入和复制出数组的权限char可以被解读为需要一个实际的数组char,而不仅仅是它的功能等价物。

就个人而言,除非我真的遇到使用该解释的实现,否则我可能不会担心这一点,但如果您想安全地使用它,您可以引入这样一个临时数组,并验证您的编译器是否设法优化它。

于 2017-02-04T10:27:28.550 回答
-3

为什么你不应该总是相信由三个相同类型成员组成的结构等同于一个相同类型的数组,基本上是因为内存对齐。

https://en.wikipedia.org/wiki/Data_structure_alignment

您的代码可以在 C++ 编译器上正常运行,而在另一个甚至在具有不同配置的同一编译器上运行失败。

另请注意,您错误地使用了数组指针。

std::memcpy(a, &v, 3 * sizeof(float));

并不是

std::memcpy(&a, &v, 3 * sizeof(float));

a 已经是一个指向 float 的常量指针

于 2017-02-04T09:11:37.420 回答