目录
一.为什么要动态内存分配
int a=4; 在内存开辟了4个字节的空间
int arr[10]={0}; 在内存开辟了40个字节的空间
这样开辟空间是固定的,无论我们用不用,这4个字节和40个字节的空间已经存在。所以有时候我们想要放更多的数据,空间就不够了,或者数据放完了,空间还没用完,这样就会使内存浪费。
所以我们需要动态内存分配来使空间合理开辟。
二.动态内存分配函数 <stdlib.h>
1.malloc
malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
参数:需要开辟的空间内存大小。
返回类型:void* (具体按照使用者决定)
值得注意的是:
malloc函数如果开辟空间失败会返回NULL指针
2.free
free函数用来释放动态开辟的内存。参数:动态开辟的空间返回类型:void需要注意的是:1.free函数 只能用于动态开辟空间的释放。2.如果参数是 NULL指针,则什么都不发生。3.使用完free函数后,原空间需要置为NULL指针, 避免变成野指针!
int main()
{
int* p = (int*)malloc(4*sizeof(int));
if (p)
{
memset(p, 0, 4 * sizeof(int));
}
else
{
printf("malloc fail\n");
}
free(p);
p = NULL;
return 0;
}
3.realloc
realloc函数用于更改开辟内存的大小。
参数:需要调整的空间地址,改变后的字节大小
返回类型:void*(按使用者决定)
需要注意的是:
1.如果原空间之后有空间能存下realloc的新空间,则在原有空间之后追加空间,数据不变化,返回的是原空间的地址;如果原空间之后没有空间能存下realloc的新空间,则会重新找一片空间,此时返回的是一个新空间的地址。
4.calloc
calloc用于开辟一片空间。
参数:元素个数,开辟字节大小
返回类型:void*(按使用者决定)
与malloc不同的是:
realloc函数在开辟空间后,会初始化内存为0。
calloc
malloc
![]()
三.常见动态内存分配错误
1.对NULL指针解引用
//Err:
void test()
{
int *p = (int*)malloc(sizeof(int)*4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
p=NULL;
}
//Right:
void test()
{
int *p = (int*)malloc(sizeof(int)*4);
if(p)
{
*p = 20;
}
free(p);
p=NULL;
}
2.对动态开辟空间的越界访问
//Err:
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
p=NULL;
}
//Right:
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<10; i++)
{
*(p+i) = i;
}
free(p);
p=NULL;
}
3.对非动态开辟内存使用free释放
//Err:
void test()
{
int a = 10;
int *p = &a;
free(p);
p=NULL;
}
4.使用free释放一块动态开辟内存的一部分
//Err:
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
p=NULL;
}
5.对同一块动态内存多次释放
//Err:
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
6.动态开辟内存忘记释放(内存泄漏)
//Err:
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
//Right:
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
free(p);
p=NULL;
}
int main()
{
test();
while(1);
}
四.柔性数组
1.柔性数组是什么
结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。struct s { int n; int arr[]; //柔性数组 }
2.柔性数组的特点
1.结构中的柔性数组成员前面必须至少一个其他成员。
2.sizeof 返回的这种结构大小不包括柔性数组的内存。3.包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大 小,以适应柔性数组的预期大小。
由于柔性数组的大小是未知的,所以计算大小不包含柔性数组。因为这种特性,所以结构体里必须有一个其他成员,不然系统分配空间时不知道分配多少。
3.柔性数组的使用
我们为柔性数组开辟10个字节的空间,并赋值为'R'
4.柔性数组的优势
我们用另一种方式实现其他类型数据+数组的形式
这里使用的方法同样也能模拟出柔性数组,所以柔性数组的优势在哪呢?
1.方便内存释放
使用柔性数组我们只malloc 1次 free 1次 ,而这个模拟实现malloc 2次,free 2次
2.这样有利于访问速度
我们知道malloc是开辟一片空间,malloc次数越多,开辟的空间也越多,而如果我们开辟的空间没有连续的话,会有许多内存碎片(空间浪费)。
柔性数组的使用,我们malloc的空间是连续的,访问速度也更快
而模拟柔性数组的实现方法,空间可能不连续,效率不及柔性数组。