10

如果我有这样的对象:

struct {
    uint32_t n;
    uint8_t c;
} blob {};

那么将有3个“填充”字节。

是 UB 访问填充字节吗?例如:

uint8_t * data = reinterpret_cast<uint8_t*>(&blob);
std::cout << data[4] << data[5] << data[6] << data[7];

我首先假设这可能是 UB,但如果这是真的,那么 memcpy 也将是 UB:

memcpy(buf, &blob, sizeof(blob));

我的具体问题是:

  • 是 UB 访问填充字节吗?
  • 如果不是,那么这是否意味着也定义了这些值?
4

5 回答 5

6

No, it's not UB to access padding when the entire object has been zero-initialised (the standard says in §8.5/5 that padding is initialised to 0-bits when objects are zero-initialised) or value-initialised and it is not a class with a user-defined constructor.

于 2013-02-06T20:52:48.273 回答
0

我认为,在适当的情况下,您最终可能会为此选择 UB。我在想的是你有 ECC 或奇偶校验检查的内存,其中 ecc/奇偶校验位是通过写入内存来设置的。如果在 [never waswritten to AT ALL] 之前没有使用过一块内存,并且您在填充字段中读取了未初始化的字节,那么当尚未写入的内存时,它可能会导致 ecc/parity 错误正在阅读。

当然,在这样的系统中,您可以通过在启动期间的某个时间点简单地执行“填充所有内存”来避免一大堆痛苦,因为这样做是不合时宜的:

struct Blob 
{
    uint32_t n;
    uint8_t c;
};

Blob *b = malloc(sizeof(Blob)*10);

for(int i = 0; i < 10; i++)
{
   b[i].n = i;
   b[i].c = i;
 }


 ...

 Blob a[3];

 memcpy(a, &b[1], sizeof(a));    // Copies 3 * Blob objects, including padding. 

现在,由于并非 b[x] 的所有位都已设置,它可能无法复制 memcpy 中的数据,因为奇偶校验/ecc 错误。那会很糟糕。但同时,不能强制编译器“设置”所有的填充区域。

我的结论是,这是UB,但除非有特殊情况,否则不太可能引起问题。当然,您会在很多代码中看到上述类型的memcpy代码。

于 2013-02-06T21:07:32.403 回答
0

在 C 中,它不是未定义的行为。唯一一次访问未初始化的东西(例如对象中的填充)会导致未定义的行为,即对象具有自动存储持续时间并且从未使用过它的地址:

6.3.2.1.2:如果左值指定了一个可以使用寄存器存储类声明的自动存储持续时间的对象(从未使用过它的地址),并且该对象未初始化(未使用初始化程序声明并且没有分配给它已在使用前执行),则行为未定义。

但在这种情况下,您使用的是地址(带有&),因此行为是明确定义的(不会发生错误),但您可能会得到任何随机值。

在 C++ 中,所有的赌注都没有了,就像通常的情况一样。

于 2013-02-06T21:31:00.020 回答
0

如果它不是未定义的行为,那么它肯定是实现定义的。虽然 C++ 标准不能保证你的程序做什么,但你的系统的 ABI 规范——如果你使用的是 Linux,则为SysV——可以。我怀疑如果你在填充位上胡闹,你可能更感兴趣的是你的程序将如何在你的系统上运行,而不是它在任何符合 C++ 的任意系统上的运行方式。

于 2013-02-06T21:44:31.333 回答
0

POD 结构将存在于至少 sizeof(struct) 字节(包括任何填充字节)的连续内存块中。访问填充字节(如果它们存在)只有在没有首先初始化的情况下才会是 UB。

memset(&s, 0, sizeof(s));

这将初始化所有字节,包括填充。之后从填充中读取将不是 UB。

当然,memset()这是一种 C 主义,我们永远不会在 C++ 中这样做,对吧?

于 2013-02-06T22:05:21.190 回答