[C语言]-基础知识点梳理-动态内存管理

2024-08-21 07:52

本文主要是介绍[C语言]-基础知识点梳理-动态内存管理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

各位师傅大家好,我是qmx_07,今天给大家讲解动态内存管理的相关知识,下一章节更新 文件管理部分的知识点

为什么要进行动态内存分配

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

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

  • 空间开辟⼤⼩是固定的。
  • 数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整
  • 有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道,那数组的编译时开辟空间的⽅式就不能满⾜了,所以引申了动态内存的概念,让程序员自己申请和释放空间

malloc和free

malloc和free函数使用,需要加载<stdlib.h>头文件

malloc

void* malloc (size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

free

void free (void* ptr);

free函数用来释放动态开辟的内存

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。

代码演示

#include <stdio.h>
#include <errno.h>
int main()
{int arr[10] = { 0 };int* p = (int*)malloc(40);if (NULL == p){printf("%s\n", strerror(errno));return 1;}for (int i = 0; i <= 10; i++){arr[i] = i;}for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}free(p);p = NULL;return 0;
}

在这里插入图片描述

  • 使用free函数 只是将这片内存空间 还给 操作系统,内容没有发生改变,所以我们需要把这个指针置空

calloc

void* calloc (size_t num, size_t size);
  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

代码演示

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));if (NULL == p){printf("%s\n", strerror(errno));return 1;}int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}

在这里插入图片描述

  • calloc = malloc + memset

realloc

  • 概念: 通过malloc或者calloc方式申请空间,可能过小或者过大,这时候就需要使用realloc来更改已经申请的内存空间大小
void* realloc (void* ptr, size_t size);
  • ptr 指针,也就是要改变的内存地址
  • size 要改变的大小

realloc 底层剖析

在这里插入图片描述
在这里插入图片描述

  • 第一种开辟情况,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址
  • 第二种开辟情况,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化

代码演示

在这里插入图片描述
在这里插入图片描述

  • 详情参考 底层剖析解释

动态内存错误

  • 对NULL指针的解引用操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
  • 对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
  • 对非动态开辟内存使用free释放
void test()
{
int a = 10;
int *p = &a;
free(p);
}
  • 使用free释放一块动态开辟内存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//此时p的位置已经发生了变化,p不再指向动态内存的起始位置
}
  • 对同一块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
  • 动态开辟内存忘记释放(内存泄漏)
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
  • 重点:开辟空间之后,记得free置空

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

在这里插入图片描述

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

总结

  • 我们学习了malloc,calloc,realloc,以及c/c++程序的内存开辟,下一章节学习 文件管理

这篇关于[C语言]-基础知识点梳理-动态内存管理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092565

相关文章

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

从基础到进阶详解Python条件判断的实用指南

《从基础到进阶详解Python条件判断的实用指南》本文将通过15个实战案例,带你大家掌握条件判断的核心技巧,并从基础语法到高级应用一网打尽,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录​引言:条件判断为何如此重要一、基础语法:三行代码构建决策系统二、多条件分支:elif的魔法三、

Python WebSockets 库从基础到实战使用举例

《PythonWebSockets库从基础到实战使用举例》WebSocket是一种全双工、持久化的网络通信协议,适用于需要低延迟的应用,如实时聊天、股票行情推送、在线协作、多人游戏等,本文给大家介... 目录1. 引言2. 为什么使用 WebSocket?3. 安装 WebSockets 库4. 使用 We