精通C语言(4.四种动态内存有关函数)

🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言

🔥专栏:《C语言入门知识点》《C语言底层》《精通C语言》《C语言编程实战》

💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。


前言:

我们提法C语言或者说各种编程语言,其实就是与他们对内存的处理打交道,今天依旧是围绕内存来做一些文章:动态内存管理。包括接下来的几篇也都是关于动态内存管理的文章,今天我们先来了解一下四个有关动态内存管理的函数吧,记好笔记出发喽~


文章目录

  • 前言:
  • 正文:
    • 1.为什么要有动态内存管理
    • [2. malloc函数和free函数](#2. malloc函数和free函数)
      • [2.1 malloc函数](#2.1 malloc函数)
      • [2.2 free函数](#2.2 free函数)
    • [3. calloc函数和realloc函数](#3. calloc函数和realloc函数)
      • [3.1 calloc函数](#3.1 calloc函数)
      • [3.2 realloc函数](#3.2 realloc函数)

正文:

1.为什么要有动态内存管理

我个人认为,你学一个东西,你一定要带有目的性的学,就像是你高中时,学习很大程度上本来就是为了高考服务的,而到了大学乃至其他阶段,你学习更要有一个目标,漫无目的的学是行不通的,你连学一个知识的目的是什么都不知道,你怎么能学好它

那我们就开始,到底什么是动态内存管理,以及为什么需要动态内存管理

我们平时开辟内存空间是怎么开的呢?我们知道两种方式:

c 复制代码
int i = 0;
int arri[30] = { 0 };

这两种方式一种是直接以整型为单位开辟四个字节的空间,另一种是以数组为单位来开辟想要的空间大小

但是这两种方式,他们对空间的开辟都是固定的,对于数组,就有三种情况:

① 我们现在需要存大于30个整数,那么上面的空间显然就不够通用了

②刚好够用

③需要存小于30个整数,上面的空间就多出来了,会被浪费掉

这种时候第二种当然是最好的,但是我们很难保证每次都是第二种情况,这个时候就需要动态内存管理了

2. malloc函数和free函数

先直观的来说malloc函数是用来开辟空间的,free函数是用来释放空间的

malloc中的mmemory"存储 "的意思,allocallocate"开辟 "的意思
free是"自由 "的意思

下面我们看一下他们在C语言标准库中的描述

  1. malloc
  2. free

可以看到他们都是在头文件<stdlib.h>中的函数

下面是他们的作用,意为"开辟内存空间"和"释放内存空间"

那我们来看看具体怎么用吧:

2.1 malloc函数

标准形式:

c 复制代码
void* malloc(size_t size);

返回结果

  1. 如果成功则返回指向开辟的初始地址的指针
  2. 如果失败则返回NULL这个空指针
  3. 如果size为0,会根据编译器不同产生不同的结果

演示:

c 复制代码
//x64环境下
//void* malloc(size_t size)
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
int* ptr = (int*)malloc(INT_MAX);
if (p != NULL)
{
	printf("yes\n");
}
if (ptr != NULL)
{
	printf("yes\n");
}
printf("p申请大小: %zu\n", 10 * sizeof(int));
printf("ptr申请大小: %zu\n", (size_t)INT_MAX * sizeof(int));  // 观察是否溢出
return 0;
}

运行结果:

c 复制代码
yes
yes
p申请大小: 40
ptr申请大小: 8589934588
c 复制代码
//x86环境下
//void* malloc(size_t size)
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
int* ptr = (int*)malloc(INT_MAX);
if (p != NULL)
{
	printf("yes\n");
}
if (ptr != NULL)
{
	printf("yes\n");
}
return 0;
}

运行结果:

c 复制代码
yes

为什么两种情况下会不一样呢?

  1. 在 x64 环境下,系统理论上可以访问非常大的虚拟地址空间(最大可达 2^64 字节),相比 x86 环境,它能利用的内存资源更充足。
  2. 在 x86 环境(32 位系统)下,系统能访问的虚拟地址空间最大为 4GB ,这就导致了malloc函数必然申请不到足够的连续的内存空间。

2.2 free函数

标准形式:

c 复制代码
void free(void* ptr)

返回结果:

  1. 如果ptr不是动态内存开辟的指针,那free的行为是未被定义的
  2. 如果ptr是一个NULL,那free将没有行为,什么都不做

演示:

c 复制代码
//void free(void* ptr)
int main()
{
	int* ptr = (int*)malloc(10 * sizeof(int));//这里的10可以用过变量代替
	if (ptr != NULL)
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(ptr + i) = 0;
		}
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

这就是一个完整的free函数搭配malloc函数的代码
free是释放内存空间的,但是后来一定得使空间置空,不然还是可以找到,导致麻烦

主要有三个麻烦之处

  1. 误用已释放的内存(内存访问错误)
c 复制代码
free(ptr);
// 未执行 ptr = NULL;
*ptr = 10;  // 错误:向已释放的内存写入数据,属于访问非法内存
  1. 重复释放内存(二次释放错误)
c 复制代码
free(ptr);
// 未执行 ptr = NULL;
free(ptr);  // 错误:重复释放同一内存块
  1. 条件判断失效(逻辑错误)
c 复制代码
free(ptr);
// 未执行 ptr = NULL;
if (ptr != NULL) {  // 错误:野指针非空,判断为"有效"
    *ptr = 20;      // 实际访问已释放内存
}

3. calloc函数和realloc函数

同malloc函数一样,他们两个函数都是用来开辟内存空间的

calloc函数中cclear"清零 "的意思,realloc函数是reallocate"重新分配 "的意思

依旧是看从语言标准库中的描述:

  1. calloc
  2. realloc

依旧是头文件<stdio.h>中的函数
callocmalloc效果唯一不同的一点就是他会把开辟的内存空间全部初始化成0
realloc则是对已有的空间进行重开辟

3.1 calloc函数

这里就直接演示一下:

c 复制代码
int main()
{
	int* ptr = (int*)calloc(10, sizeof(int));
	int i = 0;
	for (i = 0;i < 10; i++)
	{
		printf("%d %d\n", *(ptr + i),i+1);
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

运行结果:

c 复制代码
0 1
0 2
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10

可以看到开辟出的内存储存的值全部都是0

3.2 realloc函数

realloc函数的出现让动态内存管理更加灵活。

有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使⽤内存,我们⼀定会对内存的⼤⼩做灵活的调整。

realloc 函数就可以做到对动态开辟内存⼤⼩的调整。

c 复制代码
void* realloc (void* ptr, size_t size);

ptr是指向要调整的地址的指针
size是调整后的空间大小

返回值为指向调整后的地址的指针
但是realloc实际上会有两种情况:

这就导致会有不一样的结果:

  1. 当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。
  2. 当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤。这样函数返回的是⼀个新的内存地址

使用案例:

c 复制代码
//代码一,ptr是原开辟地址
ptr = (int*)realloc(ptr, 1000);
c 复制代码
//代码二
int* p = NULL;
p = realloc(ptr, 1000);
if (p != NULL)
{
	ptr = p;
}

这两种代码是对于原地址的两种处理结果

需要注意的是,第一种一旦发生空间不够位置改变,原来的数据就很难再找回来了

所以正确的应该是第二种

  • 以上就是四种动态内存管理的基础知识了,希望对大家有所帮助

  • 本节完...
相关推荐
暴力求解4 小时前
c++类和对象(下)
开发语言·c++·算法
应用市场4 小时前
Qt插件机制实现动态组件加载详解
开发语言·qt
小秋学嵌入式-不读研版4 小时前
C65-枚举类型
c语言·开发语言·笔记
FreeBuf_5 小时前
Happy DOM曝CVSS 9.4严重RCE漏洞,PoC已公开(CVE-2025-61927)
java·c语言·c++·python·php
熬了夜的程序员5 小时前
【LeetCode】69. x 的平方根
开发语言·算法·leetcode·职场和发展·动态规划
草莓熊Lotso5 小时前
C++ 手写 List 容器实战:从双向链表原理到完整功能落地,附源码与测试验证
开发语言·c++·链表·list
无限进步_5 小时前
【C语言】杨辉三角:数学之美与编程实现的完美结合
c语言·开发语言
Cg136269159745 小时前
封装的实现和定义
java·开发语言
武子康5 小时前
Java-146 深入浅出 MongoDB 数据插入、批量写入、BSON 格式与逻辑查询and or not操作指南
java·开发语言·数据库·sql·mongodb·性能优化·nosql