【发布时间】:2021-05-07 10:47:32
【问题描述】:
这是对following question 的跟进。我假设我最初使用的指针算法会导致未定义的行为。但是,一位同事告诉我,该用法实际上已明确定义。下面是一个简化的例子:
typedef struct StructA {
int a;
} StructA ;
typedef struct StructB {
StructA a;
StructA* b;
} StructB;
int main() {
StructB* original = (StructB*)malloc(sizeof(StructB));
original->a.a = 5;
original->b = &original->a;
StructB* copy = (StructB*)malloc(sizeof(StructB));
memcpy(copy, original, sizeof(StructB));
free(original);
ptrdiff_t offset = (char*)copy - (char*)original;
StructA* a = (StructA*)((char*)(copy->b) + offset);
printf("%i\n", a->a);
free(copy)
}
根据 C++11 规范的§5.7 ¶5:
如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则计算不应产生溢出;否则,行为未定义。
我假设,以下部分代码:
ptrdiff_t offset = (char*)copy - (char*)original;
StructA* a = (StructA*)((char*)(copy->b) + offset);
导致未定义的行为,因为它:
- 减去两个指向不同数组的指针
- 偏移量计算的结果指针不再指向同一个数组。
这会导致未定义的行为,还是我误解了 C++ 规范?这同样适用于 C 语言吗?
编辑:
根据 cmets,我假设以下修改仍然是未定义的行为,因为生命周期结束后对象的使用:
ptrdiff_t offset = (char*)(copy->b) - (char*)original;
StructA* a = (StructA*)((char*)copy + offset);
在使用索引时是否会定义它:
typedef struct StructB {
StructA a;
ptrdiff_t b_offset;
} StructB;
int main() {
StructB* original = (StructB*)malloc(sizeof(StructB));
original->a.a = 5;
original->b_offset = (char*)&(original->a) - (char*)original
StructB* copy = (StructB*)malloc(sizeof(StructB));
memcpy(copy, original, sizeof(StructB));
free(original);
StructA* a = (StructA*)((char*)copy + copy->b_offset);
printf("%i\n", a->a);
free(copy);
}
【问题讨论】:
-
你的假设是正确的,因为你提到的确切原因。而且,是的,C 也一样。
-
另外,在 malloc 的位置没有 StructB 对象。您需要使用placement new 创建一个。
-
@maxbachmann 不,您分配了内存但没有创建对象
-
停止使用那个同事,换一个新的。
-
@Jean-BaptisteYunès:这不是一个很好的推理基础。很常见的是,可以保证分配的差异是结构大小的倍数:结构的大小是 8 字节(两个 4 字节成员)或 16(指针是 8 字节,八字节对齐) ),而
malloc返回的地址是 16 的倍数。要断定行为是未定义的,观察original在它指向的内存被释放后是无效的就足够了,而且,减法不在指针之间进入同一个数组。
标签: c++ c undefined-behavior