目录
一、为什么存在动态内存分配
我们可以这样开辟空间:int arr[5] = {0};
这样开辟的空间有局限性:开辟大小固定,并且开辟时必须指定数组长度。这样会有的时候浪费内存,有的时候不够存放,很不方便。于是就有了动态内存管理。
※浅浅介绍一下电脑中内存的分配:分为栈区(存放局部变量,函数的形式参数);堆区(malloc/free calloc realloc 动态内存分配);静态区(全局变量 静态变量)
二、动态内存函数的介绍
2.1 malloc / free
传统申请内存方式:
int main()
{
int arr1[10]; //申请40个字节
char arr2[40]; //40个字节
return 0;
}
传统的存在局限性。用malloc函数可以开辟动态内存,那么就让我们来了解一下malloc的用法:
malloc——申请内存块,传入大小,返回void*的指针,指向开辟的空间。
释放malloc申请的内存空间是用函数free:
free可以把申请的空间还给操作系统。
使用示例如下:
int main()
{
int arr1[10]; //申请40个字节
char arr2[40]; //40个字节
//申请空间
int* ptr = (int*)malloc(40); //等同于 int ptr[10];
int* p = ptr; //一般不动申请的起始地址,会用别的变量表示然后动别的变量,所以这里不动ptr,动p
if(p == NULL) //如果申请失败,则报错
{
perror("malloc");
return 1;
}
for(int i = 0; i<10; i++) //将开辟的空间初始化为0 1 2 3 4 5 6 7 8 9
{
*p = i;
p++;
}
//释放空间
free(ptr);
ptr = NULL; //不一定需要,但这样更安全
return 0;
}
如果malloc之后不free(只申请空间不释放空间):1.如果程序结束,动态申请的内存由操作系统自动回收 2.如果程序不结束,动态内存是不会自动回收的,就会形成内存泄漏的问题。所以记得malloc之后要free!
2.2 calloc
也是一种动态内存分配的函数。
malloc函数申请空间的初始化是随机值,calloc申请空间的初始化时0。
例:
int main()
{
int* p = calloc(10, sizeof(int));
if(p == NULL) //如果失败,显示失败原因
{
perror("calloc");
return 1;
}
//申请成功,使用
for(int i = 0; i<10; i++)
{
printf("%d ", *(p+i));
}
free(p);
p = NULL;
return 0;
}
2.3 realloc
重新开辟。当我们觉得空间不够了,可以用realloc再开辟的大一些。(realloc可扩大也可以缩小)
int main()
{
int* p = (int*)malloc(40);
if(p == NULL) //如果失败,显示失败原因
{
perror("malloc");
return 1;
}
//申请成功,使用
for(int i = 0; i<10; i++)
{
*(p+i) = i; //初始化为0 1 2 3 4 5 6 7 8 9
}
//空间不够,希望放20个元素,考虑扩容
int* ptr = (int*)realloc(p, 80); //如果扩容失败会返回NULL,所以不能直接赋值给p
if(ptr!=NULL)
{
p = ptr;
}
//扩容成功了,开始使用
//此处省略使用的代码
free(p);
p = NULL;
return 0;
}
三、常见的动态内存的错误
3.1 对NULL指针的解引用操作
解决办法:对malloc函数的返回值进行判空操作。
3.2 对动态内存开辟空间的越界访问
解决方法:写代码的时候注意边界。
3.3 对非动态开辟的内存使用free释放
局部变量不需要我们释放。
解决方法:写代码的时候小心点。
3.4 使用free释放一块动态开辟内存的一部分
解决方法:记住只能释放起始位置!
3.5 对同一块动态内存多次释放
解决方法:多动脑子记住你有没有释放。
3.6 动态开辟内存忘记释放(内存泄漏)
解决方法:记得释放!
四、C/C++程序的内存开辟(简略介绍)
内存中其实不止有栈区、堆区、静态区(数据段),还有内核空间(用户代码不可读写)、内存映射段、代码段(存放可执行代码的二进制指令/只读常量)
五、柔性数组
结构体中最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。
struct S
{
int num;
double d;
int arr[]; //柔性数组成员
};
struct S2
{
int num;
double d;
int arr[0]; //柔性数组成员
};
5.1 柔性数组的特点
- 柔性数组前面必须至少一个其他成员
- sizeof返回的这种结构大小不包括柔性数组的内存
- 包含柔性数组成员的结构用malloc进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
5.2 使用示例
struct S3
{
int num;
int arr[];
};
int main()
{
struct S3* ps = (struct S3*)malloc(sizeof(struct S3)+40);
if(ps = NULL)
{
perror("malloc\n");
return 1;
}
ps->num = 100;
int i = 0;
for(i = 0; i<10; i++)
{
ps->arr[i] = i;
}
for(i = 0; i<10; i++)
{
printf("%d ", ps->arr[i]);
}
//如果要扩容
struct S3* ptr = (struct S3*)realloc(ps, sizeof(struct S3)+80);
if(ptr == NULL)
{
perror("realloc\n");
return 1;
}
else
{
ps = ptr;
}
free(ps);
ps = NULL;
return 0;
}
5.3 柔性数组的优势
1.方便内存释放(一次释放即可)
2.有利于访问速度,有益于减少内存碎片
这就是有关动态内存管理的内容啦~欢迎交流!