目录
1.为什么要使用动态内存
Int arr[20]={0};
Int va=10;
像上面方式开辟内存空间的时候,开辟空间的大小是固定的,且数组在申明的时候需要指定数组的长度。例如:在存储一个班同学的身高的时候,往往不知道班级人数的情况,我们会估计一个相对合理的值来开辟空间的大小。如果开辟的空间大了就会造成浪费,小了可能会造成数据的丢失。开辟的内存空间的大小往往和认为输入数据有关。为了改善上述问题,就需要,开辟动态内存来进行改善
2.什么是内存的动态分配
当我们创建变量的时候,全局变量存放在静态存储区,局部变量(包括形参)存放在动态存储区中,这个存储区称为栈。
除了栈区,还有堆区,用来存放一些临时数据的内存动态分配区域,需要的时候开辟,不需要的时候就释放,使用起来比较灵活,但只能通过指针来引用。
3.动态内存函数之一malloc
malloc函数在C语言提供的一个开辟连续动态内存的函数,在stdlib.h中进行声明。
一般形式:void* malloc(unsigned int size);
作用:在动态内存中开辟一个长度为size的连续空间
返回值:第一个字节的地址。
注:1.size为无符号整型,是需要开辟的内存的字节数,当size为0时,malloc的行为是标准未定义的,取决于编译器,且开辟的空间是不会进行初始化的。
2.指针的基类型是void*,不指向任何确定类型数据(纯地址),只提供一个地址,需要在使用的时候由使用者自行确定,如:int* pt=(int*)malloc(100);
3.若不能成功开辟空间,则返回空指针(NULL),因此每次都检测通过malloc返回的指针是必要的,确保它不是空指针。
4.动态内存函数之一free
一般形式:void free(void* p);
作用:函数free在stdlib.h中声明,释放指针变量p所指向的动态空间,p是最近一次调用malloc函数或者callloc函数的返回值。
返回值:无
注:当free的参数是NULL时,是没有任何效果的。
5.动态内存韩式之一calloc
开辟动态内存时方法除了malloc外,C语言还提供了一个函数叫做calloc,原型如下:
一般形式:void* calloc(unsigned n,unsigned size)
作用:在动态内存中开辟n个长度为size 的连续空间
返回值:所分配域的起始位置指针,若请求失败,返回NULL。
注:与malloc有一个很大的不同是,calloc会对分配的内存初始化为零。
其中:nitems是要分配的元素个数,size是元素的大小。当请求失败的时候,则返回NULL。
6.动态内存函数之一realloc
当开辟的空间过大、或过小,需要对开辟的动态内存空间进行调整,让内存管理更加灵活的时候,就可以尝试使用realloc函数了,函数原型如下:
一般形式:void *realloc( void *memblock, size_t size );
作用:将memblock所指向的动态空间重新分配为大小为size的动态空间。
其中:memblock 是要调整的地址的内存其实位置,size是重新调整之后新的大小,返回值为调整后的内存的起始位置。如果分配失败则,返回空指针NULL。
注:当需要“扩大”动态空间的时候,如果原内存空间后方有足够的空间,能满足分配额外内存的要求时,自然天下太平,在原内存空间后面直接分配就好。但是,当原空间后方的连续空间不足的时候,那就需要从堆上另外找一块size大小的内存,并且把原空间的内容复制到新的大小为size的空间中,返回新的指针(数据被移动了的)。原内存地址的指针就成了悬挂指针,即指针指向了一块没有分配给用户使用的内存
下面通过一个例子来进行加深,理解。
问题描述:
输入5个人的成绩,并输入其中不及格(小于60分)的成绩。
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
/*练习:建立动态数组,存储5个学生成绩,并将不合格学生成绩输出*/
int main()
{
void check(int*);
int* p1 = (int*)malloc(5 * sizeof(int)); //开辟动态内存空间,并地址转化成int* 型,存放值指针变量p1中
if (p1 == NULL)//检验内存是否开辟成功
{
printf("内存空间开辟失败\n");
}
//输入5个学生成绩
printf("请输入5个学生的成绩:\n");
for (int i = 0; i < 5; i++)
{
scanf("%d", p1 + i);
}
//检查是否合格
check(p1);
free(p1);//释放内存空间
p1 = NULL;//将改指针置空
return 0;
}
void check(int* str)
{
printf("不合格的学生成绩是:");
for (int i = 0; i < 5; i++)
{
if (*(str + i) < 60)
{
printf("%d ", str[i]);
}
}
printf("\n");
}
7.常见动态内存函数的错误
对NULL指针的解引用操作
void test()
{
int* p = (int*)malloc(100);
*p = 20;//如果内存开辟失败,p的值是NULL,就会出问题
free(p);
}
对动态开辟空间的越界访问
void test()
{
int* p = (int*)malloc(5*sizeof(int));
if (p == NULL)
{
printf("err");
}
for (int i = 0; i <= 5; i++)
{
*(p + i) = i;//当i是5的时候越界访问
}
free(p);
}
对非动态开辟内存使用free释放
int test()
{
int a = 10;
int* p = &a;
free(p);//不行的
}
使用free释放一块动态开辟内存的一部分
int test()
{
int* p = (int*)malloc(100);
p++;//此时p不在指向p
free(p);//不能只释放动态内存的一部分
}
对同一块动态内存多次释放
int test()
{
int* p = (int*)malloc(100);
free(p);
free(p);//重复释放!
}
动态开辟内存忘记释放(内存泄漏)