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出的空间就存在了内存泄漏。

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

相关推荐
passer__jw7671 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7671 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-72 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
lb36363636363 小时前
介绍一下数组(c基础)(详细版)
c语言
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
景鹤4 小时前
【算法】递归+回溯+剪枝:78.子集
算法·机器学习·剪枝