一 简要介绍
*在引入动态内存管理之前,相信不少人有这样的困惑:什么是动态内存管理?
简而言之,动态内存管理就是在内存中灵活的开辟空间,只不过这一个空间在内存中所占的大小是不确定的,可变的,既然是变化的,就叫动态内存了。
*简单地了解了一下动态内存管理是什么之后,不少人又有这样的疑问了:
明明在内存中直接开辟各种数据类型也可以,那么动态内存有什么存在的必要?
其实在实际使用的过程中,我们并不是很清楚我们需要多大一块内存,这时候就需要进行动态内存管理了。
*那么我们如何进行动态内存管理呢?
其实malloc,calloc,realloc,free就是专门为解决这一问题而涉及的函数。
这一函数在堆区进行操作。
我们来拓展一下内存区域中数据的存储。
进行动态内存管理实际上就是在堆区上进行的一个操作。
二 动态内存管理操作有关的函数
1开辟空间
malloc
向内存申请一块连续可用的空间,如果开辟成功那么返回指向这一片空间的指针,如果开辟失败,那么返回NULL
①函数的声明与定义:
void*malloc(size_t size)
传入的参数表示你要为这一块内存申请多大的字节。这个空间不可能无限大,它的范围是:0~4294967295
void*则指向开辟的空间。我们需要根据实际意义对他进行强制类型转换。
注意:由于malloc有可能开辟内存空间失败,因此我们使用他的时候要检查他的返回值。
malloc(0)是一种标准未定义的写法,取决于编译器
②接下来演示一下malloc的用法:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* p = NULL;
p = (int*)malloc(10 * sizeof(int));//包含头文件#include<stdlib.h>,假设在开辟10个char 空间
int i = 0;
if (p != NULL)
{
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
}
类似于开辟了一个可以存储10个int的整型数组。
2 calloc
calloc的功能类似于malloc,只不过传参和初始化不同。calloc会在开辟空间的同时初始化这块空间0。
① 对于calloc的声名和定义
void* calloc (size_t num, size_t size);
num为传入参数的个数,size是每个元素的大小。
举个例子:
3 动态内存释放 free
其实在动态内存空间开辟完成之后,需要free进行内存的释放,否则就会造成内存泄漏。
注意:
①free只用于动态内存开辟后内存的释放
②需要注意部分内存释放的问题。比如上述的程序,如果循环内是p++的话,那么指针进入循环中就会被更改,指向后面,此时再进行释放的话就会出现此类问题。也可以对指针进行拷贝,那么即使对指针进行了操作。也可以找到首地址。
③指针内存释放之后需要被置成NULL。(避免野指针)
④如果是free(0)则什么事情都不做。也不会出现报错。
因此,对上述的程序进行修改:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* p = NULL;
p = (int*)calloc(10,sizeof(int));//包含头文件#include<stdlib.h>,假设在开辟10个char 空间
int i = 0;
if (p != NULL)
{
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
4 调整开辟的内存——realloc
realloc的功能就是为调整原先开辟的内存空间的大小。
①函数的声明和定义
void* realloc (void* ptr, size_t size);
对于ptr,需要调整内存大小的指针的位置。
对于size,调整之后的大小(单位:byte)
对于返回值:
情况一:如果这块空间后面存在一块连续的空间并且这一块空间足够大,那么realloc扩容操作可以直接在这一块空间后面开辟空间。
情况二:如果这块空间后面没有足够的空间了,那么就会在内存的其他位置再找空间,足够放得下再开辟后的总数据。并且将原来的数据销毁。
如图:
对于内存开辟失败的话,就会返回空指针,会将原来的数据弄丢。因此需要再引入一个指针,对原先的数据进行维护。举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<stdio.h>
int main()
{
int* p = NULL;
p = (int*)malloc(10*sizeof(int));//包含头文件#include<stdlib.h>,假设在开辟10个char 空间
int i = 0;
if (p != NULL)
{
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}//malloc之后觉得空间太小了
int* ptr = NULL;
ptr =(int*)realloc(p, 15 * sizeof(int));//将调整后的大小用另外的ptr指针操作
if (ptr != NULL)
{
;//业务处理
}
free(ptr);
ptr = NULL;
return 0;
}
三 柔性数组
1 什么是柔性数组?
柔性数组是结构体中未指定大小的一个数组,它位于结构体中最后一个成员位置。
注意
计算结构体成员大小的时候,不包含柔性数组的大小
2 生命和定义
struct ALL_STUDENT {
char school;
int age;
char course[];//char course[0]
};
3 使用
可以直接对包含柔性数组的结构体一次性开辟空间,也可以分开开辟。
举例子:
#include<stdio.h>
#include<stdlib.h>
struct ALL_STUDENT {
char school;
int age;
int course[0];//char course[0]
};
int main()
{
struct ALL_STUDENT* p = (struct ALL_STUDENT*)malloc(sizeof(struct ALL_STUDENT) + 100 * sizeof(int));//为柔性数组开辟100个char的空间
if (p != NULL)
{
for (int i = 0; i < 100; i++)
{
p->course[i] = i;
}
for (int i = 0; i < 100; i++)
{
printf("%d ", p->course[i]);
}
}
free(p);
p = NULL;
return 0;
}
一次性开辟的好处是防止忘记多次free指针,并且运行的速度更快