C语言——动态内存管理

为什么要进行动态内存管理?

在平常进行变量定义的时候,我们通常是进行如下的定义方式:

cpp 复制代码
int a = 10;//整型变量
int arr[] = {1,2,3,4,5,6,7,8,9,10};//十个元素的整型数组

但是这样定义出的变量a只能进行存储一个整形变量,以及数组arr也只能存储十个整形,如果想要扩大,那是做不到的,只能重新定义一个,这是相当麻烦的,所以我们便引出了动态内存,可以根据需要来进行扩大,这样不仅节约了空间,也提供了便利。

动态内存函数的介绍

malloc函数:

cpp 复制代码
#include<stdlib.h>
//这是包含malloc的头文件
int main() {

	void* p = malloc(10 * sizeof(int));
	//这种当然没什么问题,但是使用者在用的时候肯定是知道自己要开辟什么类型的空间的,
	// 如果用void* P来接收的话,反而不是太方便,所以我们习惯写下面这种写法。

	int* p = (int*)malloc(10 * sizeof(int));

	if (p == NULL) {//这里我们需要判断一下是否申请成功了,
		//如果不进行判断的话,后面会出现解引用NULL指针的问题。
		perror("malloc");
		return 1;
	}
	return 0;
}

那么有人要问了,malloc真的会申请失败么,答案是会的 🕶️🕶️🕶️

看图当我们所申请的值太大时,就会申请失败。

free函数:

当我们动态调用之后,我们有义务对其动态开辟的空间进行释放,也就是说我们向操作系统调了一块空间,我们就必须在不用的时候返还给操作系统,所以free函数是用来将动态开辟的空间释放的。

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
int main() {
	int* p1 = (int*)malloc(10 * sizeof(int));
	if (p1== NULL) {
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++) {
		*(p1 + i) = i;
		printf("%d ", *(p1+i));
	}
	free(p1);
	p1 = NULL;
	return 0;
}

我在14行处打一个断点, F5跳到断点处

可以看到,地址并没有发生任何变化,但是实际上这块地址已经还给操作系统 了,不再属于我们动态开辟的了,所以一定要在释放完之后,进行指针置空,防止后面访问野指针而导致程序错误。

calloc函数

这是malloc函数的空间开辟,可以看到并没有进行初始化

这是改用calloc函数进行的:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
int main() {
	int* p1 = (int*)calloc(10 , sizeof(int));
	if (p1== NULL) {
		perror("calloc");
		return 1;
	}
	for (int i = 0; i < 10; i++) {
		printf("%d\n", *(p1+i));
	}
	free(p1);
	p1 = NULL;
	return 0;
}

realloc函数:

那么正确的方法应该是这样的:

这样即使realloc申请失败,那也不会影响原本的内存内容。

当然如果我们在realloc的地址处传一个NULL指针的话,其实它可以等价于malloc

cpp 复制代码
	int* p = (int*)realloc(NULL, 20*sizeof(int));// == malloc

常见的动态内存错误

对空指针进行解引用操作

cpp 复制代码
int* p = (int*)malloc(40);
//如果不对p进行返回值判断,那么当申请失败时,就会发生解引用操作
*p = 20;

对动态空间的越界访问

当我们的访问超出了申请的内存范围时,就是越界了,这时我们的编译器就会报错。

对非动态开辟内存使用free释放

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
int main() {
	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

我们不能对局部变量进行free释放,free函数只能用于malloc、calloc等这些存在于堆上的内存。

使用free释放一块动态开辟内存中的一部分

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}

	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//0 1 2 3 4 0 0 0 0 0 
	
	//释放
	free(p);
	p = NULL;

	return 0;
}

如果我们在这个代码里,p进行++操作,当p+到5的时候,突然不想用了,那么我们是不能进行释放一部分动态开辟出来的空间的。

对同一块动态内存多次释放

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		//....
		return 1;
	}
	//....
	
	//释放
	free(p);
	p = NULL;
	//...

	free(p);

	return 0;
}

这里我们对p进行了两次释放,就会导致出错。

动态开辟内存忘记释放(内存泄漏)

cpp 复制代码
void test()
{
	int* p = (int*)malloc(40);
	//...
	if (3)
		return;

	free(p);
	p = NULL;
}

int main()
{
	test();
	//...
	while (1)
	{
		;
	}

	return 0;
}

这里我们在主函数中调用了test,但是进入函数malloc了空间,代码走到if时直接就返回主函数了,并没有走到free,然后主函数里面也无法对其进行释放,如果我们的程序不结束,就例如这里的while死循环,那么malloc出的空间就存在了内存泄漏。

下次我在给大家带来一些常见的经典题目🌹🌹🌹

相关推荐
冷雨夜中漫步3 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
HABuo4 小时前
【linux文件系统】磁盘结构&文件系统详谈
linux·运维·服务器·c语言·c++·ubuntu·centos
颜酱5 小时前
图结构完全解析:从基础概念到遍历实现
javascript·后端·算法
m0_736919105 小时前
C++代码风格检查工具
开发语言·c++·算法
yugi9878385 小时前
基于MATLAB强化学习的单智能体与多智能体路径规划算法
算法·matlab
2501_944934735 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
DuHz5 小时前
超宽带脉冲无线电(Ultra Wideband Impulse Radio, UWB)简介
论文阅读·算法·汽车·信息与通信·信号处理
Polaris北极星少女5 小时前
TRSV优化2
算法
黎雁·泠崖6 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
代码游侠6 小时前
C语言核心概念复习——网络协议与TCP/IP
linux·运维·服务器·网络·算法