这些代码中有哪些代码具有UB(具体而言,违反了严格的别名规则)?
void a() {
std::vector<char> v(sizeof(float));
float *f = reinterpret_cast<float *>(v.data());
*f = 42;
}
void b() {
char *a = new char[sizeof(float)];
float *f = reinterpret_cast<float *>(a);
*f = 42;
}
void c() {
char *a = new char[sizeof(float)];
float *f = new(a) float;
*f = 42;
}
void d() {
char *a = (char*)malloc(sizeof(float));
float *f = reinterpret_cast<float *>(a);
*f = 42;
}
void e() {
char *a = (char*)operator new(sizeof(float));
float *f = reinterpret_cast<float *>(a);
*f = 42;
}
我问这个,因为this问题。
我认为,d
没有UB(否则malloc
在C ++中无用)。因此,b
,c
&amp; e
也没有。我错了吗?也许b
是UB,但c
不是?
答案 0 :(得分:7)
序言:存储和对象是C ++中的不同概念。 存储指的是内存空间,对象是具有生命周期的实体,可以在一个存储空间内创建和销毁。随着时间的推移,存储可以重新用于托管多个对象。所有对象都需要存储,但可以存储没有对象的存储。
c 是正确的。 Placement-new是在存储中创建对象的有效方法之一(C ++ 14 [intro.object] / 1),即使该存储中存在预先存在的对象。重新使用存储会隐藏地破坏旧对象,只要它们没有非平凡的析构函数([basic.life] / 4),这就完全没问题了。 new(a) float;
在现有存储([expr.new] / 1)中创建类型为float
的对象和动态存储持续时间。
d 和 e :通过glvalue表达式访问内存的效果仅在表达式引用对象时定义;而不是当表达式引用不包含任何对象的存储时。 (注意:请不要对现有定义明显不足提出非建设性意见。)
这并不意味着“malloc无用”; malloc
和operator new
的效果是获取存储空间。然后,您可以在存储中创建对象并使用这些对象。事实上,这正是标准分配器和new
表达式的工作方式。
a 和 b 是严格的别名冲突:类型float
的glvalue用于访问不兼容类型char
的对象。 ([basic.lval] / 10)
There is a proposal会使所有案例定义明确(除了下面提到的 a 的对齐):根据此提案,使用*f
隐式创建对象在该位置的那种类型,有一些警告。
注意: b 到 e 的情况下没有对齐问题,因为new-expression和::operator new
可以保证正确分配存储空间type([new.delete.single] / 1)。
但是,在std::vector<char>
的情况下,即使标准指定调用::operator new
来获取存储,标准也不要求将第一个向量元素放在第一个字节中存储;例如向量可以决定在前面分配3个额外字节,并将其用于某些簿记。
答案 1 :(得分:1)
尽管OP和我之间的讨论产生了这个问题,但我仍然会在这里解释。
我认为c()
的所有这些保存都包含标准正式定义的严格别名冲突。
我的基础是standard
的第1.8.1节...对象由定义(3.1),new-expression(5.3.4)创建 或者在需要时通过实施(12.2)。 ...
reinterpret_cast<>
记忆不属于任何一种情况。
答案 2 :(得分:1)
来自cppreference:
键入别名
每当尝试读取或修改存储的值时 DynamicType类型的对象,通过类型为AliasedType的glvalue, 除非满足以下条件之一,否则行为是未定义的:
- AliasedType和DynamicType相似。
- AliasedType是DynamicType的(可能是cv限定的)有符号或无符号变体。
- AliasedType是std :: byte,(自C ++ 17开始)char或unsigned char:这允许检查任何对象的对象表示为 一个字节数组。
非正式地,如果在剥离cv资格后,两种类型是相似的 每个级别(但不包括函数类型中的任何内容),它们是相同的 类型。
例如:[...一些例子......]
另外cppreference:
glvalue是一个表达式,其评估决定了它的身份 对象,位域或函数;
除了(c)之外,以上所有例子都是相关的。类型既不相似也不签名/无符号变体。此外,number start end
0 3 4
0 7 8
1 1 2
1 5 5
2 6 6
(您投射的类型)既不是AliasedType
,char
或unsigned char
。因此,所有这些(但是c)都表现出不确定的行为。
免责声明:首先,cppreference不是官方参考,但只有标准是。其次,不幸的是,如果我对cppreference上读到的内容的解释是正确的,我甚至不能100%肯定。