如果我有这样一个对象:
struct {
uint32_t n;
uint8_t c;
} blob {};
然后会有3个'填充'字节。
UB是否可以访问填充字节? E.g:
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));
我的具体问题是:
答案 0 :(得分:6)
不,当整个对象被初始化为零时,不是UB访问填充(标准在§8.5/ 5中说,当对象初始化为零时填充初始化为0位)或值初始化时不是具有用户定义构造函数的类。
答案 1 :(得分:0)
我认为,在适当的情况下,你最终可能会遇到UB。我在想的是你有ECC或奇偶校验的内存,其中ecc /奇偶校验位是通过写入内存来设置的。如果在[从未写入AT ALL]之前尚未使用内存块,并且您在填充字段中读取了未初始化的字节,那么当未写入的内存时,可能会导致ecc /奇偶校验错误正在阅读。
当然,在这样一个系统中,你只需在启动过程中的某个时刻做一个“填充所有内存”就可以避免一大堆痛苦,因为这样做会很容易:
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
代码。
答案 2 :(得分:0)
在C中,它不是未定义的行为。您访问未初始化的东西(例如对象中的填充)时唯一一次获得未定义的行为,就是当对象具有自动存储持续时间并且从未进行过地址时
6.3.2.1.2: 如果 左值指定了一个自动存储持续时间的对象 声明与寄存器存储类(从未有过其地址)和该对象 未初始化(未使用初始化程序声明,并且没有对其进行任何分配) 在使用之前执行),行为未定义。
但在这种情况下,您正在使用地址(使用&
),因此行为定义良好(不会发生错误),但您可能会得到任何随机值。
在C ++中,所有的赌注都是关闭的,通常就是这样。
答案 3 :(得分:0)
POD结构将存在于至少sizeof(struct)字节(包括任何填充字节)的连续内存块中。只有在未首次初始化时,访问填充字节(如果存在)才是UB。
memset(&s, 0, sizeof(s));
这将初始化所有字节,包括填充。之后从填充中读取不会是UB。
当然,memset()
是C-ism,我们永远不会在C ++中这样做,对吗?