我想知道静态内存分配和动态内存分配有什么区别?
你能用任何例子解释一下吗?
这是一个标准的面试问题:
动态内存分配
是在运行时使用 calloc()
、malloc()
和朋友分配的内存。它有时也称为“堆”内存,尽管它与堆数据结构 ref 无关。
int * a = malloc(sizeof(int));
在调用 free()
之前,堆内存是持久的。换句话说,您控制变量的生命周期。
自动内存分配
这就是通常所说的“堆栈”内存,在您进入新范围时分配(通常是在调用堆栈上推送新函数时)。一旦你移出范围,自动内存地址的值是未定义的,它是一个error to access them。
int a = 43;
请注意,范围并不一定意味着功能。作用域可以嵌套在一个函数中,并且该变量将仅在它被声明的块中的作用域内。另请注意,未指定分配此内存的位置。 (在一个健全的系统上,它将在堆栈上,或注册进行优化)
静态内存分配
在编译时分配*,变量在静态内存中的生命周期是lifetime of the program。
在 C 中,可以使用 static
关键字分配静态内存。范围只是编译单元。
事情变得更有趣了when the extern
keyword is considered。当 extern
变量被定义时,编译器会为其分配内存。当 extern
变量声明时,编译器要求该变量在其他地方定义。未能声明/定义 extern
变量将导致链接问题,而未能声明/定义 static
变量将导致编译问题。
在文件范围内,静态关键字是可选的(在函数之外):
int a = 32;
但不在函数范围内(函数内部):
static int a = 32;
从技术上讲,extern
和 static
是 C 中两个独立的变量类。
extern int a; /* Declaration */
int a; /* Definition */
*关于静态内存分配的注意事项
说静态内存是在编译时分配的,这有点令人困惑,特别是如果我们开始考虑编译机和主机可能不一样,甚至可能不在同一个架构上。
最好认为静态内存的分配是由编译器处理的,而不是在编译时分配的。
例如,编译器可能会在编译后的二进制文件中创建一个大的 data
段,当程序加载到内存中时,程序的 data
段内的地址将用作分配内存的位置。如果使用大量静态内存,这具有使编译的二进制文件非常大的明显缺点。可以编写由不到六行代码生成的数 GB 二进制文件。另一种选择是让编译器注入初始化代码,该代码将在程序执行之前以其他方式分配内存。此代码将根据目标平台和操作系统而有所不同。在实践中,现代编译器使用启发式方法来决定使用哪些选项。您可以自己尝试通过编写一个小的 C 程序来分配一个包含 10k、1m、10m、100m、1G 或 10G 项的大型静态数组。对于许多编译器来说,二进制大小会随着数组的大小线性增长,超过某个点,随着编译器使用另一种分配策略,它会再次缩小。
注册内存
最后一个内存类是“寄存器”变量。正如预期的那样,寄存器变量应该分配在 CPU 的寄存器上,但实际上由编译器决定。您不能使用 address-of 将寄存器变量转换为引用。
register int meaning = 42;
printf("%p\n",&meaning); /* this is wrong and will fail at compile time. */
大多数现代编译器在选择哪些变量应该放在寄存器中时比你聪明:)
参考:
libc 手册
K&R 的 C 编程语言,附录 A,第 4.1 节,“存储类”。 (PDF)
C11 标准,第 5.1.2、6.2.2.3 节
维基百科也有关于静态内存分配、动态内存分配和自动内存分配的好页面
维基百科上的 C 动态内存分配页面
这个内存管理参考有更多关于动态分配器底层实现的细节。
有三种类型的分配——静态的、自动的和动态的。
静态分配意味着,在程序启动时为变量分配内存。创建程序时,大小是固定的。它适用于在函数内部定义的全局变量、文件范围变量和使用 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);
静态内存分配:编译器为声明的变量分配所需的内存空间。通过操作符的地址,得到保留地址,这个地址可以分配给指针变量。由于大部分声明的变量都有静态内存,这将指针值分配给指针变量的方法称为静态内存分配。内存是在编译期间分配的。
动态内存分配:它使用malloc()或calloc()等函数动态获取内存。如果这些函数用于动态获取内存,并且这些函数返回的值被赋值给指针变量,这种赋值称为动态内存allocation.memory 在运行时被分配。
静态内存分配:
变量被永久分配
分配在程序执行之前完成
它使用称为堆栈的数据结构来实现静态分配
效率较低
没有内存可重用性
动态内存分配:
仅当程序单元激活时才分配变量
分配是在程序执行期间完成的
它使用称为堆的数据结构来实现动态分配
更高效
有内存可重用性。不需要时可以释放内存
静态内存分配和动态内存分配的区别
在程序开始执行之前(编译期间)分配内存。内存是在程序执行期间分配的。
执行期间不执行内存分配或释放操作。内存绑定在执行期间建立和销毁。
变量保持永久分配。仅在程序单元处于活动状态时分配。
使用堆栈和堆实现。使用数据段实现。
访问变量需要指针。不需要动态分配的指针。
比动态执行更快。执行比静态慢。
需要更多内存空间。所需的内存空间更少。
静态内存分配是在编译期间执行 pf 程序之前分配的内存。动态内存分配是在运行时执行程序期间分配的内存。
静态内存分配。分配的内存将在堆栈中。
int a[10];
动态内存分配。分配的内存将在堆中。
int *a = malloc(sizeof(int) * 10);
并且后者应该被释放,因为 C 中没有垃圾收集器(GC)。
free(a);
不定期副业成功案例分享
int * a = malloc(sizeof(*a));
,以避免重复a
的类型。如果a
的类型发生变化,这会使事情变得更容易。