1.为什么要有动态内存分配
我们已经掌握的内存开辟方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节个连续空间
但是上述的开辟空间的方式有两个特点:
空间开辟的大小是固定的。
数组在申明的时候,必须指定数组的长度,数组空间一旦确定了大小不能调整。
但是对于空间的要求,不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足。
C语言引入了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。
2.malloc和free
2.1malloc
malloc 函数是 C 语言标准库中的一个函数,用于动态分配内存。它在 <stdlib.h> 头文件中声明,用于在堆(heap)上分配指定大小的内存块,并 返回 指向该内存块的指针。如果分配失败,它会返回 NULL 指针。
malloc 函数的基本语法如下:
void* malloc (size_t size);
1.其中 size_t 是一个无符号整数类型,用于指定要分配的字节数。
2.使用 malloc 函数时,你需要包含 <stdlib.h> 头文件,并且在使用完毕后,应该使用 free 函数来释放分配的内存,以避免内存泄漏。(关于什么free我们下面会说)。
3.返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候,使用者自己来决定。
4.如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。
2.2free
free 函数是 C 语言标准库中的一个函数,用于释放之前通过 malloc 、 calloc 或 realloc 函数动态分配的内存。它在 <stdlib.h> 头文件中声明。
当你使用 malloc 或其他动态内存分配函数分配内存后,这些内存会一直被占用,直到你明确地使用 free 函数来释放它。如果不释放不再使用的内存,会导致内存泄漏,即内存资源被占用但无法被其他程序或系统回收,这可能会导致系统性能下降。
C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free函数用来释放动态开辟的内存。
1.如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
2.如果参数ptr是NULL指针,则函数什么事都不做。
malloc和free都声明在stdlib.h头文件中
下面我给出一串代码,来解释一下malloc和free函数要怎么使用。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
int n = 10; // 假设我们想要分配10个整数的空间
// 分配内存
ptr = (int*)malloc(n * sizeof(int));
if (ptr == NULL) {
// 如果分配失败,打印错误信息并退出程序
fprintf(stderr, "Memory allocation failed\n");
return 1;
}
// 使用分配的内存
for (int i = 0; i < n; i++)
{
ptr[i] = i;
}
// 打印分配的内存中的值
for (int i = 0; i < n; i++)
{
printf("%d ", ptr[i]);
}
printf("\n");
// 释放内存
free(ptr);
// 注意:释放内存后,ptr 指针仍然指向原来的内存地址,但该内存区域已经不再属于程序。
// 因此,不应该再使用 ptr 指针访问内存,除非重新分配。
return 0;
}
3.calloc和realloc
3.1calloc
calloc 函数是 C 语言标准库中的一个函数,用于动态内存分配。它与 malloc 函数类似,但有两个主要区别:
1. calloc 会将分配的内存初始化为零,而 malloc 不会初始化内存。
2. calloc 接受两个参数:元素的数量 和 每个元素的大小,而 malloc 只接受一个参数,即所需内存的大小。
C语言还提供一个函数叫calloc,calloc函数也用来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
num 是要分配的元素数量。
size 是每个元素的大小(以字节为单位)。
calloc 返回一个指向分配内存的指针,如果分配失败,则返回 NULL 。
函数的功能是为num个大小为size的元素开辟一块空间,并把空间的每一个字节初始化为0。
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每一个字节初始化为0。
我们可以给一串代码,来讲解一下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));//开辟了十个空间,每一个空间的大小就是sizeof(int),并且把每一个字节都初始化成了0。
if (p != NULL)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
结果就是这样的:
所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来进行任务。
3.2realloc
realloc函数的出现让动态内存管理更加灵活。
realloc 函数是 C 语言标准库中的一个函数,用于重新分配动态分配的内存。当你已经使用 malloc , calloc , 或 realloc 分配了一块内存,但后来发现需要更多的空间或更少的空间时,可以使用 realloc 来调整内存块的大小。
realloc函数的原型如此下:
void* realloc(void* ptr, size_t size);
ptr 是指向之前分配的内存块的指针。
size 是新的内存块大小(以字节为单位)。
返回值为调成之后的内存起始位置。这个函数调整原内存空间大小的基础上,还会将原来内存的数据移动到新空间。
realloc 返回一个指向重新分配的内存块的指针,如果分配失败,则返回 NULL 。如果新的内存块大小 小于原来的大小,那么原始内存块中超出新大小的部分将被丢弃。如果新的内存块大小大于原来的大小,那么原始内存块的内容将保持不变,新分配的内存将不会被初始化。
realloc再调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间
下面的代码把空间调整为40个字节就包含两种情况,下面会细细道来:
情况1:
当情况1的时候,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。
情况2:
当时情况2的时候,红色区域假设已经分配了,原有空间之后没有足够多的空间时,扩展的方法是:
1.在堆空间上另找一个适合大小的连续空间来使用
2.会在原来的空间的数据拷贝一份到新的空间
3.释放旧的空间。(不用对旧的空间free因为这个函数就会直接把旧的空间释放掉)
4.返回新的内存空间的起始地址
由于有这两种情况,realloc函数使用的时候就要注意一些:这里给出一串代码;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(100);
if (ptr != NULL)
{
//业务处理
}
else
{
return 1;
}
//扩展容量
//代码1 - 直接将realloc的返回值放到ptr中
ptr = (int*)realloc(ptr, 1000);//这样可以吗?答案是不可以,因为你不确定这个空间是否会请求成功。
//如果请求失败的话就会变成空指针,就会找不到这个地址了
//所以说换一种思路:
//代码2 - 先将realloc函数的返回值放到p中,不为NULL,放到 ptr中
int* p = NULL;//对p进行初始化
p = realloc(ptr, 1000);
if (p != NULL)
{
ptr = p;
//这时候就可以使用它了
int i = 0;
for (i = 5; i < 10; i++)
{
*(p + i) = i + 1;
}
for (i = 0; i < 10; i++)
{
printf("%d", *(p + i));
}
free(p);
p = NULL;
}
else
{
perror("realloc");
}
//到这一步就是请求成功的话,就会是使用40个字节的空间了
//业务处理
free(ptr);
return 0;
}
OK了兄弟们关于动态内存申请涉及到的函数到这讲完了,但是这个章节还是有很多知识,我们下篇在开始。