C语言动态内存管理
1.为什么要有动态内存
以下又两种内存开辟的方式
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
0};//在栈空间上开辟10个字节的连续空间
特点:
- 空间开辟大小是固定的。
- 数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能调整。
但有时候程序在运行的时候才知道所需空间的大小,所以引用了动态内存,可以让程序员自己灵活地申请和释放空间。
2.malloc和free
malloc和free都声明在 stdlib.h
头文件中。
2.1malloc
C语言提供了⼀个动态内存开辟的函数:
void* malloc (size_t size);
函数向内存申请一块连续的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回⼀个指向开辟好空间的指针
- 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = NULL;
ptr = (int*)malloc(10 * sizeof(int));
if (ptr == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(ptr + i) = i + 1;
}
for (i = 0; i < 10; i++)
{
printf("% d", *(ptr + i));
}
free(ptr);
ptr == NULL;
return 0;
}
2.2free
C语言提供了另外⼀个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free用来释放动态开辟的内存。
- 如果参数
ptr
指向的空间不是动态开辟的,那free函数的行为是未定义的。 - 如果参数
ptr
是NULL指针,则函数什么事都不做。
3.calloc和realloc
3.1calloc
C语⾔还提供了⼀个函数叫 calloc
, calloc
函数也⽤来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
- 函数的功能是为 num 个大小为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
- 与函数 malloc 的区别只在于
calloc
会在返回地址之前把申请的空间的每个字节初始化为全0。
3.2realloc
有了realloc可以让内存的开辟更加灵活。
函数原型如下:
void* realloc (void* ptr, size_t size);
ptr
是要调整的内存地址- size 调整之后新大小
- 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
- 还会存在两种情况,原地扩容和异地扩容。
4.柔性数组
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员。
4.1柔性数组的特点
- 结构中的柔性数组成员前面必须至少一个其他成员。
sizeof
返回的这种结构大小不包括柔性数组的内存。- 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该⼤于结构的大小,以适应柔性数组的预期大小。
4.2柔性数组的使用
#include<stdio.h>
#include<stdlib.h>
struct S
{
int n;
int arr[];
};
int main()
{
struct S* str = (struct S*)malloc(sizeof(struct S) + 20);
int i = 0;
str->n = 100;
for(i = 0; i < 5; i++)
{
str->arr[i] = i + 1;
printf("%d", str->arr[i]);
}
free(str);
return 0;
}
4.3柔性数组的优点
第一个好处是:方便内存释放
第⼆个好处是:这样有利于访问速度。
连续的内存有益于提高访问速度,也有益于减少内存碎片。