一、动态内存管理引入
1、为什么要动态内存管理
静态分配的内存大小在编译时就固定了,不论是否使用都会占用这部分资源。动态内存管理可以根据实际需要来分配内存,这有助于避免浪费和更有效地利用有限的内存资源。
许多复杂的数据结构,如树、图和各种链式结构,通常需要在运行时动态地创建和销毁节点,这也需要动态内存管理。
二、相关函数
C语言的动态内存管理涉及几个关键函数,这些函数通常包含在C标准库的stdlib.h
头文件中。动态内存管理允许程序在运行时分配和释放内存,这对于处理大小不确定或数据量大的情况非常有用。以下是C语言中动态内存管理的几个关键函数:
1、malloc
1)函数作用
在堆区(heap)分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。通常用于分配单一大块内存,例如数组或结构体。
2)函数原型:
3)函数参数:
size是需要初始化内存块的大小,单位是字节。
如果size是0,这时malloc的行为是标准未定义的,取决于编译器。
4)返回值:
如果分配成功,返回指向这块内存的指针。如果分配失败,比如内存不足,返回NULL
指针。
malloc的返回值是void*类型的,是因为函数并不知道开辟空间的用途,所以使用泛型指针。
5)示例:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
int n = 0;
printf("请输入数组大小>:");
scanf("%d", &n);
int* arr = (int*)malloc(n * sizeof(int));
if (arr == NULL)
{
fprintf(stderr, "内存分配失败\n");
return EXIT_FAILURE;//内存分配失败,主函数以失败状态退出
}
//分配成功后的操作
int i = 0;
printf("请依次输入数组元素>:");
for (i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for (i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
free(arr);
arr = NULL;
return 0;
}
运行结果:
虽然当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议您在不需要内存时,都应该调用函数 free() 来释放内存。
在释放内存后,将指针置为NULL。
2、free
1)函数作用:
用于释放不再需要的动态分配的内存,是防止内存泄漏的关键步骤。释放之前通过malloc
、calloc
或realloc
分配的内存块。free
后的指针应该设置为NULL
,避免悬挂指针(即指针指向的内存已被释放或不可用)。
2)函数原型:
3)函数参数:
如果memblock指针指向的空间不是动态内存开辟的,则free的行为是未定义的。
如果memblock是空指针(NULL),则free函数什么也不做。
4)示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int* p = 0;
p = (int*)malloc(sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));//开辟失败
return EXIT_FAILURE;
}
//开辟完成后的操作
*p = 6;
printf("%d\n", *p);
free(p);//释放开辟的内存块
p = NULL;//将指针置空
return 0;
}
3、calloc
1)函数作用:
contiguous allocation,意思是连续分配。calloc
函数分配多个连续的内存块,每块的大小相同,并且将所有位自动初始化为零。
也就是在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是 0。
通常用于分配一定数量相同类型的对象,并需要其初始化为0的情况。
2)函数原型:
3)函数参数:
它接受两个参数,即需要分配的内存块数 num 和每个内存块的大小 size (以字节为单位),也就是分配num
个对象的内存,每个对象的大小都是size
字节。
4)函数返回值:
如果分配成功,返回指向这块内存的指