C++ 为什么动态内存允许在运行时操纵数组?

C++ 为什么动态内存允许在运行时操纵数组?,c++,c,memory,dynamic,C++,C,Memory,Dynamic,这只是“语言就是这样工作的”问题之一吗编辑: 为什么动态内存允许在运行时分配数组大小? 为什么我不能使用从堆栈调用的变量,而不是从堆调用的变量? 它们都是变量,其中一个只是从不同的位置调用,必须手动释放和创建。在堆栈中创建的变量可以 在运行时更改,对吗 使用堆的主要原因不是您可以使用不同数量的堆。它的主要用途是允许内存在特定函数完成后保持 C99和后来的标准(但不是C++),虽然我相信一些编译器(G++)确实有扩展,允许它至少在函数中允许“可变长度数组”,但是像固定大小的数组一样,函数结束时它

这只是“语言就是这样工作的”问题之一吗编辑:

为什么动态内存允许在运行时分配数组大小?

为什么我不能使用从堆栈调用的变量,而不是从堆调用的变量? 它们都是变量,其中一个只是从不同的位置调用,必须手动释放和创建。在堆栈中创建的变量可以
在运行时更改,对吗

使用堆的主要原因不是您可以使用不同数量的堆。它的主要用途是允许内存在特定函数完成后保持

C99和后来的标准(但不是C++),虽然我相信一些编译器(G++)确实有扩展,允许它至少在函数中允许“可变长度数组”,但是像固定大小的数组一样,函数结束时它们就“消失”。p> <>在C++标准中,所有数组在编译时必须有一个已知的(常数)长度。您必须使用堆来创建非恒定长度(编译时已知)的内容。这就是“语言的工作方式”

话虽如此,也有一个合理的理由。堆栈空间非常有限,事实上,耗尽堆栈是非常“危险”的,因为程序对此无能为力——它崩溃了,并且没有安全/合理的恢复方法。可以处理HEAPHOLL的运行(抛出C++异常,但至少程序可以显示一些合理的错误消息,并且可能以某种方式继续,即使它在HEAPHORE中运行时它试图做的不成功)。p> 当然,“C++方法”不是编写手动操作数组大小的代码,而是使用预定义的容器类型之一,例如
std::vector
等等

编辑 请注意,一旦从堆中分配了一个数组,它将保持分配时的大小。改变其大小的方法是为第二个数组分配另一块不同大小的内存,然后将“旧”数组的内容复制到“新”数组中——只要这样做,代码就只能看到数组地址[指向第一个元素的指针]的“当前”值,没有人会知道它不是同一个数组

为什么我不能使用从堆栈调用的变量,而不是从堆调用的变量

堆分配使您能够更好地控制内存

此外,在堆栈变量方面也存在局限性


//警告:仅适用于PC,对于我不熟悉的其他体系结构可能不适用。(mips、摩托罗拉68000等基本上与x86无关的任何产品)

在堆栈中创建的变量可以在运行时更改,对吗

它们的大小不能改变

  • 堆栈的大小有限。大小由操作系统和编译器决定。若堆栈变得太大,程序将因堆栈溢出而死亡。经典例子:

    int main(int argc, char** argv){
        char buffer[1024*1024*64];
        buffer[0] = 0;
        return 0;
    }
    
    如果在windows上使用默认设置编译,该程序将崩溃,在linux上也会崩溃。这是因为32位windows上的默认堆栈大小为1MB,32位linux上的默认堆栈大小为8MB(不过,系统可能会使用
    ulimit
    )进行更改),而64MB的数组无法放入堆栈中

  • 若变量位于堆栈上的两个其他变量之间,则无论发生什么情况,都不能更改其大小。至少在x86/64 CPU上

  • 理论上,如果堆栈数组是堆栈上的最后一个对象,则可以增加堆栈数组的大小。(如果我没记错的话,可能有一个名为alloca的非标准C函数可以在堆栈上分配数组)。但是,仍然会达到堆栈大小限制
  • 为了理解为什么会有这样的限制,你需要从C++中退一步,学习一点汇编。试着找一本书,介绍段(数据/代码/堆栈),解释函数返回地址的存储位置,最好告诉您有关保护模式的内容。这应该会有帮助

    当然,有一点问题。汇编知识只对特定系列的CPU有用。不同的CPU与C++编译器可以使用不同的规则。 --更新--

    为什么动态内存允许在运行时分配数组的大小

    根据拱门的不同,堆栈大小可能或多或少是固定的。您为堆栈保留了一些内存区域,比如地址0x00100000..0x00200000,堆栈上的所有变量都将位于该区域的某个位置。新变量的位置由(若我没记错的话)“堆栈指针”确定,它沿着CPU确定的方向移动。在堆栈上添加新变量时,堆栈指针按大小变量移动(移动方向由CPU决定),变量将位于新旧内存位置之间的地址中。因为堆栈空间可以被限制,并且因为变量是相邻的(加上函数返回地址也存储在堆栈上),所以不能突然在其中间阻塞2GB数组。主要的问题实际上不是大小有限,而是变量彼此相邻

    现在,堆不同了。从理论上讲,堆分配可以从整个地址空间中返回任何地址,但实际上某些地址将被保留(例如,在32位窗口中,整个4GB空间中只有2..3GB可用)。因为您有更多的可用空间,而且分配的块不必相邻,所以您可以自由分配大数组,甚至(理论上)调整它们的大小(实际上,像realloc这样的函数可能只是创建新数组,将旧内容复制到新数组中,然后杀死旧数组)

    请注意,还有其他隐藏的详细信息。例如,堆分配函数返回给您的地址不是物理地址,而是虚拟地址