C语言中动态内存分配malloc、calloc和realloc函数解析

目录
  • 前言
  • free函数
  • malloc函数
  • calloc函数
  • realloc函数
  • 扩充
  • malloc/calloc/realloc区别总结
  • 总结

前言

有时候我们需要的空间大小不确定,需要随着程序需要的空间而变化, 那以数组开辟的固定大小的空间就不适用了, 这时候我们就需要动态分配开辟空间了。当空间不够时就扩容。动态开辟是在堆区开辟一块连续可用空间,并返回这块空间的地址。有三种函数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一样

总结

到此这篇关于C语言中动态内存分配malloc、calloc和realloc函数解析的文章就介绍到这了,更多相关C语言动态内存分配函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c语言中malloc、realloc与calloc 的区别以及联系

    ANSI C说明了三个用于存储空间动态分配的函数(1) malloc分配指定字节数的存储区.此存储区中的初始值不确定 (2) calloc为指定长度的对象,分配能容纳其指定个数的存储空间.该空间中的每一位(bit)都初始化为0 (3) realloc  更改以前分配区的长度(增加或减少).当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定 .分配函数时再分配 realloc()使我们可以增.减以前分配区的长度(最常见的用法是增加该区). 如果先分配一个可容

  • C语言中 malloc,calloc,realloc的区别

    C语言中 malloc.calloc.realloc的区别 (1)C语言跟内存分配方式 <1>从静态存储区域分配. 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量.static变量. <2>在栈上创建 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限. <3>从堆上分配,亦称动态内存分配. 程序在运行的时候用malloc

  • C语言中动态内存分配malloc、calloc和realloc函数解析

    目录 前言 free函数 malloc函数 calloc函数 realloc函数 扩充 malloc/calloc/realloc区别总结 总结 前言 有时候我们需要的空间大小不确定,需要随着程序需要的空间而变化, 那以数组开辟的固定大小的空间就不适用了, 这时候我们就需要动态分配开辟空间了.当空间不够时就扩容.动态开辟是在堆区开辟一块连续可用空间,并返回这块空间的地址.有三种函数malloc, calloc和realloc.我们动态内存分配就在堆区开辟空间 上面的四个区只有堆区的空间是需要手动

  • C语言初识动态内存管理malloc calloc realloc free函数

    目录 一.为什么存在动态内存分配 二.动态内存函数的使用 1.malloc函数 (1)malloc的定义 (2)malloc函数的注意事项 (3)malloc函数的使用 2.calloc函数 (1)calloc函数的定义 (2)calloc函数的注意事项 (3)calloc函数的使用 3.realloc函数 (1)realloc函数的定义 (2)realloc函数的注意事项 (3)realloc函数的使用 总结 一.为什么存在动态内存分配 在c语言中我们目前掌握的内存开辟方式有: int val

  • 详细谈谈C语言中动态内存

    目录 前言 1.关于动态内存的函数 1.1malloc和free函数 1.2calloc函数 1.3realloc函数 2.常见的动态内存错误 2.1对NULL指针解引用 2.2对动态内存开辟的空间越界访问 2.3 对非动态开辟内存使用free释放 2.4 使用free释放一块动态开辟内存的一部分 2.5对同一块动态内存多次释放 2.6内存泄漏 补充:为什么要引入动态内存分配 总结 前言 关于动态内存管理,可能有学习过的小伙伴,也有没有听说过的.没有听说过的小伙伴会觉得很奇怪啊,为什么要动态开辟

  • C语言中动态内存管理图文详解

    目录 1.动态内存开辟的原因 2.动态内存函数的介绍 2.1malloc和free 2.2calloc 2.3realloc 3.常见的动态内存错误 3.1对NULL指针的解引用操作 3.2对动态开辟空间的越界访问 3.3对非动态开辟内存使用free 3.4使用释放一块动态开辟内存的一部分 3.5对同一块动态内存多次释放 3.6动态开辟内存忘记释放(内存泄漏) 4.练习 4.1练习1 4.1练习2 4.3练习3 4.4练习4 5.C/C++程序的内存开辟 总结 1.动态内存开辟的原因 常见的内存

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

  • 深入解析C语言中的内存分配相关问题

    C内存分配区域 程序代码区 存放函数体的二进制代码 全局数据区 全局变量和静态变量的存储是放在一起的.初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域.常量数据存放在另一个区域里.这些数据在程序结束后由系统释放.我们所说的BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称 栈区 由编译器自动分配释放,存放函数的参数值,局部变量的值等.其操作方式类似

  • C语言编程动态内存分配常见错误全面分析

    目录 前言:为什么存在动态内存分配? 一.动态内存函数 1.malloc和free函数 2.calloc函数 3.realloc函数 二.常见错误 1.对NULL指针解引用 2.对动态开辟空间的越界访问 3.对非动态开辟使用free函数 4.使用free释放一块动态内存开辟内存的一部分 5.对同一块空间多次释放 6.动态开辟内存忘记释放 总结 前言:为什么存在动态内存分配? 我们已经掌握的内存开辟方式如下 int a=10;//在栈空间上开辟4字节 char arr[10]={0};//在栈空间

  • C语言的动态内存分配及动态内存分配函数详解

    目录 malloc malloc的使用: free calloc calloc的使用: realloc realloc的使用改进: realloc的另一种用法: 常见的动态内存错误 对空指针的解引用操作 对动态开辟空间的越界访问 对非动态开辟内存使用free释放 使用free释放一块动态开辟内存的一部分 对同一块动态内存多次释放 动态开辟内存忘记释放(内存泄露) 找出下面问题: T1: T2: T3: T4: 柔性数组 柔性数组的定义 柔性数组的特点: 总结 malloc void *mallo

  • C语言动态内存分配的详解

    C语言动态内存分配的详解 1.为什么使用动态内存分配 数组在使用的时候可能造成内存浪费,使用动态内存分配可以解决这个问题. 2. malloc和free C函数库提供了两个函数,malloc和free,分别用于执行动态内存分配和释放. (1)void *malloc(size_t size); malloc的参数就是需要分配的内存字节数.malloc分配一块连续的内存.如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针. (2)void free(void *poi

  • C语言中关于动态内存分配的详解

    目录 一.malloc 与free函数 二.calloc 三.realloc 四.常见的动态内存的错误 [C语言]动态内存分配 本期,我们将讲解malloc.calloc.realloc以及free函数. 这是个动态内存分配函数的头文件都是 <stdlib.h>. c语言中动态分配内存的函数,可能有些初学c语言的人不免要问了:我们为什么要通过函数来实现动态分配内存呢? 首先让我们熟悉一下计算机的内存吧!在计算机的系统中大致有这四个内存区域: 1)栈:在栈里面储存一些我们定义的局部变量以及形参(

随机推荐