动态内存分配函数 | free为什么只传入一个指针就能正确释放

文章目录

        • 1.Linux内存分布图
        • 2.C标准库中动态内存分配函数
        • 3.动态内存分配函数的常见错误

1.Linux内存分布图

在程序设计当中,可以定义全局变量也可以定以局部变量,分别也是在全局区、栈区开辟,那么这些区域都不有用我们动手管理,但是我们在堆上动态开辟的空间需要我们手动管理!

当然,在程序设计中使用的是虚拟内存,在操作系统层面上是按照的方式分配空间的,所有即使我们向堆申请1个字节的空间都不仅仅只是一个字节,所以小空间的变量要使用malloc来分配!

在这里插入图片描述

  • 代码区(text):二进制代码,字面值常量,const修饰的具有常属性变量
  • 数据区(data):包括已初始化的全局(包括静态和非静态)变量和静态局部变量
  • BSS区:包括未初始化的(包括静态和非静态)变量和静态局部变量(进程成加载此区即被清0)注:有时候静数据区和BSS区被合称为全局区或静态区
  • 堆(heap):动态内存分配,从低地址向高地址扩展,程序员动手管理的区域
  • 文件映射或共享区:动静态库
  • 栈(stack):非静态的局部变量,函数参数和返回值,从高地址向低地址扩展;栈的大小有一个固定值,可以通过调系统参数改变
  • 命令行参数和环境变量

测试代码:

#include<stdio.h>       
#include<stdlib.h>    
         
int g_val = 5;
static int static_g_val = 5;

const int const_g_val = 5;
static const int static_const_g_val = 5;
int g_unval;    
         
int main(int argc,char* argv[],char* env[])    
{      
      static int static_tmp = 10;    
      const char* constant = "hello world";    
         
      printf("env addr:%p\n",env[0]);    
      printf("argv addr:%p\n",argv[0]);    
          
      char* heap1 = (char*)malloc(5);                                                                         
      char* heap2 = (char*)malloc(5);    
      char* heap3 = (char*)malloc(5);    
      
      printf("statck1 addr:%p\n",&heap1);    
      printf("statck2 addr:%p\n",&heap2);                                   
      printf("heap3 addr: %p\n",heap3);
    
      printf("g_unval adrr:%p\n",&g_unval);
      printf("static_tmp adrr:%p\n",&static_tmp);
      printf("static_g_val adrr:%p\n",&static_g_val);
      printf("g_val adrr:%p\n",&g_val);
      printf("constant adrr:%p\n",constant);
      printf("static_const_g_val adrr:%p\n",&static_const_g_val);
      printf("const_g_val adrr:%p\n",&const_g_val);
      printf("code adrr:%p\n",&main);
  
      return 0;
}

输出结果:

env addr:0x7ffe3ca5f812
argv addr:0x7ffe3ca5f809
statck1 addr:0x7ffe3ca5f228
statck2 addr:0x7ffe3ca5f220
heap3 addr: 0x1277050
g_unval adrr:0x60104c
static_tmp adrr:0x601044
static_g_val adrr:0x601040
g_val adrr:0x60103c
constant adrr:0x400790
static_const_g_val adrr:0x400888
const_g_val adrr:0x400884
code adrr:0x40059d
2.C标准库中动态内存分配函数
void* malloc (size_t size)
功能:在堆上申请一块连续可用的空间
参数:申请的字节数,如是0,标准未定义,有编译器决定
返回值:返回指向这块空间的指针,否则返回NULL
void* calloc (size_t num, size_t size);
介绍:函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0

关于realloc函数:

void* realloc (void* ptr, size_t size);
功能:realloc 函数就可以做到对动态开辟内存大小的调整。
参数:
    ptr 是要调整的内存地址
    size 调整之后新大小

在这里插入图片描述

情况2:原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用

在这里插入图片描述

free函数

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


在64位Linux机器,用的是glibc2.7版本:注:在Windows下测试是由很大不同的

示例代码:

```c
int main()
{
	int* ptr1 = (int*)malloc(sizeof(int));
	int* ptr2 = (int*)malloc(sizeof(int));
	double* ptr3 = (double*)malloc(sizeof(double));
	printf("ptr1:%x ,ptr2:%x ,ptr3:%x\n", ptr1, ptr2,ptr3);

	free(ptr2);
	ptr2 = NULL;
	int* ptr4 = (int*)malloc(sizeof(int));

	printf("ptr1:%x ,ptr3:%x ,ptr4:%x\n", ptr1, ptr3,ptr4);

	void *ptr5 = realloc(ptr4,25/*25是一个阈值*/); 
	printf("ptr1:%x ,ptr3:%x ,ptr5:%x\n", ptr1, ptr3,ptr5);
	
	return 0;
}

输出结果:

ptr1:24d1010 ,ptr2:24d1030 ,ptr3:24d1050
ptr1:24d1010 ,ptr3:24d1050 ,ptr4:24d1030
ptr1:24d1010 ,ptr3:24d1050 ,ptr5:24d1070

每个地址都是16的整数倍;而且指针之间有很大的空间比如,ptr1ptr2隔着32个字节比int4个字节要多不少!

为什么会输出这样的结果,另外free的参数只传入一个指针它怎么知道要释放多少个字节

使用malloc函数申请空间,在返回地址前(当然,有可能在后面,不同的实现不同)有个控制信息,记录着malloc开辟空间的管理信息,比如,开辟空间的大小,所以在free释放的时候会根据传入的地址读取控制信息!malloc开辟空间,会根据一定的对齐规则来管理!显然这里地址都是16的整数倍,对齐数是16!

在这里插入图片描述

总的来说:malloc根据一定的对齐规则,开辟空间并自动的开辟了对应的控制信息管理开辟的空间!当然,malloc一般申请的是33页,然后free不会立刻归还系统!

ptr4 和ptr2的指针为什么相同

free释放的资源不会立刻归还给系统,如果对一块空间释放,那么下次申请资源如果释放的空间大小合适,会通过双链表查找到释放的位置重新利用,这也避免一定的内碎片问题!

3.动态内存分配函数的常见错误
  1. 函数返回值都是指针,避免申请的失败对空指针的解引用
  2. 对动态开辟空间的越界访问
  3. 对非动态开辟内存使用free释放
  4. 使用free释放一块动态开辟内存的一部分
  5. 对同一块动态内存多次释放
  6. 动态开辟内存忘记释放导致内存泄漏问题

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【第六天】c++虚函数多态

一、多态的概述 多态按字面的意思就是多种形态。当类之间存在层次结构&#xff0c;并且类之间是通过继承关联&#xff08;父类与子类&#xff09;时&#xff0c;就会用到多态。 C 多态意味着调用成员函数时&#xff0c;会根据调用函数的对象的类型来执行不同的函数。 静态多态&…

Java图形化界面编程——菜单组件 笔记

2.7 菜单组件 ​ 前面讲解了如果构建GUI界面&#xff0c;其实就是把一些GUI的组件&#xff0c;按照一定的布局放入到容器中展示就可以了。在实际开发中&#xff0c;除了主界面&#xff0c;还有一类比较重要的内容就是菜单相关组件&#xff0c;可以通过菜单相关组件很方便的使用…

【Spring MVC篇】参数的传递及json数据传参

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Spring MVC】 本专栏旨在分享学习Spring MVC的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、普通参数的传…

什么是智慧隧道,如何建设智慧隧道

一、隧道管理的难点痛点 近年来隧道建设规模不断扩大&#xff0c;作为隧道通车里程最多、规模最大的国家&#xff0c;截至2022年底&#xff0c;我国公路隧道共有24850处、2678.43万延米&#xff0c;其中特长隧道1752处、795.11万延米&#xff0c;长隧道6715处、1172.82万延米。…

【数学建模】【2024年】【第40届】【MCM/ICM】【C题 网球运动中的“动量”】【解题思路】

一、题目 &#xff08;一&#xff09; 赛题原文 2024 MCM Problem C: Momentum in Tennis In the 2023 Wimbledon Gentlemen’s final, 20-year-old Spanish rising star Carlos Alcaraz defeated 36-year-old Novak Djokovic. The loss was Djokovic’s first at Wimbledon…

Android:Ionic框架使用实例

Ionic学习 ionic 是一个强大的 HTML5 应用程序开发框架(HTML5 Hybrid Mobile App Framework )。通过使用H5,JS,CSS构建接近原生体验的移动应用程序。 ionic放弃对IOS6和Android4.1以下的版本的支持,提高应用程序的运行效率。 Ionic官网地址: Ionic Framework - The Cross-Pla…

Vagrant 虚拟机工具基本操作指南

Vagrant 虚拟机工具基本操作指南 ​#虚拟机 #​ ​#vargant#​ ​#ubuntu#​ ‍ 虚拟机virtualbox ,VMWare及WSL等大家都很了解了&#xff0c;那Vagrant是什么东西&#xff1f; 它是一组命令行工具&#xff0c;可以象Docker管理容器一样管理虚拟机&#xff0c;这样快速创…

使用client-only 解决组件不兼容SSR问题

目录 前言 一、解决方案 1.基于Nuxt 框架的SSR应用 2.基于vue2框架的应用 3.基于vue3框架的应用 二、总结 往期回顾 前言 最近在我的单页面SSR应用上开发JSON编辑器功能&#xff0c;在引入组件后直接客户端跳转OK&#xff0c;但是在直接加载服务端渲染的时候一直报这…

基于springboot超市进销存系统源码和论文

随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;超市进销存系统也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变化&#xff0c;而超…

IDEA中Git的使用小技巧-Toolbar(工具栏)的设置

目录 1 前言 2 步骤 2.1 打开设置 2.2 找到Menus and Toolbars 2.3 Menus and Toolbars界面的介绍 2.4 选择工具 2.5 查看 1 前言 工具栏的合理运用&#xff0c;能够极大程度上为我们省时省力 &#xff0c;接下来我将以Git工具的添加&#xff0c;介绍如何定制我们IDEA…

应用层 HTTP协议(1)

回顾 前面我们说到了数据链路层,网络层IP协议,传输层的TCP/UDP协议一些知识点,现在让我们谈谈 应用层的HTTP协议的知识点. 这篇我们先从大局入手,仍然是对总体报文进行全局分析,再对细节报文进行拆解分析 版本 首先我们谈谈HTTP协议的版本 HTTP 0.9 (1991) HTTP 1.0 (1992 - 1…

GEE数据集——美国地质调查局历史地形图(更新)

美国地质调查局历史地形图 美国地质调查局地形图的历史可追溯到 19 世纪末&#xff0c;当时美国地质调查局开始着手绘制整个美国的详细地图。1:24,000 比例尺&#xff0c;也称为 7.5 分钟四边形地图&#xff0c;成为最广泛使用的比例尺之一。每张地图覆盖 7.5 分经纬度的区域&a…

从信息隐藏到功能隐藏

本文主要记录复旦大学张新鹏教授于2022年12月在第三届CSIG中国媒体取证与安全大会上的汇报

机器学习复习(8)——逻辑回归

目录 逻辑函数&#xff08;Logistic Function&#xff09; 逻辑回归模型的假设函数 从逻辑回归模型转换到最大似然函数过程 最大似然函数方法 梯度下降 逻辑函数&#xff08;Logistic Function&#xff09; 首先&#xff0c;逻辑函数&#xff0c;也称为Sigmoid函数&#…

【C++第二阶段】赋值运算符重载

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 赋值运算符重载 赋值运算符重载 实验①&#xff0c;还没有对析构运算符重载时 #include<iostream> #include<string> using namespace std;clas…

【Chrono Engine学习总结】3-地型terrain

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 1、关于物体材质 在介绍地型之前&#xff0c;要初步了解chrono中关于材质的一些基本概念。 首先&#xff0c;最基本的材质类是ChMaterialSurface,其进一步包括&…

爬虫练习——动态网页的爬取(股票和百度翻译)

动态网页也是字面意思&#xff1a;实时更新的那种 还有就是你在股票这个网站上&#xff0c;翻页。他的地址是不变的 是动态的加载&#xff0c;真正我不太清楚&#xff0c;只知道他是不变的。如果用静态网页的方法就不可行了。 静态网页的翻页&#xff0c;是网址是有规律的。 …

MATLAB知识点: intersect、union、setdiff和setxor函数 交集、并集、差集和对称差集

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章 3.4.5 集合运算 intersect、union、setdiff和se…

Open CASCADE学习|保存为STL文件

STL (Stereolithography) 文件是一种广泛用于3D打印和计算机辅助设计 (CAD) 领域的文件格式。它描述了一个三维模型的表面而不包含颜色、材质或其他非几何信息。STL文件通常用于3D打印过程中&#xff0c;因为它们仅包含构建物体所需的位置信息。 由于STL文件只包含表面信息&am…

初步探索Pyglet库:打造轻量级多媒体与游戏开发利器

目录 pyglet库 功能特点 安装和导入 安装 导入 基本代码框架 导入模块 创建窗口 创建控件 定义事件 运行应用 程序界面 运行结果 完整代码 标签控件 常用事件 窗口事件 鼠标事件 键盘事件 文本事件 其它场景 网页标签 音乐播放 图片显示 祝大家新…