静态内存分配和动态内存分配的区别

118

我想了解静态内存分配和动态内存分配之间的区别是什么?

您能用示例来解释一下吗?

7个回答

138

这是一个标准的面试问题:

动态内存分配

calloc()malloc()等函数可以在运行时分配内存。有时候也称为“堆”内存,尽管它与堆数据结构无关。参考

int * a = malloc(sizeof(int));

堆内存在调用free()之前会一直存在。换句话说,你控制变量的生命周期。

自动分配内存

这通常被称为“栈”内存,在进入新作用域时分配(通常是将一个新函数推入调用堆栈中)。一旦离开该作用域,自动内存地址的值就未定义了,访问它们是一个错误

int a = 43;

请注意,"scope"不一定指函数。作用域可以嵌套在函数内部,变量仅在声明它的块范围内有效。还要注意,分配此内存的位置未指定(在正常的系统上,它将位于堆栈或寄存器中以进行优化)。

静态内存分配

在编译时分配,静态内存中变量的生命周期是程序的生命周期

在 C 中,使用 static 关键字可以分配静态内存。其作用域仅限于编译单元。

当考虑 extern 关键字时,事情变得更有趣。定义一个 extern 变量时,编译器会为其分配存储空间。当声明一个 extern 变量时,编译器需要在其他地方定义该变量。未声明/定义extern变量将导致链接问题,而未声明/定义static变量将导致编译问题。

在文件范围内,static 关键字是可选的(函数外部):

int a = 32;

但是不适用于函数范围(在函数内部):

static int a = 32;

从技术上讲,externstatic 是 C 中两个不同类别的变量。

extern int a; /* Declaration */
int a; /* Definition */

*静态内存分配注意事项

说静态内存是在编译时分配有点令人困惑,尤其是当我们考虑到编译机器和主机可能不相同,甚至不在同一架构上时。

更好的方式是认为静态内存的分配由编译器处理,而不是在编译时分配。

例如,编译器可能会在编译后的二进制文件中创建一个大的 data 段,在程序加载到内存中时,程序的 data 段中的地址将被用作所分配内存的位置。这种方法的明显缺点是,如果使用了大量的静态内存,编译后的二进制文件会非常大。实际上可以通过不到半打代码生成几个GB的二进制文件。另一种选项是让编译器注入初始化代码,在程序执行之前以某种其他方式分配内存。该代码将根据目标平台和操作系统而异。实际上,现代编译器使用启发式算法来决定使用这些选项中的哪一个。您可以通过编写一个分配10k、1m、10m、100m、1G或10G个静态数组的小C程序来尝试此功能。对于许多编译器,二进制文件的大小将随数组大小线性增长,并且在某个点之后将会再次缩小,因为编译器使用另一种分配策略。

寄存器内存

最后一个内存类是“寄存器”变量。正如所料,寄存器变量应该分配在CPU的寄存器中,但实际上决策权留给了编译器。您不能通过使用地址引用来将寄存器变量转换为引用。

register int meaning = 42;
printf("%p\n",&meaning); /* this is wrong and will fail at compile time. */

大多数现代编译器比你更聪明地选择应该放在寄存器中的变量 :)

参考资料:


4
建议使用int * a = malloc(sizeof(*a));,以避免重复写a的类型。如果a的类型发生变化,这种方式会更加方便。 - Shahbaz
1
实际上它被称为堆,但与堆数据结构无关。在这种情况下,堆指的是一个杂乱的地方。 - dynamic
2
"静态内存分配...是在编译时分配的。" 你的意思是分配大小在编译时被确定吗?难道不是只有在运行时才会设置内存吗? - lf215
嘿,我有一个疑问,如果你还在回复的话 :(。自动内存分配怎么样?编译器是否也会将这些局部变量的地址存储在数据段中,并将其传递给可执行文件。当代码执行(并进入作用域)时,这些地址实际上将被用作已分配内存的位置。或者它是否只在运行时分配,没有任何地址生成和处理由我的编译器完成? - LocalHost
1
@LocalHost 自动变量的作用域限定在定义它们的上下文(花括号)的生命周期内。通常在运行时分配在调用堆栈上,绝对不会存储在数据段中。您可以在此处阅读C18标准:(6.2.4.5-7) https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf - brice

110

内存分配有三种类型——静态、自动和动态。

静态分配意味着在程序启动时为您的变量分配内存。程序创建时大小是固定的。它适用于全局变量、文件范围变量以及函数内定义了 static 限定符的变量。

自动内存分配发生在函数内部定义的(非静态)变量上,并且通常存储在堆栈中(虽然 C 标准并不规定必须使用堆栈)。您不必使用它们来预留额外的内存,但另一方面,对于这些内存的寿命也有限制。例如:函数中的自动变量仅存在于函数完成之前。

void func() {
    int i; /* `i` only exists during `func` */
}

动态内存分配与静态内存分配有所不同,你现在可以控制这些内存位置的确切大小和生命周期。如果你不释放它,就会遇到内存泄漏问题,这可能导致你的应用程序崩溃,因为在某个时刻,系统无法再分配更多的内存。

int* func() {
    int* mem = malloc(1024);
    return mem;
}

int* mem = func(); /* still accessible */

在上面的例子中,即使函数终止,所分配的内存仍然有效和可访问。当您使用完内存后,您必须释放它:

free(mem);

3
你肯定能控制变量的生命周期...因为你是决定作用域的人,对吧? - Luchian Grigore
6
答案有误。您混淆了静态变量和自动变量。 - brice
3
你的句子写道:“静态分配意味着变量的内存是自动分配的”,这是错误的。请参考GNU libc的手册页面,了解更详细的信息。 - brice
1
堆栈分配不是静态的。它在运行时动态发生,并且取决于程序的运行时条件,而不是其静态已知属性(这就是C和编程中静态的含义)。静态分配是编译器可以推断出来的,而不必实际运行程序。我认为你应该重新表述你的答案。 - Eli Bendersky
1
@EliBendersky 现在已经重新表述了,请检查是否正确。 - Suraj Jain
显示剩余8条评论

2

静态内存分配:

  • 变量被永久性地分配
  • 分配在程序执行之前完成
  • 使用称为的数据结构来实现静态分配
  • 效率较低
  • 没有内存可重用性

动态内存分配:

  • 只有程序单元活跃时才会分配变量
  • 在程序执行期间进行分配
  • 使用称为的数据结构来实现动态分配
  • 效率更高
  • 内存具有可重用性。当不需要时可以释放内存

1
"静态内存分配[...]它使用称为堆栈的数据结构来实现静态分配" 不,那是不正确和误导性的。请参见我的帖子以了解自动分配和静态分配之间的区别。静态内存可能使用堆栈。这强烈依赖于实现,并且可以为同一实现使用多个策略。我也不确定您所说的“效率低下”是什么意思。@Trieu Toan,您通过糟糕的编辑更改了此答案的含义。" - brice

2

静态内存分配:编译器为声明的变量分配所需的内存空间。通过使用地址运算符,获取保留的地址并将该地址分配给指针变量。由于大多数声明的变量具有静态内存,因此将指针值分配给指针变量的这种方式称为静态内存分配。内存在编译时分配。

动态内存分配:它使用malloc()或calloc()等函数动态获取内存。如果使用这些函数动态获取内存并将其返回的值分配给指针变量,则此类分配称为动态内存分配。内存在运行时分配。


1

静态内存分配和动态内存分配的区别

在程序执行之前(编译期间)分配内存。
在程序执行期间分配内存。

执行期间不执行任何内存分配或释放操作。
在执行期间建立和销毁内存绑定。

变量保持永久分配。
仅在程序单元处于活动状态时分配。

使用堆栈和堆实现。
使用数据段实现。

需要指针来访问变量。
不需要动态分配的指针。

比动态执行更快。
比静态执行更慢。

需要更多的内存空间。
需要较少的内存空间。


1
静态内存分配是在栈上分配的,而动态内存分配是在堆上分配的。 - Usman Kurd
@UsmanKurd,关于静态内存,你的说法通常是不正确的。请看我的回答。 - brice

0

静态内存分配是在编译时程序执行之前分配内存。 动态内存分配是在运行时程序执行期间分配内存。


-1

静态内存分配。分配的内存将在堆栈中。

int a[10];

动态内存分配。分配的内存将在堆中。

int *a = malloc(sizeof(int) * 10);

而且后者应该免费,因为 C 语言没有垃圾回收器(GC)。

free(a);

当栈上的内存是动态的时,如何在栈上分配静态内存?变量随时可能被“弹出”... - Abdel Aleem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接