使用new char []或malloc的结果来表示浮动*是UB(严格别名冲突)吗?

时间:2017-10-26 17:51:13

标签: c++ malloc language-lawyer strict-aliasing object-lifetime

这些代码中有哪些代码具有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 ++中无用)。因此,bc&amp; e也没有。我错了吗?也许b是UB,但c不是?

3 个答案:

答案 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无用”; mallocoperator 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 (您投射的类型)既不是AliasedTypecharunsigned char。因此,所有这些(但是c)都表现出不确定的行为。

免责声明:首先,cppreference不是官方参考,但只有标准是。其次,不幸的是,如果我对cppreference上读到的内容的解释是正确的,我甚至不能100%肯定。