1. 前言
在讲内存分配前,咱来聊一下为什么会有内存分配这个概念呢,大家都知道C语言当中是有着许多的数据类型,使用这些数据类型就会在内存上开辟其相对应的空间,那既然会开辟相应的空间,为什么还会有内存分配呢,我们会发现C语言分配内存空间,是已经被固定好了的,固定开辟多少空间,但是这样开辟空间会不会太死板了呢,如果我用不上这些空间呢,这样会导致内存的利用率很低,有没有一种,我需要多少空间,就开辟多少空间的方法呢,这个时候C语言就为我们引进了动态内存分配的这个概念,顾名思义,就是你想分配多少空间就分配多少空间,下边我们来一起了解一下怎样在内存上开辟我们想要的空间
(注意:使用这些函数都需要包含#include<stdlib.h>这个头文件)
2. malloc函数:
2.1 malloc函数的声明:
函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针(由于不知道你要申请的类型,使用返回值为void*)
- 如果开辟成功,则返回⼀个指向开辟好空间的指针。
- 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查
- 如果参数 size 为0,malloc的⾏为是标准是未定义的取决于编译器决于编译器(vs2022)
所以一个malloc的使用可以分为以下几步:
- 使用malloc函数对内存空间申请指定空间
- 判断返回值是否为NULL
- 内存空间的释放
2.2 malloc函数的简单应用:
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 5); //向内存申请5个int型的内存空间
if (p == NULL) //判断返回值是否为空
{
perror("malloc"); //为空,则报错malloc函数处有问题
return 1;
}
free(p); //释放内存,在下边会说喔
p = NULL; //将p指针赋为NULL,防止出现野指针
return 0;
}
3. free函数:
在前边的代码当中,我们了解到free函数是专门用来释放动态申请的内存空间,同时也说明了它只能释放动态申请的内存空间,关于这一点兄弟们不要搞混咯
3.1 free函数声明:
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的
-
如果参数 ptr 是NULL指针,则函数什么事都不做
4. malloc与free的实际运用:
int main()
{
int* p = (int*)malloc(sizeof(int) * 10);//申请10个int型元素
if (p == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i < 10; i++) //对malloc申请的空间进行赋值
{
*(p + i) = i;
}
for (int i = 0; i < 10; i++) //输出
{
printf("%d ", *(p + i));
}
free(p); //释放空间
p = NULL;
return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9
5. calloc函数:
5.1 calloc函数的声明:
calloc
void* calloc (size_t num, size_t size);
- 与malloc相似,也是用来开辟内存空间
- 开辟num个size大小的元素
- 相较于malloc来说,calloc开辟好空间后,还会将空间内的每个字节初始化为0
5.2 calloc的简单应用:
下边我们来一段代码调试进行了解:
int main()
{
int* p1 = (int*)calloc(10, sizeof(int));
if (p1 == NULL)
{
perror("calloc");
return 1;
}
free(p1);
return 0;
}
题解分析:
6 realloc函数:
当你觉得动态内存申请的空间,不够使用的时候,这个时候就可以运用realloc函数对内存空间进行扩容,所以realloc函数是对动态内存空间进行扩容的
6.1 realloc函数的声明:
realloc
void* realloc (void* ptr, size_t size);
1. ptr 是要调整的内存地址
6.2 relloc函数扩容内存时的两种情况:
1. 原有的空间在扩容的时候,后边有足够的空间
2. 原有的空间在扩容的时候,后边没有足够的空间
6.3 relloc函数的简单应用:
int main()
{
int* p = (int*)malloc(sizeof(int) * 10);
if (p == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
printf("\n");
int* p1 = (int*)realloc(p,sizeof(int) * 15); //扩容5个int型
if (p1 == NULL)
{
perror("realloc");
return 2;
}
for (int i = 10; i < 15; i++)
{
*(p1 + i) = i;
}
for (int i = 0; i < 15; i++)
{
printf("%d ", *(p1 + i));
}
free(p1);
p1 = NULL;
return 0;
return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
6.4 柔性数组:
在c99中规定,一个结构体中最后的数组如果没有指定大小,则这个数组被称为柔性数组
柔性数组的特点:
- 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员
-
sizeof 返回的这种结构⼤⼩不包括柔性数组的内存
-
包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
struct S
{
int a;
char a1;
int a3[];
}q;
int main()
{
printf("%结构体大小为:zd", sizeof(q)); //计算结构体大小,了解柔性数组是否分配了大小
struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 5); //为a3数组分配了5个int型的空间
return 0;
}
题解分析:
6.4.1 柔性数组的简单应用:
申请一块动态内存空间后,对齐扩容,扩大柔性数组的内存空间,然后对柔性数组进行赋值,最后输出结构体中成员的值:
//柔性数组
struct S
{
int a;
char a1;
int a3[];
}q;
int main()
{
int ret = sizeof(struct S);
struct S* p2 = (struct S*)malloc(sizeof(struct S));
if (p2 == NULL)
{
perror("malloc");
return 1;
}
p2->a = 100;
p2->a1 = 48;
struct S* p3 = (struct S*)realloc(p2, sizeof(struct S)+sizeof(int)*5); //扩容柔性数组的大小
printf("%zd\n", sizeof(p3));
if (p3 == NULL)
{
perror("realloc");
return 2;
}
p2 = NULL;
for (int i = 0; i < 5; i++)
{
p3->a3[i] = i;
}
printf("%d\n", p3->a);
printf("%d\n", p3->a1);
for (int i = 0; i < 5; i++)
{
printf("%d ", p3->a3[i]);
}
free(p3);
p3 = NULL;
return 0;
}
//执行结果
8
100
48
0 1 2 3 4
7. 常见的动态内存错误:
7.1 使用free对非动态内存进行释放:
int main()
{
int a = 10;
int* p = &a;
free(p); //free对非动态内存申请的空间进行释放
return 0;
}
执行结果:
7.2 动态内存越界访问:
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);//向内存动态申请5个int型空间
if (p == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i < 6; i++) //向p执行的空间放入6个int型元素
{
*(p + i) = i;
}
return 0;
}
执行结果:
(程序死循环加上报错)
7.3 使⽤free释放⼀块动态开辟内存的⼀部分:
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);
if (p == NULL)
{
perror("malloc");
return 1;
}
for (int i = 0; i < 5; i++)
{
*p = i;
p++; //每执行一次p++,则p指向的地址加1
}
free(p);
return 0;
}
题解分析:
执行结果:
(程序死循环加上报错)
7.4 对同⼀块动态内存多次释放:
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);
if (p == NULL)
{
perror("malloc");
return 1;
}
free(p);
free(p);
return 0;
}
执行结果:
(程序死循环加上报错)
7.5 动态开辟内存忘记释放(内存泄漏):
什么意思呢,简单来讲,就是你申请了空间,但是没有及时的将空间释放掉,与上边多次释放刚好相反:
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);
if (p == NULL)
{
perror("malloc");
return 1;
}
//free(p); //为注释掉的内容
//free(p);
return 0;
}
执行以后不会报错,但是会造成内存上的浪费
7.6 对NULL指针的解引⽤操作:
int main()
{
int* p = (int*)malloc(INT_MAX*10);
*p = 100;
free(p);
return 0;
}
由于并没有判断p是否为NULL,当p为NULL时,就会发生以下报错:
(死循环直至程序崩溃)
(今日分享到此结束,如觉得对您有帮助,还请点赞关注支持一下,Thanks♪(・ω・)ノ!!!)