C 语言动态分配 和静态分配的区别,以及在函数调用不同的表现
在C语言中,动态分配和静态分配是两种不同的内存分配方式。
-
静态分配:
- 在编译时分配内存空间。
- 内存分配在程序编译时就已经确定,所以内存大小和生命周期都是固定的。
- 静态分配的变量通常是在栈(stack)或者数据段(data segment)中分配的。
- 静态分配的变量在函数调用时,它们的内存空间会在函数调用栈帧中分配,并在函数返回时释放。
-
动态分配:
- 在程序运行时根据需要动态地分配内存空间。
- 内存分配发生在程序运行时,所以内存大小和生命周期可以在运行时动态改变。
- 动态分配的内存通常在堆(heap)中分配。
- 动态分配的内存需要手动分配和释放,分配使用
malloc()
或calloc()
,释放使用free()
函数。 - 动态分配的变量的内存空间在堆上,函数调用时传递的是指针,所以函数调用的开销相对较小。
在函数调用方面的表现:
- 对于静态分配的变量,每次函数调用时,这些变量的内存空间会被分配到调用函数的栈帧中,函数返回时释放。
- 对于动态分配的变量,函数调用时传递的是指针,而不是整个变量,因此函数调用的开销相对较小。此外,动态分配的变量的生命周期可以超出函数的作用域,因为它们不依赖于函数调用栈。
静态分配的示例:
#include <stdio.h>
void staticAllocation() {
int staticVar = 10; // 静态分配的变量
printf("Static variable inside function: %d\n", staticVar);
}
int main() {
staticAllocation(); // 调用函数
// 在这里无法访问 staticVar,因为它的作用域仅限于 staticAllocation 函数内部
return 0;
}
在上面的示例中,staticVar
是静态分配的变量,它在函数调用时分配到调用函数的栈帧中。当函数返回时,staticVar
变量的内存空间被释放。
动态分配的示例:
#include <stdio.h>
#include <stdlib.h>
void dynamicAllocation() {
int *dynamicVar = (int *)malloc(sizeof(int)); // 动态分配的变量
*dynamicVar = 20;
printf("Dynamic variable inside function: %d\n", *dynamicVar);
free(dynamicVar); // 释放动态分配的内存
}
int main() {
dynamicAllocation(); // 调用函数
// 在这里无法访问 dynamicVar,因为它是在 dynamicAllocation 函数中动态分配的,并且在函数返回后被释放
return 0;
}
在上面的示例中,dynamicVar
是动态分配的变量,使用 malloc()
函数在堆上分配内存。在函数调用时,传递的是指向动态分配内存的指针,而不是整个变量。在函数结束后,需要手动释放动态分配的内存空间,以免发生内存泄漏。
这两个示例展示了静态分配和动态分配在函数调用中的不同表现:
让我们用代码来说明动态分配的变量的生命周期可以超出函数的作用域。
#include <stdio.h>
#include <stdlib.h>
int *createDynamicVar() {
int *dynamicVar = (int *)malloc(sizeof(int)); // 动态分配的变量
*dynamicVar = 30;
return dynamicVar;
}
int main() {
int *ptr = createDynamicVar(); // 调用函数创建动态分配的变量
printf("Dynamic variable inside main function: %d\n", *ptr);
// 在此处仍然可以访问 dynamicVar 的值,尽管 createDynamicVar 函数已经返回
printf("Dynamic variable outside function: %d\n", *ptr);
free(ptr); // 释放动态分配的内存
return 0;
}
在上面的示例中,createDynamicVar
函数动态地分配了一个整数,并将其值设为 30,然后返回指向该动态分配内存的指针。在 main
函数中,我们调用 createDynamicVar
函数来创建这个动态分配的变量,并将其指针存储在 ptr
中。即使 createDynamicVar
函数返回后,我们仍然可以通过 ptr
指针来访问该动态分配的变量,因为它的生命周期不仅仅限于 createDynamicVar
函数的作用域。
在程序的末尾,我们使用 free
函数释放了动态分配的内存,这是很重要的,以免造成内存泄漏。
如果这段代码改成静态分配会有什么后果?
如果将上面的代码中的动态分配改为静态分配,会导致动态变量的生命周期与函数的作用域相同。这意味着在函数返回后,该变量将不再可用。这里是修改后的代码:
#include <stdio.h>
int *createStaticVar() {
static int staticVar = 30; // 静态分配的变量
return &staticVar;
}
int main() {
int *ptr = createStaticVar(); // 调用函数创建静态分配的变量
printf("Static variable inside main function: %d\n", *ptr);
// 在此处无法访问 staticVar,因为它是在 createStaticVar 函数内部静态分配的
// printf("Static variable outside function: %d\n", *ptr); // 这行代码会导致编译错误
return 0;
}
在上面的代码中,createStaticVar
函数返回了指向静态分配变量 staticVar
的指针。但是,由于 staticVar
是在 createStaticVar
函数内部静态分配的,其生命周期仅限于 createStaticVar
函数的作用域。因此,当 createStaticVar
函数返回后,staticVar
变量将不再可用。尝试在 main
函数中访问 staticVar
会导致编译错误。
如果是普通分配,会出现指针悬挂:
如果你指的是普通的局部变量(非静态、非动态),并且将其地址返回给调用函数,那么是可能出现指针悬挂的情况。这是因为普通的局部变量在函数返回时会被销毁,其内存空间可能会被重用,因此在函数返回后访问这样的变量可能会导致未定义行为。
以下是一个可能导致指针悬挂的示例:
#include <stdio.h>
int *createLocalVar() {
int localVar = 40; // 普通分配的局部变量
return &localVar; // 返回局部变量的地址
}
int main() {
int *ptr = createLocalVar(); // 调用函数创建普通分配的局部变量
printf("Value pointed by pointer: %d\n", *ptr); // 这里可能会出现指针悬挂问题
return 0;
}
在这个示例中,createLocalVar
函数返回了指向局部变量 localVar
的指针。但是,一旦 createLocalVar
函数返回,localVar
变量的内存空间就会被释放。因此,ptr
指针将指向一个已经销毁的内存区域,这可能导致指针悬挂问题。