学C的第三十二天【动态内存管理】

这篇具有很好参考价值的文章主要介绍了学C的第三十二天【动态内存管理】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

学C的第三十一天【通讯录的实现】_高高的胖子的博客-CSDN博客

 =========================================================================

                     

1 . 为什么存在动态内存分配

学到现在认识的内存开辟方式有两种:

              

  • 创建变量:       

int val = 20;        ——        在栈空间上开辟4个字节

  • 创建数组:       

char arr[10] = {10};        ——        在栈空间上开辟10个字节的连续空间

                     

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     

                     

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

               

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

                   

但是对于空间的需求不仅仅是上述的情况

有时候我们需要的空间大小在程序运行的时候才能知道

数组的编译时开辟空间的方式就不能满足了。

这时候就只能试试动态内存开辟了。

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 动态内存函数的介绍

(1). malloc 和 free :

                 

malloc :

该函数是一个动态内存开辟的函数

这个函数可以向内存申请一块连续可用的空间

返回指向这块空间的指针

               

书写格式如下

void* malloc (size_t size); 

                

  • 如果开辟成功,则返回一个指向开辟好空间的指针

                

  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查

                  

  • 返回值的类型void* ,所以malloc函数并不知道开辟空间的类型
  • 具体在使用的时候使用者自己决定

               

  • 如果参数 size 为0malloc行为是标准未定义的,取决于编译器

             

  • malloc声明在 stdlib.h 头文件中。
示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                       

free :

malloc函数申请的内存空间程序退出时才会还给操作系统

如果程序不退出,动态申请的内存不会主动释放的。

所以需要 free函数释放动态内存

               

书写格式如下

void free (void* ptr);

                

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

        

  • 如果参数 ptr 是NULL指针,则函数什么事都不做

           

  • free声明在 stdlib.h 头文件中。
示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(2). calloc :

         

书写格式如下

void* calloc (size_t num, size_t size);

                

  • 函数的功能以 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

               

  • 如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。

               

  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0

              

  • calloc 声明在 stdlib.h 头文件中。
示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(3). realloc :

         

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

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,

那为了合理地使用内存,我们一定会对内存的大小做灵活的调整

realloc 函数就可以做到对动态开辟内存大小的调整

                                

书写格式如下

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

                                  

  • ptr 要调整的内存地址,如果填的是NULL空指针,那会开辟一块新的空间跟malloc函数一样

                  

  • size 调整之后新大小

                  

  • 返回值 调整之后的内存起始位置

                  

  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

                  

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

情况1 --  原有空间之后足够大的空间:

在这种情况下,要扩展内存直接在原有内存之后直接追加空间

原来空间的数据不发生变化

                           

情况2 --  原有空间之后没有足够大的空间:

在这种情况下,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用

旧的空间中的数据拷贝到新的空间中,再释放旧的空间,最后返回新空间的起始地址

这样函数返回的就是一个新的内存地址

                  

  • realloc 声明在 stdlib.h 头文件中。
示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

3 . 常见的动态内存错误

(1). 对NULL指针的解引用操作:

                      

malloccallocrealloc函数 可能开辟空间
    开辟空间就有可能会失败返回 NULL空指针
    这时解引用该空指针就可能会出问题

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(2). 对动态开辟空间的越界访问:

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(3). 对非动态开辟内存使用free函数释放:

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(4). 使用free函数释放一块动态开辟内存的一部分:

                      

使用动态空间过程中

改变指向动态空间的指针

这时要使用free函数释放空间就会出问题

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(5). 对同一块动态内存多次释放:

                      

可以在释放动态空间后

将该空间指针设置为空指针

防止多次释放

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(6). 动态开辟内存忘记释放 -- 内存泄漏 :

                      

只有两种方式可以对动态内存进行释放

free函数 程序运行结束

所以如果 忘记释放  没释放且程序无法结束

就会造成内存泄漏

示例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

4 . 相关经典笔试题

题一:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                 

进行修改:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

               

对应代码:

//1:改前
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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;
}


//1:改后
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void GetMemory(char** p)
{
	//开辟动态空间
	*p = (char*)malloc(100);
}

void Test(void)
{
	//创建空指针:
	char* str = NULL;

	//使用该指针进行动态内存开辟:
	GetMemory(&str);

	//对动态空间赋值并使用:
	strcpy(str, "hello world");
	printf(str);

	//使用后进行释放:
	free(str);
	str = NULL;
}

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

                     


                    

题二:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

               

进行修改:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

               

对应代码:

//2:改前
#include <stdio.h>
#include <stdlib.h>

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}

void Test(void)
{
	//创建空指针:
	char* str = NULL;
	//调用上面的函数:
	str = GetMemory();
	printf(str);
}

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


//2:改后
#include <stdio.h>
#include <stdlib.h>

char* GetMemory(void)
{
	static char p[] = "hello world";
	return p;
}

void Test(void)
{
	//创建空指针:
	char* str = NULL;

	str = GetMemory();
	printf(str);
}

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

                     


                    

题三:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

             

进行修改:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

               

对应代码:

//3:改前:
#include <stdio.h>
#include <stdlib.h>

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;
}


//3:改后:
#include <stdio.h>
#include <stdlib.h>

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(str);
	str = NULL;
}

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

                     


                    

题四:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                

进行修改:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

               

对应代码:

//4:改前:
#include <stdio.h>
#include <stdlib.h>

void Test(void)
{
	//创建动态空间并接收:
	char* str = (char*)malloc(100);

	//使用动态空间:
	strcpy(str, "hello");

	//释放:
	free(str);

	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

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


//4:改后:
#include <stdio.h>
#include <stdlib.h>

void Test(void)
{
	//创建动态空间并接收:
	char* str = (char*)malloc(100);

	//使用动态空间:
	strcpy(str, "hello");

	//释放:
	free(str);
	str = NULL;

	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

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

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

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

(1). C/C++程序内存区域划分:

                

C/C++程序内存分配的几个区域:

1. 栈区(stack):

执行函数时函数内局部变量的存储单元都可以在栈上创建

函数执行结束时这些存储单元自动被释放

栈内存分配运算内置于处理器的指令集中效率很高,但是分配的内存容量有限

栈区主要存放运行函数而分配的局部变量函数参数返回数据返回地址等。

2. 堆区(heap):

一般由程序员分配释放若程序员不释放

程序结束时可能由OS(操作系统)回收

分配方式类似于链表

3. 数据段(静态区)(static)

存放全局变量静态数据程序结束后由系统释放

4. 代码段:

存放函数体类成员函数和全局函数)的二进制代码

           

图示:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

          

 有了这幅图,

我们就可以更好的理解static关键字修饰局部变量的例子了。

实际上普通的局部变量在栈区分配空间的,

栈区的特点在上面创建的变量出了作用域就销毁

但是被static修饰的变量存放在数据段(静态区)

数据段的特点是在上面创建的变量直到程序结束才销毁

所以生命周期变长

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

6 . 柔性数组

             

C99中,结构体中最后一个元素允许是未知大小的数组,这就叫做柔性数组成员

              

实例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                  

(1). 柔性数组的特点:

                    

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

              

实例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(2). 柔性数组的使用:

              

实例:

学C的第三十二天【动态内存管理】,CCC全是C,c语言

                     


                    

(3). 柔性数组的优势:

方便内存释放

如果我们的代码是在一个给别人用的函数中

在里面做了二次内存分配(使用两次malloc函数可以实现类似柔性数组的效果)

把整个结构体返回给用户

用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free

所以你不能指望用户来发现这个事

所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了

返回给用户一个结构体指针

用户做一次free就可以把所有的内存也给释放掉

             

有利于访问速度

连续的内存益于提高访问速度

也有益于减少内存碎片(两个开辟的空间中间空余的内存)文章来源地址https://www.toymoban.com/news/detail-625877.html

到了这里,关于学C的第三十二天【动态内存管理】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue 3 第三十一章:状态管理(Pinia基础知识)

    状态管理是现代 Web 应用开发中的一个重要概念。Vue 3 中的状态管理库 Pinia ,是一个基于 Vue 3 Composition API 的状态管理库,它提供了一种 简单 、 灵活 的方式来管理应用程序的状态,同时还具有 高性能 和 可扩展性 。 Pinia 在某种程度上来说,也可以被叫做 Vuex5 ,因为它结合

    2024年02月07日
    浏览(38)
  • 第三十六讲:神州无线AP胖AP模式配置与管理

          胖AP(Fat AP)配置一个开放式WLAN非常方便,需要完成的操作包括有线和无线两部分的配置。有线部分即ethernet接口的配置,保证AP能够接入Internet,无线部分的配置包括关联WLAN与VLAN,广播SSID,启用VAP,若无其他DHCP服务器的话,AP还需要启用DHCP为无线客户下发IP地址。 配置要

    2024年02月05日
    浏览(38)
  • 学习c#的第二天

    目录 C# 基本语法 using class C# 中的注释 成员变量 成员函数 类的实例化 标识符 C# C# 是一种面向对象的编程语言。在面向对象的程序设计方法中,程序由各种相互交互的对象组成。相同种类的对象通常具有相同的类型,或者说,是在相同的 class 中。 例如

    2024年02月04日
    浏览(34)
  • 学习c++的第二天

    目录 数据类型 基本数据类型 typedef 声明 枚举类型 类型转换 变量类型 变量定义 变量声明 左值(Lvalues)和右值(Rvalues) 变量作用域 C++ 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的 C++ 数据类型: 类型 布尔型 bool 字符型

    2024年02月06日
    浏览(38)
  • 算法训练第三十八天|动态规划理论基础、509. 斐波那契数 、70. 爬楼梯 、 746. 使用最小花费爬楼梯

    参考:https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 动态规划是什么 动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。 所以 动态规划中每一个状态一定是由上一个状态推导出来的 ,这一

    2024年02月04日
    浏览(31)
  • 学习Android的第十二天

    目录 Android SeekBar:拖动条控件 SeekBar 属性 SeekBar 事件 SeekBar 定制 范例: 参考文档 Android RatingBar 星级评分条 RatingBar 属性 RatingBar 样式 RatingBar 事件 范例: 官方文档 Android ScrollView 滚动视图 ScrollView 滚动到底部或顶部 设置滚动的滑块图片 隐藏滑块 设置滚动速度 官方文档 S

    2024年02月20日
    浏览(34)
  • 学习Bootstrap 5的第二天

    ​​​​​​​ 目录 前言  网格系统 网格类 网格系统规则 网格的基本结构 网格选项 从堆叠到水平 自动布局列 超小型设备 超小型设备网格实例 自动布局列 小型设备 小型设备网格实例 自动布局列 中型设备 中型设备网格实例 自动布局列 大型设备 大型设备网格实例 自动

    2024年02月10日
    浏览(38)
  • 第三章 内存管理 一、内存的基础知识

    目录 一、什么是内存 二、有何作用  三、常用数量单位 四、指令的工作原理 五、装入方式 1、绝对装入 2、可重定位装入(静态重定位) 3、动态运行时装入(动态重定位) 六、从写程序到程序运行 七、链接的三种方式 1、静态链接 2、装入时动态链接 3、运行时动态链接

    2024年02月07日
    浏览(42)
  • 预计练习两年半的安全服务练习生的第二天

            form表单是HTML语言中用于向服务器提交信息的标签         常见属性 action 指定将表单数据发送到哪个URL method 指定提交表单数据的方法,常用的有GET和POST,一般我们用POST,区别是:POST将数据作为HTTP发送,GET将数据作为URL查询字符串发送 enctype encode type,当

    2024年02月13日
    浏览(39)
  • 操作系统-笔记-第三章-内存管理

    一、第一章——操作系统的概念 二、第二章——【进程】 二、第二章——【线程】​编辑 二、第二章——【进程调度】 二、第二章——【进程同步与互斥】 二、第二章——【锁】 三、第三章——内存管理 四、第四章——文件管理 五、第五章——输入输出管理 ​​​​​

    2024年02月11日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包