分配后填充字节

use*_*522 12 c++ padding language-lawyer

#include<cstring>

struct A {
    char a;
    int b;
};

int main() {
    A* a = new A();
    a->a = 1;
    unsigned char m[sizeof(A)];
    std::memcpy(m, a, sizeof(A));
    return m[1];
}
Run Code Online (Sandbox Code Playgroud)

除了由于分配失败而可能出现的异常以及假设在和之间0至少有一个填充字节之外,该程序是否保证以 C++ 中的状态退出?abA

new A()进行值初始化,因为A的默认构造函数很简单,但既不是用户提供的也不是删除的,因此将对象的所有成员和填充字节归零A

对于 C,N1570(C11 草案)中的 6.2.6.1p6 对我来说似乎暗示填充字节在分配给成员后处于未指定的状态,尽管我可能会误解这一点(请参阅评论)。但无论如何,我在 C++ 标准(草案)中没有看到任何允许这样做的规则。


受此启发如果在第二个(不兼容)示例中分配给成员,则零初始化结构的填充可能会泄漏信息。但请注意,该示例的描述无论如何都是错误的,因为它实际上执行聚合初始化,而不是值初始化,因此没有零初始化。


这是我之前在问题中使用的代码的两个类似版本,但由于与我用来检查对象表示的方法不相关的问题,它们可能具有 UB(请参阅注释):

#include<new>

struct A {
    char a;
    int b;
};

int main() {
    unsigned char* m = new unsigned char[sizeof(A)];
    A* a = new(m) A();
    a->a = 1;
    return m[1];
}
Run Code Online (Sandbox Code Playgroud)

struct A {
    char a;
    int b;
};

int main() {
    A* a = new A();
    a->a = 1;
    return reinterpret_cast<unsigned char*>(a)[1];
}
Run Code Online (Sandbox Code Playgroud)

Sol*_*cko 0

看起来像这样的代码memcpy隐式允许使用任何值作为填充位。实际上,memcpy只需复制那里的任何内容,但其他代码(例如字段分配)可以更改它(如果它被认为是不可观察的)。

\n

https://eel.is/c++draft/basic.types.general指出:(添加了重点)

\n
\n

对于普通可复制类型 T 的任何对象(除了潜在重叠的子对象),无论该对象是否持有类型 T 的有效值,构成该对象的底层字节([intro.memory])都可以复制到char、unsigned char 或 std\xe2\x80\x8b::\xe2\x80\x8bbyte ([cstddef.syn]) 的数组。30如果该数组的内容被复制回对象中,则该对象随后应保留其原始

\n
\n
\n

T 类型对象的对象表示是 T 类型对象占用的 N 个 unsigned char 对象的序列,其中 N 等于 sizeof(T)。类型 T 的对象的值表示是参与表示类型 T 的值的一组位。对象表示中不属于值表示的位是填充位。对于普通可复制类型,值表示是对象表示中确定值的一组位,该值是实现定义的一组值的一个离散元素32

\n
\n
\n
    \n
  1. 例如,通过使用库函数([头])std\xe2\x80\x8b::\xe2\x80\x8bmemcpy或 std\xe2\x80\x8b::\xe2\x80\x8bmemmove。
  2. \n
\n
\n
\n
    \n
  1. 目的是 C++ 的内存模型与 ISO/IEC 9899 编程语言 C 的内存模型兼容。
  2. \n
\n
\n