C语言--动态内存管理

目录

一,动态内存的作用

二,malloc和free

1,malloc函数

2,free函数

三,calloc和realloc

1,calloc函数

2,realloc函数

四,常见动态内存错误分析

1,没有对malloc的返回值进行检查

​编辑

2,对动态开辟空间的越界访问

3,对于非动态内存开辟使用free释放

4,对动态内存的一部分使用free

5,动态内存开辟忘记释放(内存泄漏)

五,题目案例分析

1,例题分析1:

2,例题分析2:

3,例题分析3:

六,柔性数组

1,了解柔性数组

2,柔性数组的特点

3,柔性数组的使用

七,补充内容

1,perror函数

2,exit函数


一,动态内存的作用

创建变量开辟空间:

int a = 0; //开辟4个字节

char arr[10] = {0}; //开辟10个字节

像这样开辟的内存空间是固定的,是不可调整的。数组变量在创建时需要指定大小,一旦指定之后就不可以被修改。

但是有时候我们需要自己自由创建空间,就引出了动态内存,可以由程序员自己创建空间和释放空间,比较灵活

补充:动态开辟的函数都放在堆区

二,malloc和free

1,malloc函数

作用:用来动态内存开辟

头文件:#include <stdlib.h>

语法:void* malloc(size_t size);

size为申请空间的大小,单位为字节,类型为无符号整型(size_t)

返回值为void*表示:函数返回一个申请到的空间的地址,void表示不知道是什么类型,需要在使用时由使用者自己决定。

如果开辟成功,则返回一个开辟好空间的指针

如果开辟失败,则返回NULL指针,由此一定要对malloc开辟的返回值进行检查:if判断或者assert(p!=NULL)

特殊情况:如果size的大小为0,则属于标准未定义,编译器进行提示。

运用举例:

2,free函数

核心功能:专门用来释放和回收动态内存函数开辟的空间。

头文件:#inclde <stdlib.h>

语法:void free(void * ptr);

使用细节:每使用一次malloc函数或者calloc函数,就要使用free函数来释放内存。

特殊情况:

1,如果ptr不是动态内存开辟的空间,则free函数的行为是未定义的。

2,如果ptr是NULL指针,则函数什么也不做。

上图表示:先释放掉p中的内容,再将p定义为空指针。

三,calloc和realloc

1,calloc函数

作用:同样用来动态内存分配

头文件:#include <stdlib.h>

语法:void* calloc(size_t num,size_t size);

把num个size大小的元素,开辟成一块空间,并把每个空间的每个字节初始化为0

与malloc的唯一区别为:会把开辟的空间的字节初始化为0

运用实例:

由上图可知:开辟的空间都被初始化为了0

2,realloc函数

作用:让动态内存管理更加灵活

头文件:#include <stdlib.h>

语法:void* realloc(void* ptr,size_t size);

ptr是要调整的内存的起始地址

size是调整后的内存的大小

返回值为调整之后的内存的起始地址

内存中的原有储存的值不变

realloc在调整空间时,所遇到的两种情况:

第一种:原有空间之后有足够大的空间满足调整之后的大小。

第二种:原有空间之后没有足够大的空间满足调整之后的大小,此时会将原来的空间释放,重新开辟一块足够大的新空间,并将原来储存的值拷贝到新空间内,此时起始地址改变,返回的是一个新的地址。

特殊情况:调整失败,返回的是NULL

运用展示:

1,

2,

3,

四,常见动态内存错误分析

1,没有对malloc的返回值进行检查

2,对动态开辟空间的越界访问

3,对于非动态内存开辟使用free释放

free只能用于动态内存开辟的函数,对于非动态内存的函数开辟的空间会报错

4,对动态内存的一部分使用free

5,动态内存开辟忘记释放(内存泄漏)

注意使用动态开辟函数开辟空间在最后一定要使用free进行释放。

通常满足使用一次malloc / calloc后就要使用一次free(成对出现)

特殊情况:使用了free但没效果。

例如:

此时虽然成对出现了,但是依旧出错,就是由于没有执行到free部分。

五,题目案例分析

1,例题分析1:

分析错误:

1,没有使用free进行内存的释放

2,传入p的是NULL,开辟一个新空间,但p一出函数就销毁,str最终没有得到开辟的空间,依旧是空指针。

修改后:

2,例题分析2:

分析错误:

1,p返回的是数组首元素的地址,但是在GetMemory函数执行完后,p就销毁了,p中的内容就还给了操作系统了,但str依旧得到了地址,里面的内容无法访问,强制使用str访问就会导致出现非法访问。

此时的str是野指针(指向的空间已经被释放)。

3,例题分析3:

六,柔性数组

1,了解柔性数组

当结构体的最后一位成员是一个大小未知的数组,这个结构成员就是柔性数组。

struct S

{

int i;

char k;

int arr[]; //或者int arr[0] //柔性数组

}

2,柔性数组的特点

1,结构体的柔性数组成员前面必须要有其他的成员。

2,使用sizeof计算结构体大小时,柔性数组的大小不计入其中。

3,柔性数组的使用

通过malloc和realloc实现了对柔性数组空间的任意分配,这也是柔性数组柔性的原因。

另一种写法:

比较这两种写法:

第一种写法相比较于第二种写法来说有两种好处:

1,第一种写法由于只使用了一次malloc,所以只需要使用一次free就可以将使用的内存释放掉。

2,第一种写法开辟的空间是连续的,而连续的空间有利于提高访问速度,减少内存碎片(内存中不同空间之间的空隙)。

七,补充内容

1,perror函数

perror是一个错误处理函数,会将当前的代码错误转化为可读的错误信息,并输出。

语法:void perror(const char* s);

s为一个自定义字符串,会在错误信息前输出

输出格式:s:错误信息

展示:

其中INT_MAX为2147483647

此时开辟的空间过大,开辟失败

2,exit函数

核心功能:用于立即终止程序

头文件:#include <stdlib.h>

语法:void exit(int status);

status:程序退出状态码

当为0或EXIT_SUCCESS时:表示程序正常退出

当为非零整数或EXIT_FAILURE时:表示程序异常退出

EXIT_FAILURE是一个标准的宏(通常值为1),它代表"程序异常终止"。

3,程序的内存区域划分

相关推荐
Z9fish2 小时前
C语言算法专题总结(一)排序
c语言·算法·排序算法
wjs20243 小时前
CSS 颜色
开发语言
Felven3 小时前
B. Roof Construction
c语言
无巧不成书02183 小时前
Java数值字面量速查表
java·开发语言·python·开发者·字面量
小鸡吃米…3 小时前
测试线程应用程序
开发语言·python
python开发笔记3 小时前
python(79) 底层代码追踪工具
开发语言·python
kgduu3 小时前
js之错误处理
开发语言·前端·javascript
Bert.Cai3 小时前
Python函数的定义与调用
开发语言·python
美式请加冰3 小时前
模拟的介绍和使用
java·开发语言·算法
无限进步_3 小时前
深入解析vector:一个完整的C++动态数组实现
c语言·开发语言·c++·windows·git·github·visual studio