面试题答案
一键面试一维数组内存布局特点
- 连续存储:一维数组在内存中是连续存储的,数组元素按照顺序依次排列。例如,定义
int arr[5];
,这 5 个int
类型的元素会在内存中占据连续的一段空间,每个元素的大小为sizeof(int)
。 - 静态分配:如果数组是在函数内部定义为局部变量(非
static
),则它存储在栈区。若定义为全局变量或static
局部变量,存储在静态数据区。例如:
int globalArr[10]; // 全局变量,在静态数据区
void func() {
static int staticLocalArr[5]; // static局部变量,在静态数据区
int localArr[3]; // 局部变量,在栈区
}
指针内存布局特点
- 存储地址:指针本身是一个变量,它存储的是另一个变量(或数组、函数等)的内存地址。指针变量本身也占据一定的内存空间,其大小取决于系统的寻址能力,在 32 位系统中通常为 4 字节,64 位系统中通常为 8 字节。例如,定义
int *ptr;
,ptr
变量会在栈区(若为局部指针变量)或静态数据区(若为全局或static
局部指针变量)占据相应大小的空间来存储地址。 - 动态指向:指针可以指向不同的内存位置,通过改变指针所存储的地址来实现。例如:
int num = 10;
int *ptr = #
int arr[5];
ptr = arr; // 指针从指向num变为指向数组arr的首地址
生命周期管理以避免内存泄漏和悬空指针问题
- 栈上数组:对于在栈上分配的一维数组(局部非
static
数组),当函数结束时,数组占用的栈空间会自动释放,无需手动干预。但要注意数组的作用域仅限于函数内部。 - 静态数组:全局数组和
static
局部数组在程序启动时分配内存,在程序结束时释放内存,也无需手动管理内存释放。 - 指针与动态内存分配:
- 避免内存泄漏:当使用
malloc
等函数动态分配内存给指针时,一定要记得使用free
函数释放内存。例如:
- 避免内存泄漏:当使用
int *dynamicArr = (int *)malloc(10 * sizeof(int));
if (dynamicArr == NULL) {
// 处理内存分配失败的情况
return;
}
// 使用dynamicArr
free(dynamicArr);
dynamicArr = NULL; // 防止悬空指针
- 避免悬空指针:在释放内存后,立即将指针赋值为
NULL
。这样,后续如果不小心再次使用该指针,由于NULL
指针不能正常解引用,程序会在解引用NULL
指针时崩溃,而不是访问已释放的内存(悬空指针指向的内存)导致未定义行为。
结合一维数组和指针进行正确内存管理(动态分配情况)
- 模拟动态数组:可以通过指针和
malloc
来模拟动态一维数组。例如:
int *dynamicArray = (int *)malloc(n * sizeof(int));
if (dynamicArray == NULL) {
// 处理内存分配失败
return;
}
// 像使用数组一样使用指针
for (int i = 0; i < n; i++) {
dynamicArray[i] = i;
}
// 使用完后释放内存
free(dynamicArray);
dynamicArray = NULL;
- 传递动态数组:在函数间传递动态分配的数组(通过指针)时,要注意在合适的地方释放内存。例如:
void processArray(int *arr, int size) {
// 处理数组
for (int i = 0; i < size; i++) {
arr[i] *= 2;
}
}
int main() {
int *dynamicArr = (int *)malloc(5 * sizeof(int));
if (dynamicArr == NULL) {
return 1;
}
processArray(dynamicArr, 5);
// 使用完后释放内存
free(dynamicArr);
dynamicArr = NULL;
return 0;
}
通过以上方式,可以有效地管理一维数组和指针的生命周期,避免内存泄漏和悬空指针问题。