c++ - 为什么 C++ 编译器不消除 new 返回的指针的空检查?

标签 c++ compiler-construction new-operator compiler-optimization

最近我在ideone.com (gcc-4.3.4) 上运行了以下代码

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <new>

using namespace std;

void* operator new( size_t size ) throw(std::bad_alloc)
{
     void* ptr = malloc( 2 * 1024 * 1024 * 1024);
     printf( "%p\n", ptr );
     return ptr;
}

void operator delete( void* ptr )
{
    free( ptr );
}

int main()
{
    char* ptr = new char;
    if( ptr == 0 ) {
        printf( "unreachable\n" );
    }
    delete ptr;
}

得到这个输出:

(nil)
unreachable

虽然 new 永远不会返回空指针,因此调用者可以指望它并且编译器可以消除 ptr == 0 检查并将依赖代码视为无法访问。

为什么编译器不删除该代码?这只是错过了优化还是有其他原因?

最佳答案

我认为这很简单,你混淆了两个根本不同的东西:

  1. ma​​lloc() 可以返回任何值,尤其是零。

  2. 标准要求全局 C++ 分配函数 void * operator new(size_t) throw(std::bad_alloc) 返回指向所需存储量的指针(+适当对齐),或者通过异常退出。

如果您想替换全局分配函数,您有责任提供符合标准规则的替换。最简单的版本如下所示:

void * operator new(size_t n) throw(std::bad_alloc) {
  void * const p = std::malloc(n);
  if (p == NULL) throw std::bad_alloc();
  return p;
}

任何认真的实现实际上都应该包含一个循环来调用已注册的新处理程序,直到分配成功,并且仅在没有更多新处理程序时抛出。

您编写的程序格式错误。


题外话:为什么这个 new 是这样定义的?当您说 T * p =::new T(); 时,请考虑标准分配顺序。它等同于:

void * addr = ::operator new(sizeof(T));  // allocation
T * p = ::new (addr) T();                 // construction

如果第二行抛出异常(即构造失败),则使用相应的释放函数释放内存。但是,如果第一次调用失败,那么执行绝不能到达第二行!实现此目的的唯一方法是通过异常退出。 (分配函数的无抛出版本仅供手动使用,用户代码可以在继续构造之前检查分配器的结果。)

关于c++ - 为什么 C++ 编译器不消除 new 返回的指针的空检查?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8213320/

相关文章:

c++ - 这两种新语法有什么区别?

c++ - 如何在 CLion 中显示 C++ 的参数名称提示?

c++ - 堆栈与缓存友好分配器

c++ - 如何添加两个多维 std::vectors(不使用运算符)?

c++ - Bison 问题 - Start Symbol 不导出任何句子

"static"循环优化

c - token 是在预处理之后计数还是仅在预处理过程中计数?

java - 在 Java 中,new 和 local 之间有性能差异吗?

c++ - 我真的可以用圆括号初始化数组吗?

c++ - 如何对这个数据结构进行拓扑排序