Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否允许编译器优化堆外内存分配?_C++_Gcc_Optimization_Clang_Language Lawyer - Fatal编程技术网

C++ 是否允许编译器优化堆外内存分配?

C++ 是否允许编译器优化堆外内存分配?,c++,gcc,optimization,clang,language-lawyer,C++,Gcc,Optimization,Clang,Language Lawyer,考虑以下使用new的简单代码(我知道没有delete[],但它与此问题无关): 是否允许编译器优化new调用 在我的研究中,和Visual Studio 2015没有优化出新的调用。所有测试都启用了完全优化(-O3用于g++和clang,发布模式用于VisualStudio) 难道不是new在幕后进行系统调用,使得编译器无法(而且是非法的)优化它吗 编辑:我现在从程序中排除了未定义的行为: #include <new> int main() { int* mem = n

考虑以下使用
new
的简单代码(我知道没有
delete[]
,但它与此问题无关):

是否允许编译器优化
new
调用

在我的研究中,和Visual Studio 2015没有优化出
新的调用。所有测试都启用了完全优化(-O3用于g++和clang,发布模式用于VisualStudio)

难道不是
new
在幕后进行系统调用,使得编译器无法(而且是非法的)优化它吗

编辑:我现在从程序中排除了未定义的行为:

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[100];
    return 0;
}
#包括
int main()
{
int*mem=new(std::nothrow)int[100];
返回0;
}
不再是了,但是

EDIT2

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[1000];

    if (mem != 0)
      return 1;

    return 0;
}
#包括
int main()
{
int*mem=new(std::nothrow)int[1000];
如果(mem!=0)
返回1;
返回0;
}

谨记C++标准告诉一个正确的程序应该做什么,而不是应该如何做。它根本不能告诉后者,因为新的体系结构可以而且确实在标准编写之后出现,并且标准必须对他们有用

new
不必是引擎盖下的系统调用。有些计算机在没有操作系统和系统调用概念的情况下可用

因此,只要终端行为没有改变,编译器就可以优化任何内容。包括新的

有一个警告。
替换全局运算符new可以在不同的翻译单元中定义
在这种情况下,新产品的副作用可能无法优化消除。但是,如果编译器能够保证新操作符没有副作用,就像发布的代码是整个代码一样,那么优化是有效的。

新的can抛出std::bad_alloc不是一个要求。在这种情况下,当new被优化时,编译器可以保证不会抛出异常,也不会发生副作用。

代码段中可能发生的最坏情况是
new
抛出
std::bad\u alloc
,这是未经处理的。然后发生的事情是实现定义的

在最佳情况为无操作,最坏情况未定义的情况下,允许编译器将它们考虑为不存在。现在,如果您确实尝试捕获可能的异常:

int main() try {
    int* mem = new int[100];
    return 0;
} catch(...) {
  return 1;
}
。。。然后。

编译器完全可以(但不是必需的)优化原始示例中的分配,根据标准§1.9,在EDIT1示例中更是如此,这通常被称为“假设规则”:

一致性实现需要模拟(仅)抽象机器的可观察行为,如下所述:
[3页条件]

一种更具可读性的表示形式可在网站上找到

有关要点如下:

  • 您没有挥发物,因此1)和2)不适用
  • 您不输出/写入任何数据或提示用户,因此3)和4)不适用。但是,即使您这样做了,他们也会在EDIT1中感到满意(在最初的示例中也可以这样说,尽管从纯理论的角度来看,这是非法的,因为程序流和输出——理论上——不同,但请参见下面的两段)
一个异常,甚至是一个未捕获的异常,是定义良好(不是未定义!)的行为。但是,严格地说,如果
new
抛出(不会发生,另请参见下一段),可观察到的行为将不同,程序的退出代码和程序后面可能出现的任何输出都会不同

现在,在单个小分配的特殊情况下,您可以给编译器“怀疑的好处”,它可以保证分配不会失败。
即使在内存压力非常大的系统上,当可用的分配粒度小于最小分配粒度时,也不可能启动进程,堆也将在调用
main
之前设置好。因此,如果此分配失败,在调用
main
之前,程序将永远不会启动或已经遇到不正常的结束。
到目前为止,假设编译器知道这一点,即使在理论上分配可能抛出,优化原始示例也是合法的,因为编译器实际上可以保证不会发生这种情况


另一方面,在EDIT2示例中,优化分配是不允许的(正如您所观察到的,编译器错误)。该值用于产生外部可观察的效果(返回代码)。
请注意,如果将
new(std::nothrow)int[1000]
替换为
new(std::nothrow)int[1024*1024*1024*1024*1024*1024ll]
(这是一个4TB分配!),这在当今的计算机上保证会失败,它仍然优化了调用。换句话说,它返回1,尽管您编写的代码必须输出0

@雅克对此提出了一个很好的论点:只要内存从未被触动,指针就可以返回,而不需要实际的RAM。到目前为止,在EDIT2中优化分配甚至是合法的。我不确定谁是对的,谁是错的

在一台至少没有两位数千兆字节内存的机器上进行4TB分配几乎肯定会失败,因为操作系统需要创建页表。当然,C++标准不关心页面表,也不关心OS正在提供什么内存,这是正确的。 但另一方面,“如果内存不被触摸,这将工作”的假设确实依赖于这样的细节和操作系统提供的东西。假设未接触的RAM是
int main() try {
    int* mem = new int[100];
    return 0;
} catch(...) {
  return 1;
}
#include <cstddef>

extern void* operator new(std::size_t n);

template<typename T>
T* create() { return new T(); }

int main() {
    auto result = 0;
    for (auto i = 0; i < 1000000; ++i) {
        result += (create<int>() != nullptr);
    }

    return result;
}
main:                                   # @main
    movl    $1000000, %eax          # imm = 0xF4240
    ret