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,程序的内存区域划分

相关推荐
Once_day3 小时前
C++之《程序员自我修养》读书总结(5)
c语言·c++·编译和链接·程序员自我修养
Albert Edison7 小时前
【Python】学生管理系统
开发语言·数据库·python
会周易的程序员10 小时前
cNetgate物联网网关内存数据表和数据视图模块架构
c语言·c++·物联网·架构·lua·iot
爱编码的小八嘎10 小时前
第3章 Windows运行机理-3.1 内核分析(6)
c语言
宇木灵10 小时前
C语言基础-十、文件操作
c语言·开发语言·学习
云泽80811 小时前
C++ 多态入门:虚函数、重写、虚析构及 override/final 实战指南(附腾讯面试题)
开发语言·c++
yanghuashuiyue12 小时前
lambda+sealed+record
java·开发语言
yzx99101312 小时前
Python数据结构入门指南:从基础到实践
开发语言·数据结构·python
衍生星球13 小时前
【JSP程序设计】Servlet对象 — page对象
java·开发语言·servlet·jsp·jsp程序设计