目录
首先我们得知道关于动态开辟的一些函数,malloc,calloc,reallo,free这四个函数。
1.malloc函数
void* malloc(size_t size)
这个函数是向内存申请一块连续可用的空间,并返回指向这块空间的指针
如果开辟成功,则返回开辟空间的首地址
如果开辟失败,则返回NULL指针,所以malloc的返回值一定要检查
返回值是void*的类型,malloc函数并不知道要开辟的类型,所以需要使用时自己去定
如果参数size为0,malloc的行为是标准未定义的,取决于编译器
malloc函数的具体使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(40);//1
if (p == NULL)//2
{
return 1;
}
free(p);//3
return 0;
}
这段代码首先第一行(后面备注的1)开辟空间开辟40个字节并强转为int*赋给p,这里40,int*,都是可以改的,第一你想开辟多少个就多少个,并且想转化成什么类型的指针就什么类型
这段代码首先第二行(后面备注的2)这里的判断是非常必要的,这个上面也说了
这段代码首先第三行(后面备注的3)这里是释放内存,在后面会说
2.calloc函数
void* calloc(size_t num,size_t size)
函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0
2.calloc函数使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(10,4);
if (p == NULL)
{
return 1;
}
free(p);
return 0;
}
这个函数其实和malloc函数差不多,所以只要理解malloc函数就会了这个函数
3.realloc函数
realloc函数的出现让动态内存管理更加灵活
有时我们会发现过去申请的空间太小了,有时候我们又觉的申请的空间过大了,那为了合理的申请空间,我们一定会对内存大大小做灵活的调整,那realloc函数就可以做到对动态开辟内存大小的调整
函数原型
void* realloc(void* ptr,size_t size)
ptr是要调整的内存的大小
size是调整后的大小
返回值为调整后的内存的起始位置
realloc的使用
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 1;
}
int* ptr = realloc(p, 80);
if (ptr != NULL)
{
p = ptr;
}
free(p);
return 0;
}
4.free函数
void free(void* ptr)
这个函数可以说你用上面3个函数就要搭配这个函数
这个函数的作用是释放内存,当然这里的内存,是指动态开辟的内存,如果不释放就会造成内存泄漏,所以一定要释放。
这个函数的使用非常简单,就不多介绍了
动态内存常见错误
1.对NULL指针的解引用操作
void test()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;
free(P);
}
这里p的值是NULL,就会存在问题
2.对动态开辟空间的越界访问
void test()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 1;
}
for (int i = 0; i <= 10; i++)
{
*(p + i) = i;
}
free(p);
}
这里i=10,就越界访问
3.对非动态开辟内存使用free释放
void test()
{
int a = 10;
int* p = &a;
free(p);
}
这里p不再是指向动态内存起始位置
4.使用free释放一块动态内存开辟内存的一部分
void test()
{
int* p = (int*)malloc(40);
p++;
free(P);
}
这里p不在指向开辟内存的起始位置,不能部分释放
5.重复释放
void test()
{
int* p = (int*)malloc(40);
free(p);
free(P);
}
这里对p重复释放,这也是不行的
6.动态开辟内存忘记释放(内存泄漏)
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 1;
}
return 0;
}
这里没有对这样造成内存泄漏
这就是常见的六中错误
接下来要补充一个叫柔性数组
柔性数组
柔性数组这个东西也许你没听过,但它却是存在的,他是结构的最后的一个元素允许是未知大小的数组,这就柔性数组。
柔性数组的特点
1.结构体的柔性数组成员前面必须至少一个其他成员
2.sizeof返回的这种结构大小不包括柔性数组的内存
3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
struct s1
{
int num;
int arr[];
};
struct s2
{
int num;
int arr[0];
};
柔性数组的二种写法,根据编译器的不同有可能有一种不行,但至少总有一种可以
柔性数组好处
柔性数组有二个好处
1.方便内存释放
2.这样有利访问速度