动态内存管理<C语言>

news2024/11/19 13:36:42

在这里插入图片描述

✨Blog:🥰不会敲代码的小张:)🥰
🉑推荐专栏:C语言🤪、Cpp😶‍🌫️、数据结构初阶💀
💽座右铭:“記住,每一天都是一個新的開始😁😁😁
💀本章内容:《C语言动态内存管理》的介绍✨

目录

  • 动态内存函数介绍
    • malloc和free
    • calloc
    • realloc
  • 常见的动态内存错误
    • 内存泄漏
    • 不完全释放空间
    • 对非动态内存空间进行释放
  • 经典笔试题
  • C/C++程序的内存开辟
  • 柔性数组

动态内存函数介绍

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

上述的开辟空间的方式有两个特点:

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

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候就体现出了动态内存开辟的价值。

malloc和free

动态内存开辟原型:

void* malloc (size_t size);

这个函数的作用是向堆区申请一块连续的空间,并返回指向这段空间的指针。

  • 如果开辟成功则返回开辟空间的指针
  • 如果开辟失败则返回一个NULL指针,所以malloc以后记得要判断是否为NULL指针,如果是空指针则表示开辟空间失败。
  • 返回值是void*,因为malloc函数不知道使用者要开辟什么类型的空间,所以决定权交给使用者来决定。
  • 参数size_t size,则表示需要开辟多大的空间,以字节为单位。

空间释放函数:

void free (void* ptr);

free函数用来释放动态开辟的内存,如果动态内存开辟完空间,到程序结束都没有释放,那就会造成内存泄漏。所以malloc和free函数是配对使用的。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

例子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(10 * sizeof(int));
	if (NULL != ptr)//判断ptr指针是否为空
	{
		for (int i = 0; i < 10; i++)
		{
			ptr[i] = 0;
		}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;
	return 0;
}

在这里插入图片描述

这段代码的意思就是有一个ptr的指针指向一段10个整形的动态开辟空间,如果prt不为空,说明开辟成功,那我们for循环10次,每次依次把指向空间的值给掷成0,最后把ptr指向的空间释放掉,然后ptr赋成空指针,养成一个良好的习惯,做到有始有终。

calloc

calloc 函数也用来动态内存分配

void* calloc (size_t num, size_t size);

函数的功能和malloc的区别在于,calloc开辟的空间会把每个字节都初始化为0

  • num代表要开辟的个数。
  • size代表每个个数的大小是多大。
    例子:
int main()
{
	int* p = (int*)calloc(10000000000000000, sizeof(int));
	//如果p==NULL说明空间开辟失败
	if (p == NULL)
	{
		perror("calloc fail");
		return;
	}
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述
可以看到上述代码,全被初始化成了0。

realloc

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小
的调整。

void* realloc (void*ptr, size_t size);
  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

realloc在调整内存空间的是存在两种情况:

  1. 原有空间之后有足够大的空间(原地扩容)
    在这里插入图片描述

  2. 原有空间之后没有足够大的空间(异地扩容)
    在这里插入图片描述

例子:

int main()
{
	int* ptr = (int*)malloc(100);
    if (ptr == NULL)
    {
        perror("malloc fail");
        return;
    }
    //扩展容量
    ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
    if(ptr == NULL)
    {
    	perror("malloc fail");
    	return;
    }
    free(ptr);
    return 0;
}

先来看看上面的代码好不好,如果不好哪里不好?
那么拿已经malloc了空间的ptr指针作为接收返回值,如果申请失败realloc则会返回空指针,把空指针赋值给ptr不仅原本来的数据没了,还会造成内存泄漏,所以这种写法是不对的。

正确的代码应该这样写:

    int* p = (int*)realloc(ptr, 1000);
    if (p == NULL)
    {
        perror("realloc fail");
        return;
    }
    ptr = p;
    free(ptr);
    ptr = p;

新创建一个指针变量来接收realloc的返回值,如果是NULL指针直接返回,如果不是则把新指针给ptr。

常见的动态内存错误

内存泄漏

void test()
{
	int *p = (int *)malloc(100);
	if(NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	return 0;
}

不完全释放空间

void test()
{
	int *p = (int *)malloc(100);
	p++;
	free(p);//p不再指向动态内存的起始位置
}

对非动态内存空间进行释放

void test()
{
	int a = 10;
	int *p = &a;
	free(p);
}

以上代码都是常见错误,还有很多常见的错误,我就不一一列举了,谨记勿犯!规范使用。

经典笔试题

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;
}
char *GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char *str = NULL;
	str = GetMemory();
	printf(str);
}

以上代码的结果是什么?
太简单了,我不说大家应该都会🙊

C/C++程序的内存开辟

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
    束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
    分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
    回地址等。
  2. 堆区(heap):一般由使用者分配释放, 若使用者不释放,程序结束时可能由OS回收 。分
    配方式类似于链表。
  3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
    在这里插入图片描述

柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员。
但是至今为止,市面上有些编译器还没有支持柔性数组。

特点:

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

使用:

typedef struct Fortest
{
	int i;
	int a[];//柔性数组成员
}A;

int main()
{
	int i = 0;
	A* p = (A*)malloc(sizeof(A) + 100 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc fail");
		return;
	}
	p->i = 100;
	for (i = 0; i < 100; i++)
	{
		p->a[i] = i;//初始化柔性数组
	}
	free(p);
	return 0;
}

柔性数组的优势:

  1. 方便内存释放,如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
  2. 有利于访问速度,连续的内存有益于提高访问速度,也有益于减少内存碎片。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1062486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习基础之GFLOPS(2)

什么是GFLOPS 神经网络的GFLOPS&#xff08;Giga FLoating-Point Operations Per Second&#xff09;代表了神经网络模型执行计算的速度和计算能力。这可以用一个类比来解释&#xff1a; GFLOPS就像神经网络模型的"运算速度"标签。 想象你有两个数学家&#xff0c…

macOS下 /etc/hosts 文件权限问题修复方案

文章目录 前言解决方案权限验证 macOS下 etc/hosts 文件权限问题修复 前言 当在 macOS 上使用 vi编辑 /etc/hosts 文件时发现出现 Permission Denied 的提示,就算在前面加上 sudo 也照样出现一样的提示,解决方案如下; 解决方案 可以尝试使用如下命令尝试解除锁定; sudo chf…

Spring5应用之Cglib动态代理

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 前言Cglib动态代理…

STM32--基于STM32的智能家居设计与实现

本文详细介绍基于STM32F103C8T6的智能家居设计与实现&#xff0c;详细设计资料见文末链接 一、功能模块介绍 智能家居系统系统图如下所示&#xff0c;主要包括温湿度传感器、OLED液晶显示&#xff0c;WIFI物联网模块、人体红外预警模块、烟雾传感器模块、蜂鸣器模块 &#…

手边酒店V2独立版小程序 1.0.21 免授权+小程序前端

手边酒店小程序独立版酒店宾馆订房系统支持创建多个小程序&#xff0c;让每一个客户单独管理属于自己的小程序。后台支持一键入住&#xff0c;一键退款、退押金、钟点房支持微信支付、模板消息。客服实时收到新的订单信息&#xff0c;可以在手机端处理订单。支持按日期维护房价…

浅谈wor2vec,RNN,LSTM,Transfermer之间的关系

浅谈wor2vec&#xff0c;RNN&#xff0c;LSTM&#xff0c;Transfermer之间的关系 今天博主谈一谈wor2vec&#xff0c;RNN&#xff0c;LSTM&#xff0c;Transfermer这些方法之间的关系。 首先&#xff0c;我先做一个定位&#xff0c;其实Transfermer是RNN&#xff0c;LSTM&…

ActiveMQ消息中间件介绍

一、ActiveMQ简介 ActiveMQ是Apache出品&#xff0c;最流行的&#xff0c;能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMS Provide实现。尽管JMS规范出台已经是很久的事情了&#xff0c;但是JMS在当今的J2EE应用中仍然扮演这特殊的地位。 二、Active…

【逐步剖C】-第十一章-动态内存管理

一、为什么要有动态内存管理 从我们平常的学习经历来看&#xff0c;所开辟的数组一般都为固定长度大小的数组&#xff1b;但从很多现实需求来看需要我们开辟一个长度“可变”的数组&#xff0c;即这个数组的大小不能在建立数组时就指定&#xff0c;需要根据某个变量作为标准。…

小样本学习——匹配网络

目录 匹配网络 &#xff08;1&#xff09;简单介绍&#xff1a; &#xff08;2&#xff09;专业术语 &#xff08;3&#xff09;主要思想 &#xff08;4&#xff09;训练过程 问题 回答 MANN 匹配网络 &#xff08;1&#xff09;简单介绍&#xff1a; Matching netwo…

【C++设计模式之装饰模式:结构型】分析及示例

装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许在运行时动态地给一个对象添加额外的行为。 描述 装饰模式通过创建一个包装器&#xff08;Wrapper&#xff09;来包裹原始对象&#xff0c;并在原始对象的行为前后添加额外的功能。…

JAVA学习(5)-全网最详细~

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

git提交代码的流程

1.拉取代码 当你进入了一家公司就需要拉去公司的代码进行开发,此时你的项目小组长会给你个地址拉代码, git clone 公司项目的地址 此时如果不使用了这个方式拉去代码,拉去的是master分支上的代码,但是很多数的情况下&#xff0c;公司的项目可能会在其它的分支上,因此到公…

XXL-JOB源码梳理——一文理清XXL-JOB实现方案

分布式定时任务调度系统 流程分析 一个分布式定时任务&#xff0c;需要具备有以下几点功能&#xff1a; 核心功能&#xff1a;定时调度、任务管理、可观测日志高可用&#xff1a;集群、分片、失败处理高性能&#xff1a;分布式锁扩展功能&#xff1a;可视化运维、多语言、任…

微信小程序代驾系统源码(含未编译前端,二开无忧) v2.5

简介&#xff1a; 如今有越来越多的人在网上做代驾&#xff0c;打造一个代驾平台&#xff0c;既可以让司机增加一笔额外的收入&#xff0c;也解决了车主酒后不能开发的问题&#xff0c;代驾系统基于微信小程序开发的代驾系统支持一键下单叫代驾&#xff0c;支持代驾人员保证金…

【15】c++设计模式——>抽象工厂模式

在海贼世界中&#xff0c;位于水之都的弗兰奇一家是由铁人弗兰奇所领导的以拆船为职业的家族&#xff0c;当然了他们的逆向工程做的也很好&#xff0c;会拆船必然会造船。船是海贼们出海所必备的海上交通工具&#xff0c;它由很多的零件组成&#xff0c;从宏观上看它有这么几个…

数据结构--》探索数据结构中的字符串结构与算法

本文将带你深入了解串的基本概念、表示方法以及串操作的常见算法。通过深入理解串的相关概念和操作&#xff0c;我们将能够更好地应用它们来解决算法问题。 无论你是初学者还是进阶者&#xff0c;本文将为你提供简单易懂、实用可行的知识点&#xff0c;帮助你更好地掌握串在数据…

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore

【云笔记篇】Microsoft OneNote笔记插件推荐OneMore OneMore插件是一款非常强大&#xff0c;多达一百多个扩展功能的OneNote笔记插件&#xff0c;而且免费开源&#xff0c;不断更新的优秀插件—【蘇小沐】 1、实验 【OneMore官网&#xff1a;OneMore - a OneNote add-in (on…

使用idea 中的rest 将 git 合并部分分支代码到主分支

需求&#xff1a;当要将dev的分支中的部分代码合并到test分支时&#xff0c;又不想把dev的全部代码合并到test分支 例如dev分支已经提交了 demo1到4&#xff0c;到想把demo1-3的代码合并到test分支&#xff0c;demo4暂时不合并 可以使用idea的reset 功能满足以上需求 1首先切…

Activity之间数据回传【Android、activity回传、结合实例】

任务要求 在Android应用中&#xff0c;有时需要从一个Activity向另一个Activity传递数据&#xff0c;并在第二个Activity处理后将结果传递回第一个Activity。 这种情况下&#xff0c;我们可以使用startActivityForResult()和onActivityResult()方法来实现数据回传。 实现步骤…