Noob在这里,我有2个问题。
1)当创建这样一个int a[10]这样的数组时,系统如何为数组设置内存?即系统是否连续分配十个 1 字内存(在堆中?)。char a[10] 呢?还是用户定义的类型?
2)动态分配呢?如 int *a = new int[10]; 这个数组在内存中是如何设置的?(与问题 1 类似的问题)。
Noob在这里,我有2个问题。
1)当创建这样一个int a[10]这样的数组时,系统如何为数组设置内存?即系统是否连续分配十个 1 字内存(在堆中?)。char a[10] 呢?还是用户定义的类型?
2)动态分配呢?如 int *a = new int[10]; 这个数组在内存中是如何设置的?(与问题 1 类似的问题)。
C++ 标准不保证这种行为——它甚至没有提到堆栈或堆。
但通常,当您调用函数时,堆栈帧会被推送到足够大的堆栈以包含该函数的所有自动变量(以及其他对象,例如其返回值)。因此,如果您考虑该功能foo
:
void foo() {
int x;
std::string str;
}
当这个函数被调用时,栈顶被推上去,所以有足够的空间存放 aint
和 a std::string
。这些类型的大小是实现定义的,标准对它们进行了一些限制,但您可以将它们视为sizeof(int)
字节sizeof(std::string)
。
现在,当您的函数中有一个数组时,例如int a[10]
,该函数的堆栈帧将包含足够的空间 10int
秒或10*sizeof(int)
字节。此帧大小直接烘焙到您的可执行文件中 - 当调用该函数时,堆栈将增加该大小。
当你做动态分配时,比如int* a = new int[10]
,你是在堆中分配 10 int
s 的空间,或者10*sizeof(int)
,。但是,您已将堆栈帧增加了一些量,即sizeof(int*)
. 指针对象本身存储在堆栈上,但int
它指向的 s 在堆上。
std::string
请注意,在第一个示例中,您可能想知道如果 an可以具有可变长度,如何将堆栈帧大小烘焙到可执行文件中。这是因为std::string
对象本身具有固定大小,sizeof(std::string)
但很可能会进行某种动态分配来管理其内部表示 - 这种内部表示将在堆上。
您将10 * sizeof(type)
在堆栈上获得一个字节存储块(假设没有发生溢出),因此内存中的每个元素都是连续的并且大小相同,这就是数组元素的指针运算有效的原因。一旦超出范围,此内存将被释放。请注意,不一定会为您初始化此内存。在 的情况下int[10]
,它将包含垃圾,而不是 0。
它是一样的,但元素在免费存储,而不是堆栈上。new
如果无法分配内存,则抛出异常。new[]
也可以超载。一旦delete[]
被调用,这个内存就会被释放,这就是为什么类似的东西std::vector
通常是一个更好的选择,因为当向量超出范围时它会释放它,在其他一些方面使用它更容易。