首页 > 解决方案 > C++ 中的动态零长度数组

问题描述

#include <stdlib.h>

void *operator new[](size_t size, int n){
    if( size != 0 && n != 0 )
        return calloc(n, size);
    return calloc(1, 1);
}

int main(){

    int * p1;
    const int i = 0;

//  p1 = new (20)  int[i] ; // Case 1 (OK)
    p1 = new (20) (int[i]); // Case 2 (Warning)

    if( p1 == 0 )
        return 1;
    return 0;
}

此代码 ( https://godbolt.org/g/hjo7Xn ) 使用 Clang 6.0.0 成功编译,但是,GCC 7.3 发出警告说在 C++ 中禁止使用零长度数组。如果括号被删除(案例 1),警告就会消失。

与静态分配的零长度数组 (C++03:8.3.4/1) 不同,动态分配的零长度数组是允许的 (C++03:5.3.4/6)。然而,在 C++ 标准中,仅当遵循new-expression的两种可能的语法路径之一时才明确允许后者,即具有new-type-id且不带括号的语法路径(案例 1)。

C++ 标准是否允许在第二个语法路径之后使用带有零长度数组的new-expression ,即使用type-id和括号(案例 2)?

唯一相关的引用是 C++03:5.3.4/5:

当分配的对象是一个数组(即使用直接新声明符语法或new-type-idtype-id表示数组类型)时,new-expression产生一个指向初始元素的指针(如果任何)的数组。

该措辞(if any)将允许一个没有元素的数组,但是,似乎不清楚它是指这两种情况还是仅指具有new-type-id且不带括号的一种情况(情况 1)。

提前致谢。

笔记:

  1. ISO/IEC 14882:2003,第 8.3.4 节,第 1 段:

    如果常量表达式(5.19) 存在,它应该是一个整数常量表达式并且它的值应该大于零。

  2. ISO/IEC 14882:2003,第 5.3.4 节,第 6 段:

    direct-new-declarator中的表达式应具有非负值的整数或枚举类型 (3.9.1)。

  3. ISO/IEC 14882:2003,第 5.3.4 节,第 7 段:

    direct-new-declarator中表达式的值为零时,调用分配函数来分配一个没有元素的数组。

  4. ISO/IEC 14882:2003,第 5.3.4 节,第 1 段:

    新表达式

    :: opt new -placement opt new-type-id new-initializeropt

    ::opt 的新位置opt( type-id )新的初始化器opt

  5. 尽管以上引用来自 C++03 标准,但据我所知,这个问题在 C++ 标准的较新版本(C++11、C++14 和 C++17)中仍然不清楚。
  6. 有趣的 Herb Sutter关于零长度数组的帖子。
  7. 示例中的代码是对SolidSands 的SuperTest 套件稍作修改的测试。

标签: c++arraysc++11new-operatordynamic-memory-allocation

解决方案


使用括号,您有一个常规的type-id ,而不是称为支持动态数组大小的new-type-id的特殊语法。

从 C++17 开始,该标准没有对type-id的这种使用做出特殊规定,所以问题归结为您是否可以编写

auto main() -> int
{
    using Argh = int[0];
}

你不能,因为type-id的类型是根据虚构的“省略实体名称的那种类型的变量或函数的声明”(C++17 §11.1/1)定义的,并且对于数组变量的声明,规则是“如果常量表达式 (8.20) 存在,它应该是类型的转换常量表达式std::size_t并且它的值应该大于零”(C++17 §11.3.4/1 )。


现在对此有相当多的解释。例如,如果没有这种合理的解释,最后的引用就不能合理地说数组大小必须是非负的并且可以表示为size_t. 相反,在没有合理解释的情况下,它会字面上说像这样的声明

int x[42];

无效(当然不是),必须表示为

int x[std::size_t(42)];

确定什么是合理的解释或过去并不容易。有人可能会问,这有意义吗?因此,对于上述情况,答案是否定的,人们可以放弃这种可能性。

然而,在某种程度上,C++14 和 C++17 越来越多,我发现早期可靠的技术失败了。但是,由于手头的问题是关于 C++03 功能的,我认为您可以相信这个答案。但是,如果这是关于 C++14 或更高版本的问题,那么请记住,任何明显明确的答案都可能涉及一些主观解释,可能无法通过询问它是否有意义来解决。


推荐阅读