你好,我是史丰源
欢迎你的来访,希望我的博客能给你带来一些帮助。
我的Gitee: 代码仓库☀️
我的联系方式:
QQ:1756786195
邮箱:Marksky126@outlook.com🌐
动态内存管理
一、为什么存在动态内存分配
当我们所申请的空间不够时,我们就需要动态内存分配
二、动态内存函数的介绍
2.1 malloc(动态开辟空间)和free(释放动态开辟空间的内存)
1.malloc 函数
⚡️举例子:
int* p = (int*)malloc(10*sizeof(int));//动态开辟40个字节的空间
空间开辟成功,malloc返回一个指向开辟好空间的指针。
注意
在使用malloc函数时,一定要进行检查,检查是否开辟失败(返回空指针)
if(p==NULL)//开辟失败
{
perror("malloc");//显示错误信息
return 1;
}
2.free 函数(与malloc函数搭配使用)
⚡️举例子:
int* p = (int*)malloc(10*sizeof(int));//动态开辟40个字节的空间
free(p);
p=NULL;//建议与free配套出现
free函数释放空间后并没有将指针置为空,容易造成内存泄漏的问题。
(内存泄漏 Memory Leak是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。)
提示:1.若free函数释放的是非动态开辟空间,则free的行为是未定义的,如下图所示,程序会进行报错。
2.若free函数释放的对象是空指针,那么函数什么也不做(这点很容易理解,因为没有东西可以释放)
2.2 calloc(动态内存分配并初始化为0)
⚡️举例子:
int *p = (int*)calloc(10,sizeof(int))//开辟10个int型大小的字节并且初始化为0
2.3 realloc(重新分配动态内存大小)
realloc 重新分配的两种情形
1.原空间后面可以容纳重新分配空间的大小
这种情形直接在原空间后面重新分配空间。
2.原空间后面无法容纳重新分配空间的大小
这种情况 原空间内的数据被拷贝进重新分配的空间,并且原空间自动被置空(造成隐患:如果开辟失败(即任何空间都无法容纳重新分配空间的大小),原空间自动被置空,数据会丢失)
所以我们要设置一个临时变量去判断一下realloc是否返回空指针
⚡️举例子:
int *ptr = (int*)realloc(p,80);//p是原空间,ptr是重新分配的空间
if(ptr!=NULL)//如果重新分配空间分配成功
{
p = ptr;//再把重新开辟的空间 放回原空间,即实现了重新分配。
}
四、柔性数组 (Flexible Array)
4.1 柔性数组的介绍
想象一个可压缩又可扩充给的洗车水桶,对的,柔性数组也是这样。
C99中这样介绍柔性数组:结构体中最后一个元素允许是未知大小的数组。(一定要记住:最后一个)
⚡️举例子:
struct foo
{
int i;
char arr[];//柔性数组
};
4.2 柔性数组的特点
1.结构体内必须先包含一个其他成员(即不能只是柔性数组)
2.sizeof返回的这种结构大小不包括柔性数组的内存(柔性数组不占用结构体的size)
3.包含柔性数组成员的结构必须用malloc函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,用来适应柔性数组的预期大小。
(就是在动态开辟空间时尽量开辟多一点)
⚡️举个第2条的例子:
typedef struct foo
{
int i;
int a[];//柔性数组不占用结构体的size
}foo;
printf("%d\n",sizeof(foo));//输出4
4.3 柔性数组的使用
⚡️直接举例子:
#include<stdio.h>
typedef struct S
{
int i;
int arr[];
}S;
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);//动态开辟空间
//后面这个+40是为柔性数组分配的空间
ps->i = 100;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;//将1-9放入放入柔性数组
}
//释放动态开辟的空间
free(ps);
ps = NULL;
return 0;
}
其实更标准的分配空间代码如下
struct *S = (struct S*)malloc(sizof(struct S)+100*sizeof(int));
加号后面就是为柔性数组所开辟的空间。
4.4 柔性数组的优势
写在最后:
阅读陈皓前辈文章得到的启示:
- 变量——内存地址的一个抽象名字 (即变量名在机器中是以地址出现的,机器是不认识我们所创建的变量名的。因为我们所知机器底层汇编代码为二进制文本)
- 不管结构体实例是什么,访问其成员其实就是加成员的偏移量。
- 访问结构体成员数组名其实得到的是数组的相对地址(数组名==数组首元素地址)
- 访问结构体指针得到的是相对地址里的内容(*p,相当于解引用,取到地址里的内容)
- 零长度的数组是存在于结构体内的,但是它不占结构体的size(大小)。
-
大家有兴趣可以阅读一下陈皓前辈写的柔性数组介绍
- C语言结构体里的成员数组和指针-陈皓.
ps:括号里是我的一些见解,如有理解错误,还请大家批评指正,谢谢!
持续学习,保持空杯心态,做有意义的事情!共勉