使用动态内存:
1.堆区空间大;
2.内存容量可以进行动态变化修改
C 语言通过标准库 <stdlib.h> 提供 4 个核心函数,需严格遵循 “申请后必须手动释放” 的规则,否则会导致内存泄漏。
| 函数名 | 功能描述 | 示例代码 |
|---|---|---|
malloc(size_t n) | 分配 n 字节的连续内存,不初始化(内存内容为随机值) | int *p = (int*)malloc(5 * sizeof(int)); // 分配 5 个 int 的连续空间 |
calloc(size_t num, size_t size) | 分配 num * size 字节的连续内存,初始化为 0 | int *p = (int*)calloc(5, sizeof(int)); // 5 个 int,初始值均为 0 |
realloc(void *p, size_t new_size) | 调整已分配内存 p 的大小为 new_size,可能移动内存 | p = (int*)realloc(p, 10 * sizeof(int)); // 扩容为 10 个 int |
free(void *p) | 释放 p 指向的动态内存(仅堆内存,不可释放栈内存) | free(p); p = NULL; // 释放后指针置空,避免野指针 |
关键注意事项:
- 类型转换:
malloc/calloc/realloc返回void*,需在前面强制转换为目标数据类型(如(int*))。 - 内存检查:分配可能失败(如堆内存不足),需判断返回值是否为
NULL:c
int *p = (int*)malloc(5 * sizeof(int)); if (p == NULL) { // 检查分配是否成功 printf("内存分配失败\n"); exit(1); // 终止程序 } - 避免野指针:
free(p)后,p仍指向原内存地址(但内存已无效),需手动置为NULL。 - 禁止重复释放:同一指针不可多次
free,否则会导致 “双重释放”(程序崩溃)。
申请内存时会有两部分,指针头【里面会有你申请的大小数字 还有 魔术字(检查数据是否正常的)】 和 你所申请的内存空间(malloc返回的地方)
// int* arr = (int*)malloc(25 * sizeof(int)); 栈区arr保存堆区地址空间 (25可以换)
malloc申请的没有初始化,calloc申请的有初始化
可以通过 realloc 函数实现 扩容和缩容操作
memset 是 C 语言标准库 <string.h> 中的一个函数,用于将一块内存区域的每个字节都设置为指定的值。
void *memset(void *ptr, int value, size_t num);
- 参数说明:
ptr:指向要填充的内存区域的起始地址(可以是任意类型的指针,如int*、char*等)。value:要设置的值(虽然声明为int,但实际只使用其低 8 位,即0~255的范围)。num:要设置的字节数(注意不是元素个数)。
例: memset(arr, 0, sizeof(arr)); 将整数数组的所有字节设置为0(初始化数组)
memset + malloc = realloc 效果
2. realloc 实现扩容原理:
a) 原内存后存在连续空间足以进行容量增加,原空间后直接扩容
b) 原内存后 不存在足够空间 进行容量增加,重新在新空闲内存区域找到足够大的空间足以容纳新容量,将原空间数据拷贝到新内存中,释放原空间
c) 没有足够空间容纳新容量,此时扩容失败(realloc返回值为NULL)
int* tem = realloc(arr,2*10*sizeof(int)); 后面是想要的新内存大小,重新定义一个新指针指
if(tem==NULL)
{
printf("失败");
free(p);
p=NULL;
return 0;
}
else
p=tem; 继续用原指针
free(p);
p=NULL;
free出现崩溃的原因:
一、释放非动态分配的内存
free 只能释放堆上动态分配的内存(通过 malloc/calloc/realloc 分配),若释放栈内存(如局部变量)或其他非法地址,会直接崩溃。
c
// 错误示例:释放栈内存
int main() {
int a;
free(&a); // 崩溃!&a是栈内存地址,非堆内存
return 0;
}
二、重复释放内存(双重释放)
同一内存块被释放两次或多次,会破坏堆内存管理结构,导致崩溃。
c
// 错误示例:重复释放
int main() {
int *p = (int*)malloc(10);
free(p);
free(p); // 崩溃!重复释放同一指针
return 0;
}
注意:即使指针被重新赋值,只要指向的内存已被释放,再次释放仍会崩溃:
c
int *p = (int*)malloc(10);
int *q = p;
free(p);
free(q); // 崩溃!p和q指向同一块已释放的内存
三、释放野指针
指针指向的内存已被释放,且未置空(成为野指针),后续再次释放该指针会崩溃。
c
// 错误示例:释放野指针
int main() {
int *p = (int*)malloc(10);
free(p); // p成为野指针(指向已释放的内存)
// 未将p置空,后续误操作
free(p); // 崩溃!释放野指针
return 0;
}
解决方案:释放内存后立即将指针置空(p = NULL),free(NULL) 是安全的(不会做任何操作)。
四、释放指针指向的不是内存块起始地址
free 要求指针必须指向动态内存块的起始地址(即 malloc/calloc/realloc 返回的地址)。若指针被偏移后释放,会崩溃。
c
// 错误示例:释放偏移后的指针
int main() {
int *p = (int*)malloc(10 * sizeof(int));
int *q = p + 1; // 指针偏移(指向第二个元素)
free(q); // 崩溃!q不是内存块的起始地址
return 0;
}
void*指针
1.无法直接通过它访问所指向的数据(不能解引用或进行指针运算)。
2.通用指针:任何类型的指针都可以隐式转换为 void*,无需强制类型转换;反之,void* 转换为其他类型指针时,必须显式强制转换。
c
int a = 5;
int *p_int = &a;
void *p_void = p_int; // 隐式转换(安全)
// void* 转换为其他类型需强制转换
int *p_int2 = (int*)p_void; // 显式转换
*p_int2 = 10; // 正确:现在知道指向 int 类型
3.有限的指针运算:void* 不能直接进行算术运算(如 p_void + 1),因为编译器不知道指针指向的数据大小(无法确定偏移量)。
c
void *ptr = malloc(100);
ptr++; // 编译报错!不知道 ptr 指向的数据类型,无法计算偏移
2万+

被折叠的 条评论
为什么被折叠?



