首页 > 系统相关 >C语言:动态内存管理

C语言:动态内存管理

时间:2024-08-03 18:24:32浏览次数:14  
标签:malloc return 管理 int C语言 内存 动态内存 sizeof NULL

动态内存管理

一、动态分配内存的必要性

普通内存分配

  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

动态内存分配

1、空间大小可以更改。
2、空间大小可以在程序执行时再分配。

二、动态内存分配函数

(一)malloc

void* malloc (size_t size);

使用规则:
传入字节数,申请成功返回申请空间的首地址,申请失败返回空地址,因此申请空间需要进行检查。

int main() {
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) {             // 进行检查会更安全
		perror("malloc");
		return 1;
	}
	printf("申请成功");
	free(p);                      //后面会有函数用法
	p = NULL;
	return 0;
}

在这里插入图片描述

(二)calloc

void* calloc (size_t num, size_t size);

使用规则:
传入申请类型个数以及一个类型的字节数,申请成功返回申请空间的首地址,申请失败返回空地址,申请空间需要进行检查。

与 malloc 不同的是calloc 会初始化每个字节为0;

int main() {
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL) {
		perror("calloc");
		return 1;
	}
	printf("申请成功");
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

(三)realloc

void* realloc (void* ptr, size_t size);

使用规则:
如果原来的内存块后面的空间足够开辟空间,那么直接在内存块后面扩展内存。否则 realloc 会另外寻找一片内存进行开辟,返回找到的地址,如果实在找不到这么大的内存,就会返回NULL。

int main() {
	int* arr = (int*)malloc(sizeof(int) * 10);
	if (arr == NULL) {
		perror("malloc\n");
		return 1;
	}
	int* p = (int*)realloc(arr, sizeof(int) * 20);
	if (p == NULL) {
		perror("realloc\n");
		return 1;
	}
	arr = p;
	free(arr);
	arr = NULL;
	return 0;
}

在这里插入图片描述

如果 ptr 传入NULL,他的效果就相当于 malloc。

int main() {
	int* p = (int*)realloc(NULL, sizeof(int) * 20);
	if (p == NULL) {
		perror("realloc\n");
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

(四)free

void free (void* ptr);

上面的函数 malloc calloc realloc 分配了空间之后,也需要进行释放。如果不释放,可能导致内存泄漏。如果不置空,可能导致非法访问

如果传入NULL,就不进行操作。

三、常见的错误

(一)对空指针进行解引用操作

如果分配空间时,内存不足以开辟返回NULL;不进行检查就解引用可能导致程序崩溃。

(二)对动态分配空间越界访问

顾名思义,就是不可以越界访问。

(三)free释放动态分配空间的一部分

如下,p++ 改变了 p 指向的内存,不再是首地址,就好像是释放一部分空间,这种情况程序会崩溃。

int main() {
	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL) {
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++) {
		*(p++) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

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

下面代码中,如果开辟成功,malloc 开辟的内存没有释放,但是 p 的地址我们在函数外面就再也找不到了,这就是内存泄漏。

void test()
{
	int flag = 1;
	int*p = (int*)malloc(100);
	if (p == NULL)
	{
		return;
	}
	if (flag)
		return;
	free(p);
	p = NULL;
}

int main()
{
	test();
	return 0;
}

四、柔性数组

(一)柔性数组的特点

1、结构中的柔性数组成员前面必须至少一个其他成员。

2、sizeof 返回的这种结构大小不包括柔性数组的内存。

3、包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

struct S {
	int n; 
	int arr[];
};

int main() {
	struct S* p = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 10);
	if (p == NULL) {
		perror("malloc");
	}
	struct S* temp = (struct S*)realloc(p, sizeof(struct S) + sizeof(int) * 20);
	if (temp == NULL) {
		perror("realloc");
	}
	p = temp;
	return 0;
}

在这里插入图片描述

(二)柔性数组的优势

相比于上面的代码,肯定也会有疑惑,为什么不在结果体中设置一个指针变量进行动态内存开辟空间。
那我们就进行探究

struct S
{
	int n;
	int* arr;
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	int*tmp = (int*)malloc(20*sizeof(int));
	if (tmp != NULL)
	{
		ps->arr = tmp;
	}
	else
	{
		return 1;
	}
	tmp = (int*)realloc(ps->arr, 40*sizeof(int));
	if (tmp != NULL)
	{
		ps->arr = tmp;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

我们发现柔性数组的优势:
1、提高访问速度:
连续的内存有益于提高访问速度,也有益于减少内存碎片
2、方便内存释放:
如果用动态内存开辟的方式,我们要释放两次才能释放掉内存空间,因此柔性数组更优秀。

五、有趣的题目

1、求下面代码的输出结果

void GetMemory(char* p)
{
    p = (char*)malloc(100);
}

void Test(void)
{
    char* str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}

这个代码有两个问题:
1、 传入函数中的为NULL,动态分配空间GetMemory 函数结束后就无法访问到了,造成内存泄漏。
2、strcpy 函数中,如果对空指针进行解引用操作,导致程序崩溃。
可查看strcpy详解

2、求下面代码的输出结果

int main() {
	char* p = (char*)malloc(sizeof(int) * 10);
	strcpy(p, "hello");
	free(p);         //这里没有及时的将 p 置空造成了野指针
	if (p) {         //发生非法访问
		strcpy(p, "what can i see!");
	}
	return 0;

根据代码中分析,这段代码在运行时也会导致程序崩溃。

六、结束语

小伙伴看了这篇文章感觉如何呢,如果能有收获是值得高兴的,如果觉得有帮助的化,别忘记点赞支持支持哦!

在这里插入图片描述

标签:malloc,return,管理,int,C语言,内存,动态内存,sizeof,NULL
From: https://blog.csdn.net/2301_81454749/article/details/140857579

相关文章

  • Springboot计算机毕业设计安全生产目标管理系统j048q
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表部门,用户,安全生产总目标,安全生产责任状,目标考核,明细分类,安全生产明细目标开题报告内容一、课题背景随着社会经济的快速发展和工业化进程的加快,安全生产......
  • Springboot计算机毕业设计安稳汽车租赁管理系统mld41
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,汽车品牌,汽车分类,汽车租赁,租赁订单,消息通知,还车记录,车辆续租开题报告内容一、研究背景及意义研究背景随着汽车租赁市场的快速发展,越来越多的人选......
  • C语言基础8数组
    什么是数组数组是相同类型,有序数据的集合。数组的特征数组中的数据被称为数组的元素,是同构的数组中的元素存放在内存空间里  衍生概念:下标(索引)下标或索引代表了数组中元素距离第一个元素的偏移位置数组中元素的地址值,下标越大,地址值越大。(每一块内存空间都有一个独有的......
  • C语言基础7循环结构
    什么是循环        代码的重复执行,就叫做循环。循环的分类无限循环:程序设计中尽量避免无限循环。(程序中的无限循环必须可控)有限循环:循环限定循环次数或者循环的条件循环的构成循环体循环条件当型循环的实现while语法:while(循环条件){循环语句;}......
  • C语言基础6分支结构
    分支结构分支结构:又被称之为选择结构概念选择结构:根据条件成立与否,选择相应的操作条件建构关系表达式:含有关系运算符的表达式(<,>,>=,<=,!=,==)逻辑表达式:含有逻辑运算符的表达式(&&,||,!),往往用来构建复杂的符合条件:比如:常量/变量:值是否非0,取值(0|1)注意:类似以下写法,没有意义......
  • C语言运算符优先级口诀
    口诀内容(优先级自上而下递减;由逗号分隔的,优先级自左到右递减。)圆方括号,箭头句号。单目增减非反负,针强址长,从右。乘除求模,加减,位移,大小,等不等。位与异或,逻辑与或。条件赋值均右。真逗。解释(斜体字是补全;加粗字是对整行的说明;代码块即是所对应操作符。)圆括号()方括号[]......
  • 函数名冲突导致的C语言“conflicting types”编译错误
    快速解答:啊,看来你也遇到了“conflictingtypes”——类型冲突编译错误。如果你不是遇到:循环引用而没有用宏定义来解决。声明或定义在调用后面。声明和定义冲突。.h.gch未更新。那么我想告诉你,你可跟我一样忘了C语言不支持“函数重载”,即你的函数名不能重复。所......
  • C语言运算符优先级口诀
    口诀内容(优先级自上而下递减;由逗号分隔的,优先级自左到右递减。)圆方括号,箭头句号。单目增减非反负,针强址长,从右。乘除求模,加减,位移,大小,等不等。位与异或,逻辑与或。条件赋值均右。真逗。解释(斜体字是补全;加粗字是对整行的说明;代码块即是所对应操作符。)圆括号()方括号[]......
  • SSM大学生健康管理系统w60mk 丰富有趣系统界面在最后面
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表系统内容:学生,医生,体检报告,报告分析,疾病记录,健康监测,健康预警,健康评测,综合建议开题报告内容一、选题背景与意义随着社会的快速发展和人们生活水平的提......
  • Mysql--权限与安全管理
    前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除一、权限表MySQL服务器通过权限表来控制用户对数据库的访问,权限表存放在MySQL数据库中,由MySQL_install_db脚本初始化。存储账户权限信息的表主要有user、db、host、tables_priv、columns_priv和proc......