int *arr = malloc(100 * sizeof(int));
- malloc:内存分配函数(Memory ALLOCation)
- 100 * sizeof(int):计算需要的内存大小
- int *arr:声明一个整型指针接收分配的内存地址
- 逐步解析
(1)sizeof(int):计算一个整数占多少字节
在大多数现代系统上,int占4字节
sizeof(int) → 结果为4(2)100 * sizeof(int):计算总需求
需要存储100个整数 → 100 * 4 = 400字节(3)malloc(400):向系统申请内存
系统在堆内存中寻找连续的400字节空闲区域
如果成功,返回这块内存的首地址(比如0x12345678)
如果失败,返回NULL(4)int *arr = ...:指针接收地址
arr现在指向这400字节内存的开始位置
可以像普通数组一样使用arr[0]到arr[99]
- 内存图示
假设分配成功,返回地址0x12345678:
arr → 0x12345678|v[0x12345678] arr[0] (第1个int)[0x1234567C] arr[1] (第2个int,地址+4)...[0x123457E8] arr[99] (第100个int)
- 与普通数组的区别
特性 | 普通数组 int arr[100] | 动态数组 malloc |
---|---|---|
内存区域 | 栈(stack) | 堆(heap) |
大小 | 编译时固定 | 运行时动态决定 |
生命周期 | 随函数结束自动释放 | 需手动free() |
大小修改 | 不可改变 | 可用realloc()调整 |
典型用途 | 已知大小的临时数据 | 需要动态增长/大型数据 |
- 栈与堆的理解
点击查看代码
概念:在C语言中,**栈(Stack)和堆(Heap)**是两种核心的内存分配区域,它们的特性和用途有显著区别一、栈(Stack)
1. 基本特性
分配方式:由编译器自动管理(自动分配/释放)。
生命周期:与函数调用相关。变量在函数结束时自动释放。
大小限制:通常较小(默认约1-8MB,取决于系统)。
访问速度:极快(直接通过指针移动分配内存)。
存储内容:局部变量、函数参数、返回地址等。2. 典型例子
void func() {int a = 10; // 栈上分配char str[100]; // 栈上数组(固定大小)
}
# a和str在func()执行时分配,函数结束自动释放3. 优点
无需手动管理:自动释放,避免内存泄漏。
高速访问:内存连续,CPU缓存友好。4. 缺点
大小固定:无法动态调整(如char str[100]不能扩容)。
栈溢出风险:大数组或递归过深会导致崩溃(如int arr[1000000])。二、堆(Heap)
1. 基本特性
分配方式:手动管理(通过malloc/calloc/realloc申请,free释放)。
生命周期:从分配持续到显式释放(或程序结束)。
大小限制:受系统可用内存限制(通常远大于栈)。
访问速度:稍慢(需通过指针间接访问)。
存储内容:动态数据结构(如链表、动态数组)。2. 典型例子
int *arr = malloc(100 * sizeof(int)); // 堆上动态数组
if (arr == NULL) { /* 处理失败 */ }
free(arr); // 必须手动释放!
# 内存需手动释放,否则会导致内存泄漏。3. 优点
动态灵活:运行时决定大小(如malloc(n))。
大内存支持:适合处理大型数据(如1GB数组)。
全局可用:生命周期不受函数限制。4. 缺点
管理复杂:需手动释放,易出错(泄漏/野指针)。
碎片化风险:频繁分配/释放可能产生内存碎片。常见问题
1. 为什么栈比堆快?
栈通过简单的指针移动分配内存(如ESP寄存器),而堆需要复杂的内存管理算法。2. 栈溢出和堆溢出的区别?
栈溢出:递归太深或局部变量过大(如int arr[1000000])。
堆溢出:持续分配不释放导致内存耗尽(如循环中malloc但无free)。3. 动态内存可以放在栈上吗?
不可以。栈大小需编译时确定,但C99后支持变长数组(VLA),仍在栈上分配
int n = 100;
int arr[n]; // VLA(不推荐,可能引发栈溢出)总结
栈:简单高效,适合小型临时数据,但不够灵活。
堆:灵活强大,适合动态需求,但需谨慎管理。黄金法则:
能用栈优先用栈(安全高效)。
必须动态/大内存时再用堆(记得free!)。
- 为什么需要动态数组?
1.处理未知大小的数据
// 用户输入决定数组大小
int n;
scanf("%d", &n);
int *arr = malloc(n * sizeof(int));
2.避免栈溢出
栈空间有限(通常约1-8MB)
堆空间大得多(通常以GB计)
3.长期保存数据
堆内存的生命周期不受函数限制
- 使用示例
#include <stdio.h>
#include <stdlib.h>int main() {// 动态分配int *arr = malloc(100 * sizeof(int));if (arr == NULL) {printf("内存分配失败!\n");return 1;}// 像普通数组一样使用for (int i = 0; i < 100; i++) {arr[i] = i * 2; // 赋值}// 打印部分值printf("arr[50] = %d\n", arr[50]);// 必须手动释放!free(arr);arr = NULL; // 避免野指针return 0;
}
- 常见错误
1.忘记检查NULL
int *arr = malloc(1000000000); // 可能失败
arr[0] = 1; // 如果arr是NULL会崩溃
2.忘记释放内存(内存泄漏)
void func() {int *arr = malloc(100);// 忘记free(arr)
} // 每次调用func()都会泄漏100字节
3.访问已释放的内存
free(arr);
arr[0] = 1; // 危险!已释放的内存
- 动态调整大小(realloc)
int *new_arr = realloc(arr, 200 * sizeof(int));
if (new_arr ! = NULL) {arr = new_arr; // 现在有200个元素的空间
} else {// 处理分配失败(原数据仍在arr中)
}
- 总结
- malloc在堆上分配内存,返回起始地址
- 必须用指针接收返回值
- 需要手动计算字节数(数量 × sizeof(类型))
- 必须检查返回值是否为NULL
- 必须配对使用free()释放内存
- 动态数组突破了栈空间的限制,是C语言灵活性的核心体现