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

动态内存管理<C语言>

时间:2024-06-12 23:57:20浏览次数:27  
标签:str 管理 int void C语言 char 动态内存 NULL ptr

导言

        在C语言学习阶段,指针结构体动态内存管理,是后期学习数据结构的最重要的三大知识模块,也是C语言比较难的知识模块,但是“天下无难事”,只要认真踏实的学习,也能解决,所以下文将介绍动态内存管理涉及到的一些函数以及概念。


目录

导言

为什么存在动态内存管理

malloc和free

malloc

 free

calloc和realloc

calloc

 realloc

常见的关于动态内存管理错误

1.对可能是NULL指针的引用

 2.对不是动态开辟的内存进行释放

3.对动态开辟的内存进行越界访问

4.使用free释放动态开辟内存的一部分

5.忘记内存释放(忘记free),造成内存泄漏

例题


为什么存在动态内存管理

int a;
char arr[10];

这是我们常用的用于向内存申请空间的办法,但是:

●空间开辟的空间是固定的

●数组在申明时,数组大小一旦确定,申请的内存空间不可变

在实际编写程序时,可能我们对于内存空间的需求不是固定,那么使用动态内存管理自己申请空间、自己释放空间就是一个很好的选择。


malloc和free

malloc

函数参数及其返回值

void* malloc(size_t size);
//申请size个字节的空间
//返回值,成功申请:返回开辟空间的首地址、失败:返回NULL

注意点

●返回值是void*,那么我们在实际使用时,应把它强制转化为我们需要的类型。

●与局部变量不同,开辟空间在堆区(如数组在栈区)

●malloc不会将内存空间初始化为0,这是与最大calloc区别!

●动态内存可调整(通过realloc)

使用举例:

 free

函数参数及其返回值

void free(void* ptr);
//释放动态内存申请的ptr指向的空间

注意点

●只能用来手动释放动态申请的空间,如果不是结果是未定义的

●释放空间后,只是将权限交还于操作系统,指针还指向着地址(悬空指针),应该手动将其置为NULL

●如果释放指针是NULL,那么什么也不做。

 使用举例:

#include<stdlib.h>
int main() {
	int* ptr = NULL;
	int count = 0;
	scanf("%d", &count);
	ptr = (int*)malloc(count * sizeof(int));
	free(ptr);
	ptr = NULL;
	return 0;
}

calloc和realloc

calloc

函数参数及其返回值

void* calloc(size_t num,size_t size);
//申请num个size个字节的空间,并初始化为0
//返回值,成功申请:返回开辟空间的首地址、失败:返回NULL

注意点

●开辟空间并全部初始化为0

●两个参数(num个size字节)

●其他与malloc类似

使用举例:

 realloc

 函数参数及其返回值

void* realloc(void* ptr,size_t size);
//ptr是要调整的内存地址
//size是调整之后的大小
//返回值,成功申请:返回调整空间的首地址、失败:返回NULL

使用举例:

#include<stdlib.h>
#include<stdio.h>
int main() {
	int count;
	scanf("%d", &count);
	int* ptr = (int*)calloc(count, sizeof(int));//申请count个int大小的空间
	if (ptr) {//判断是不是NULL:是否申请成功
		for (int i = 0; i < count; i++)
			ptr[i] = i;//赋值:从0开始到count-1步为1的序列
		for (int i = 0; i < count; i++)
			printf("%d ", ptr[i]);
	}
	printf("\n");
	printf("调整前的地址:%p\n", ptr);//观察动态(realloc)调整前的地址
	int* p = (int*)realloc(ptr, (count + 5) * sizeof(int));
	//申明一个新指针来接收,防止调整失败返回NULL,数据丢失,调整为多5个int大小的地址
	if (p)//判断是否是NULL:是否调整成功
		ptr = p;
	printf("调整后的地址:%p", ptr);//观察动态(realloc)调整后的地址
	free(ptr);
	ptr = NULL;
	return 0;
}

运行结果:

先开辟10个int字节大小空间的运行结果:

 先开辟20个int字节大小空间的运行结果:

注意点

●参数size为0时,返回值是NULL,并将ptr的内存释放,这是未定义的行为,在不同的编译器上不能保证

●如果ptr参数为NULL,会动态开辟一个新的内存空间,此时realloc函数的作用等同于malloc

●这个函数调整空间时会把数据移到的内存空间内(实际上,有一种情况不会,但是为了代码的健壮性和可移植性,我们最好这样定义)

两种情况:

①原有地址后面有足够的空间容纳调整后的空间

②原有地址后面没有足够的空间容纳调整后的空间

其实在前面的使用举例中我们已经观察到:

先开辟10个int字节大小空间的运行结果(第一种情况)

        直接在原地址后面开辟新空间

先开辟20个int字节大小空间的运行结果(第二种情况)

        找到一块能容纳调整后的空间的地址,将数据移动到其中

关于参数size为0时的举例:

因为我们没有办法直接观察一块动态开辟的内存是否被释放,且这种size为0行为是未定义的,所以我们只能观察它的返回值

 

 关于参数ptr是NULL时的情况

此时realloc等同malloc

注意:动态内存管理的4个函数都包含在<stdlib.h>中


常见的关于动态内存管理错误

1.对可能是NULL指针的引用

 

 2.对不是动态开辟的内存进行释放
// 2.对不是动态开辟的内存进行释放
#include<stdio.h>
#include<stdlib.h>
int main() {
	int a = 0;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

3.对动态开辟的内存进行越界访问
//3.对动态开辟的内存进行越界访问
#include<stdlib.h>
int main() {
	int* p = (int*)malloc(sizeof(int));
	p++;
	*p = 1;
	free(p);
	p = NULL;
	return 0;
}

4.使用free释放动态开辟内存的一部分
//4.使用free释放动态开辟内存的一部分
#include<stdlib.h>
int main() {
	int* p = (int*)malloc(4*sizeof(int));//动态开辟4个int大小的空间
	p++;//指向第二个元素
	free(p);
	p = NULL;
	return 0;
}

5.忘记内存释放(忘记free),造成内存泄漏
//5.忘记内存释放(忘记free),造成内存泄漏
#include<stdlib.h>
int main() {
	int* p = (int*)malloc(sizeof(int));
	return 0;
}

例题

1.

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
int main() {
	Test();
	return 0;
}运行会咋样

p虽然在GetMemory函数中开辟了内存,但是在出函数时,该地址被销毁,所以str还是NULL指针,对NULL指针进行赋值是一个未定义行为。(传值调用而没有使用传址调用)

改正(二级指针):

void GetMemory(char** p)//使用二级指针接收
{
	*p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//传入指针的地址
	strcpy(str, "hello world");
	printf(str);
}
int main() {
	Test();
	return 0;
}

改正(将开辟的空间返回):

2.

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main() {
	Test();
	return 0;
}//运行结果?

GetMemory函数返回了一个地址,但是这个地址出了函数,权限已经收回给了操作系统,str接收的是一个野指针,并将它打印出来,这种行为是未定义的,可能造成错误。(说到底是栈空间返回会被销毁的问题)

我们知道只要是函数内的变量都是栈空间申请的空间,在出函数时,都会被回收,但是动态内存管理申请的空间,必须要手动释放,所以在函数中我们使用动态内存申请的地址是不会被收回的(堆区申请),所以我们尝试改正时,在函数内部使用动态内存申请,并返回。

改正:

char* GetMemory(void)
{
	char* p = (char*)malloc(20);
	strcpy(p, "hello world!");
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main() {
	Test();
	return 0;
}

3.

//3
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}
int main() {
	Test();
	return 0;
}//运行结果,以及问题

没释放空间,内存泄漏

改正:

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
    free(ptr);
    ptr=NULL;
}
int main() {
	Test();
	return 0;
}

使用了已经被释放的内存

标签:str,管理,int,void,C语言,char,动态内存,NULL,ptr
From: https://blog.csdn.net/2302_80803681/article/details/139582113

相关文章

  • 精细化运营-银行存量客户管理的关键策略与挑战【文末送书】
    银行存量客户运营在现代金融行业中,银行面临着激烈的竞争和不断变化的市场环境。为了在这种环境中立于不败之地,银行不仅需要吸引新客户,更需要有效地运营存量客户。银行存量客户运营指的是通过各种策略和手段,提高现有客户的满意度和忠诚度,进而增加客户的终身价值。本文将探讨......
  • 数据价值管理-数据验收标准
        前情提要:数据价值管理是指通过一系列管理策略和技术手段,帮助企业把庞大的、无序的、低价值的数据资源转变为高价值密度的数据资产的过程,即数据治理和价值变现。第一讲介绍了业务架构设计的基本逻辑和思路。前面我们讲完了数据资产建设标准、数据归集标准、数据处理......
  • 初阶C语言(01)—学习笔记
    if语言if语句其一C语言被称为结构化的程序设计语言,包括顺序结构、选择结构(ifswitch)和循环结构(for while dowhile)。因为今天要下雨,所以必须带伞。这就是一个简单的选择语句。例如:如果你的年龄大于18岁,那么输出成年。#include<stdio.h>intmain(){ intage=20;......
  • 使用B树实现员工(人事)管理系统
    1.前言 使用B树来表示人事管理系统,其中每个节点代表一个人员,树的根节点为董事长,每个节点可以有多个子节点,表示下属。每一层代表一个等级分布。addPerson:添加人员功能通过查找指定上司节点,然后将新的人员作为其子节点添加。deletePerson:删除人员功能首先查找要删除人员......
  • 零基础非科班也能掌握的C语言知识21 编译链接(介于作者实力有限并且没有可以演示的过程
    编译链接1.翻译环境和运行环境2.翻译环境2.1编译2.1.1预处理(预编译)2.1.2编译2.1.3汇编2.2链接3.运行环境1.翻译环境和运行环境在ANSIC的任何⼀种实现中,存在两个不同的环境。编译环境运行环境2.翻译环境翻译环境由编译和链接两个大的过程组成的,而编译又可......
  • 基于C#开发web网页管理系统模板流程-总集篇
    第一篇基于C#开发web网页管理系统模板流程-登录界面和主界面_c#的网页编程-CSDN博客第二篇基于C#开发web网页管理系统模板流程-主界面管理员录入和编辑功能完善_c#网页设计-CSDN博客第三篇基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序......
  • C语言指针介绍加练习
    #指针相关介绍定义    指针(Pointer),通常用于数据的间接访问,指针存储的是指向变量的首地址,16位平台就是2位,如果在32位平台,地址就是4个字节,如果实在64位平台,地址就是8个字节(1Byte=8bit),Int类型4Byte char类型1Byte这个是变量在内存中,分配的地址大小,在内存中一个By......
  • 【供应链管理】供应链管理降本增效图库,包括流程图、框架图、优化图、供应商管理
    **【供应链管理】供应链管理降本增效图库**一、降本增效流程图此流程图详细展示了供应链管理过程中的降本增效环节。从需求预测、采购计划制定、供应商选择、物料入库、生产排程、物流运输到最终的销售与售后服务,每个环节都通过优化流程、减少浪费、提高效率等措施实现成本......
  • C语言王国——数组的旋转(轮转数组)三种解法
    一、题目给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。示例1:输入:nums=[1,2,3,4,5,6,7],k=3输出:[5,6,7,1,2,3,4]解释:向右轮转1步:[7,1,2,3,4,5,6]向右轮转2步:[6,7,1,2,3,4,5]向右轮转3步:[5,6,7,1,2,3,4]示例......
  • 联系人管理系统(简易版)
    1、项目介绍    本项目以sqlite3为基本框架完成一个简易的手机联系人管理系统,用户可以根据自己需要进行添加联系人、删除联系人、更新联系人、查找联系人以及退出等。2、本项目涉及到的sqlite3API①sqlite3_open()函数用于打开一个SQLite数据库文件的函数,这个函......