C语言malloc、calloc和realloc函数
寄一片海给你 人气:0前言
有时候我们需要的空间大小不确定,需要随着程序需要的空间而变化, 那以数组开辟的固定大小的空间就不适用了, 这时候我们就需要动态分配开辟空间了。当空间不够时就扩容。动态开辟是在堆区开辟一块连续可用空间,并返回这块空间的地址。有三种函数malloc, calloc和realloc。我们动态内存分配就在堆区开辟空间
上面的四个区只有堆区的空间是需要手动释放的
free函数
free函数是专门用来对动态开辟内存的回收和释放的。当我们不需要再使用动态开辟的空间时,一定要free释放空间,因为是在堆上开辟的空间,所以不会随着出了作用域而销毁,需要我们free释放,避免内存泄漏,并置空(将指针置为NULL),避免形成野指针。
内存释放是标记删除, 只会修改当前空间的所属状态,并不会清除空间内容。
当然内存泄漏也不是都有危害,但为了养成良好的代码习惯,动态开辟后一定要free
void free (void* ptr);
1.如果参数ptr指向的内存空间不是动态内存开辟的,那free函数的行为是未定义的,就会报错,所以free函数是针对动态开辟的空间
2.如果ptr是 NULL空指针,那么free函数什么都不做。
下面我们来实现一下。
动态内存分配需要调用头文件#include<stdlib.h>
malloc函数
void* malloc (size_t size);
malloc函数用来动态开辟的, size是开辟所需空间的大小,单位:字节。
并返回起始地址。返回类型是void*,表示我们可以开辟任意类型的空间,同时我们需要强制类型转换成我们开辟空间类型的指针,再用指针接收
例如我们开辟的是int型的空间,就需要先转换为(int*),再用int*的指针接收
开辟float型的空间,就需要先转换为(float*),用于float*的指针接收
int* p = (int*)malloc(sizeof(int) * 10);
重点:
1.malloc函数在开辟空间后需要判断开辟空间是否成功,若开辟成功会返回开辟好的空间的指针,开辟失败会返回空指针。
2.malloc函数并不会对开辟空间进行初始化,空间内容为随机数。
下面是关于malloc函数的例子,主函数如下:
主函数中第一段代码就是在堆上动态开辟10个int型大小的空间,通过int*型的指针p去维护这块空间,如果空间开辟失败就会返回NULL,所以我们需要判断是否开辟成功。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { int* p = (int*)malloc(sizeof(int) * 10); if (p == NULL) { //我们也可以直接eixt(-1)异常退出,或是return; printf("%s\n", strerror(errno)); //strerror(errno)是用来判断开辟失败的错误原因 return -1; //error是错误码 } //需要调用最下面的两个头文件 for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } printf("\n"); return 0; }
判断p为NULL时,也可以直接打印开辟失败,不需要调用strerror函数,也不用包含那两个头文件
if (p == NULL) { printf("malloc failed\n"); exit(-1); }
calloc函数
void* calloc (size_t num, size_t size);
calloc函数与malloc类似 ,calloc可以看作malloc+memset
参数num是开辟空间的元素个数,参数size是元素的大小,单位:字节。
函数的功能是为 num 个 size大小 的元素开辟一块空间,并且把空间的每个字节初始化为0。
calloc函数与malloc函数不同点在于:会将开辟的空间都初始化为0(按字节初始化为0)。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { printf("%s\n", strerror(errno)); return -1; } for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } printf("\n"); return 0; }
下面看运行结果都为0
realloc函数
realloc函数让动态内存管理变得更加灵活,空间不够时可以对动态开辟的空间扩容
void* realloc(void* ptr, size_t szie);
参数ptr为需要扩充动态内存分配的空间的地址
size是 调整之后新大小,返回参数为调整之后的内存起始位置。
realloc在调整内存空间的是存在两种情况:
1.原地扩容
在需要扩容的空间后有足够的空间进行扩容,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
2.异地扩容
原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。
pc指向的空间的数据会迁移到ptr指向的空间,此时不需要将pc的空间free,系统会自动释放。
下图通过两行代码了解什么是异地扩容,可以看到p1和p2的地址发生了变化
p1的值是0x006b56b8 p2的值是0x006bf4d0,所以扩容后的空间地址与原空间地址不同,这就是异地扩容
当然扩容失败就会返回NULL,异常退出了
下面是malloc和realloc的联合使用,我们扩容后要将扩容后的空间再次交给原指针去维护
所以将p=ptr
#include <stdio.h> #include <stdlib.h> int main() { int* p =(int*) malloc(sizeof(int)*10); if (p == NULL) { printf("malloc faied\n"); return -1; } int* ptr = (int*)realloc(p, sizeof(int) * 20); if (ptr == NULL) return -1; p = ptr; for (int j = 0; j < 20; j++) { printf("%d ", p[j]); } printf("\n"); return 0; }
运行结果可以看到都是随机数,所以realloc函数也不会初始化
可以看出realloc与malloc相似,都不会初始化。同时这也是realloc和malloc的一个特性,
当要扩容的对象为空时,realloc可以当作malloc函数使用。
扩充
我们一般动态开辟的空间都是结构体,下面简单介绍一下开辟结构体类型的空间
我们首先定义了一个结构体类型,里面定义的是int型的数据data,还有一个next型的指针,存放下一节点的地址,这就是数据结构的单链表结构,不了解的小伙伴可以简单看一下
pc指向开辟的一个节点用,p指向开辟的另一个节点用,将pc指向的结构体中的next保存p指向节点的地址
#include <stdio.h> #include <stdlib.h> typedef struct QueueNode { DataType data; struct QueueNode* next; }QueueNode; int main() { QueueNode* pc = (QueueNode*)malloc(sizeof(QueueNode)); QueueNode* p = (QueueNode*)malloc(sizeof(QueueNode)); if (pc == NULL && pc == NULL) { printf("malloc failde\n"); exit(-1); } pc->data = 10; pc->next = p; p->data = 20; p->next = NULL; printf("%d %d\n", pc->data, p->data); printf("%p %p %p\n", pc->next, p, p->next); free(pc); free(p); pc = p = NULL; return 0; }
重点:动态开辟使用完后,一定要记得free,置空。
了解完上面的内容,是不是对动态分配有了更深的理解。
malloc/calloc/realloc区别总结
相同点:
1.都是从堆上申请空间
2.都需要对返回值判空
3.都需要用户free释放
4.返回值类型相同(void*)
5.都需要类型转化
6.底层实现上是一样的,都需要开辟多余的空间,用来维护申请的空间
可以输入以下代码观测内存:
#include <stdio.h> #include <malloc.h> int main(){ int *p= (int *)malloc(sizeof(int )*10); return 0; }
不同点:
1.函数名字不同和参数类型不同。
2.calloc会对申请空间初始化,并且初始化为0,而其他两个不会。
3.malloc申请的空间必须使用memset初始化
4.realloc是对已经存在的空间进行调整,当第一个参数传入NULL的时候和malloc一样
总结
加载全部内容