动态内存方面

使用动态内存:

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 字节的连续内存,初始化为 0int *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 指向的数据类型,无法计算偏移
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值