如果是int数组,new-expression是否可以“溢出”?

时间:2019-01-10 11:17:06

标签: c++ memory memory-management language-lawyer c++17

如果您写int *p = new int[SIZE_MAX];,可以保证会发生什么?抛出异常了吗?还是有可能使用计算为operator new[]的参数调用基础SIZE_MAX * sizeof(int)函数,并可能发生未经检查的溢出(模减少)?

在C ++ 17(N4659)§6.9.2 [basic.compound]¶2:

  

构造类型时,其对象表示形式中的字节数超过类型std::size_t(21.2)中可表示的最大值。

“格式错误”类型的后果是什么?行为不确定?

我们假设sizeof(int)大于1。以下程序格式是否正确,并保证会引发异常?

#include <cstdint> // SIZE_MAX
#include <cstddef> // std::size_t

int main() {
    std::size_t size_max = (SIZE_MAX);
    int *pointer = new int[size_max];
}

还是我必须执行以下溢出检测?

#include <cstdint> // SIZE_MAX
#include <cstddef> // std::size_t
#include <new>     // std::bad_alloc

bool mul_overflow (std::size_t a, std::size_t b) {
    std::size_t size_max = (SIZE_MAX);
    return a > (size_max / b);
}

int main() {
    std::size_t size_max = (SIZE_MAX);
    if (mul_overflow (size_max, sizeof(int)))
        throw std::bad_alloc ();

    int *pointer = new int[size_max];
}

目的是为了避免在优化但符合标准的实现中出现整数溢出,如果标准指出在这种情况下发生的事情是实现定义的(或未定义/未指定的行为),则该整数溢出可以在不检查模减少的情况下执行乘法)。

旁注:

  • 即使无符号整数类型不会溢出,我也会说“溢出”。这篇文章中的意思是“模减少”。
  • new-expression 是在C ++ 17(N4659)§8.3.4 [expr.new]¶1中定义的。
  • 这是一个“语言律师”问题。

1 个答案:

答案 0 :(得分:2)

您引用的段落是关于类型的。

即您不能将T的类型为sizeof(T) > SIZE_MAX。这与new无关,应该由编译器在编译阶段进行诊断。

new的行为在不同的地方进行了解释。 从C ++ 11开始,存在一个特殊的异常类型std::bad_array_new_length,如果大小错误,实现应抛出该异常。这是标准中的相关报价:

[expr.new]

  

在以下情况下,noptr-new-declarator中的表达式是错误的:

     
      
  • (8.1)该表达式为非类类型,在转换为std :: size_­t之前其值小于零;
  •   
  • (8.2)该表达式是类类型,并且在应用第二个标准转换([over.ics.user])之前其值小于   零;
  •   
  • (8.3),其值应使分配的对象的大小超过实现定义的限制;或
  •   
  • (8.4)new-initializer是一个括号初始化列表,以及为其提供初始化程序的数组元素的数量(包括   在字符串文字中以“ \ 0”结尾)超过了元素数量   进行初始化。
  •   
     

如果将表达式转换为std :: size_:t:

后错误      
      
  • (8.5)   如果表达式是核心常量表达式,则程序格式错误;
  •   
  • (8.6)   否则,不调用分配函数。代替      
        
    • (8.6.1)   如果将要调用的分配函数具有非抛出异常规范([except.spec]),则   new-expression是所需结果类型的空指针值;
    •   
    • (8.6.2)   否则,new表达式将通过抛出与以下类型的处理程序([except.handle])相匹配的类型的异常来终止   输入std ::: bad_­array_­new_­length。
    •   
  •   

所以这是预期的行为。但是,请记住,您的operator new重载只会收到最终的size_t(即在所有计算之后),因此您不需要/不需要进行模减少。

在实现自己的operator new(8.5)时,应由编译器处理,而您只需要处理(8.6)。