为什么存在动态内存分配
平时掌握的内存开辟方式有:
int val = 20;
//在栈空间上开辟4个字节
char arr[10]={0};
//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
1.空间开辟大小是固定的。
2.数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但对于空间的需求,不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才知道,那数组的编译时开辟空间的方式就不能满足了,这时候就只能试试动态内存开辟了。
动态内存函数的介绍
使用这些函数,首先要引用这个头文件<stdlib.h>
-
malloc
函数原型:void*malloc(unsigned int size);
// size为所开辟的字节数
功能说明:
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针的首地址。
- 如果辟失败,则返回一个空指针,因此malloc的返回值一定要做检查。
- 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- 如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。
eg.pt=malloc(100);
//开辟100个字节的连续空间.
-
calloc
函数原型:void*calloc(unsigned n,unsigned size);
//n为元素的个数,size为每个元素的字节数
功能说明:
- 函数的功能是为n个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
- 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全零。
eg.p=calloc(50,4);
//开辟50×4个字节的空间,把首地址赋给指针变量p
-
realloc
函数原型:void*realloc(void*p,unsigned int size);
//p为要调整的内存的地址,size为调整之后内存的大小。
功能说明:
有时候我们发现过去申请的空间太小了,有时候我们又觉得申请的空间过大了,为了合理的分配内存,我们会对内存的大小做灵活的调整,那realloc函数就可以做到对动态开辟内存大小的调整(可增加可减少分配的内存)。
- 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
- realloc在调整内存空间是存在两种情况
- 原空间之后有足够大的空间:
- 原空间之后没有足够大的空间:
情况二的扩展方法是在堆空间上另找一个合适大小的连续空间来使用,然后返回这个空间的地址。
使用注意:
要在动态内存开辟后使用(malloc或calloc所开辟的动态内存空间)。
eg.int *ptr = malloc(20);
int *tmp= realloc(ptr,40);
-
free
函数原型:void free (void*ptr);
//ptr为动态内存开辟的空间的地址
功能说明:
- 释放动态开辟的内存。
- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr是空指针,则函数什么都不做。
eg.free(p);
常见的动态内存错误
-
对空指针的解引用操作
-
对动态开辟空间的越界访问
-
对非动态开辟内存的使用free释放
-
使用Free释放一块动态开辟内存的一部分
-
对同一块动态内存多次释放
-
动态开辟内存忘记释放(内存泄漏)
柔性数组
在C99(标准C)中,结构体中最后一个成员元素允许是未知大小的数组,该数组叫柔性数组成员。(通过malloc,realloc变得柔性了)
eg.
srtuct S
{
int i;
int a[];
};