动态内存分配

news2024/11/17 1:39:11

目录

一、内存使用方式 

(一)一个c/c++编译的程序占用的内存分为以下几个部分

二、malloc

(一)malloc

1. 举例:malloc(4) 

2. 如何理解malloc(size(Var_T)*N)

3. 举例 

(二)静态、全局指针和malloc 

二、free

(一)free释放内存前后 

(二)将已经释放的值赋值NULL

(四)常见的动态内存错误

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

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

3. 对非动态开辟内存使用free释放 

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

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

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

三、calloc

(一)calloc

(二)用memset和malloc 实现calloc一样的功能 

1. memset

2. 举例 

四、realloc 

(一)realloc在调整内存空间的是存在两种情况

1. 原有空间之后有足够大的空间 

2. 原有空间之后没有足够大的空间


一、内存使用方式 

(一)一个c/c++编译的程序占用的内存分为以下几个部分

  1. 代码区:存放程序的二进制代码
  2. 常量区:所有常量存放在常量区,程序结束后,由操作系统释放
  3. 静态区(全局区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量的存储是放在一块的,未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放 
  4. 堆区(heap):一般由程序员分配释放,若程序员不释放,程序技术后可能由操作系统回收,与数据结构中的堆不一样 
  5. 栈区(stack):由编译器自动分配释放、存放函数的参数值,局部变量的值等。其操作方法类似于数据结构中的栈
  • 注意:凡是定义在方法中的都是局部变量(方法外的是全局变量)

 

二、malloc

  • c语言函数库分别提供了两个函数,malloc和free分别用于动态内存分配释放,这些函数维护一个可用的内存池。当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。当分配的内存不再使用的时候,程序调用free函数把它归还给内存池。

(一)malloc

 

  • malloc的参数就是需要分配的字节(字符)数,,如果内存池中的内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针
  • malloc所分配的是一块连续的内存。同时,malloc实际分配内存有可能比请求的稍微多一些,但是这个行为是由编译器定义的
  • 如果内存池是空的,或者它的可用内存无法满足需求时,malloc函数向操作系统请求,要求得到更多的内存,并在这块新内存上执行分配任务,如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,对每个从malloc返回的指针都需要进行检查,确保它并非NULL
  • malloc又是如何知道所请求的内存需要存储的是整数、浮点数、结构还是数组呢?它并不知情---malloc返回一个类型为void*的指针,正是源于这个原因,标准表示一个void*类型的指针可以转换为其他任何类型的指针,但是,有些编译器,可能会要求在转换的时候使用强制类型转换

1. 举例:malloc(4) 

  • 表示从堆中申请4个字节的内存,并且这部分内存是连续的 
(1)malloc()函数会返回一个void*类型的值,也就是分配出来的那块内存的第一个字节编号,也就是那块内存的首地址,通过首地址对那块内存进行访问
(2)void* p=malloc(4);
p指向那块内存的首地址,但是在利用*p定位内存的时候,由于变量p的返回类型是void*,定位出来的内存没有办法知道是什么变量类型,也就不知道大小等信息,因此malloc返回的值需要强制转换成一个有意义的变量类型。
例如:
int *p=(int*)malloc(4) 

2. 如何理解malloc(size(Var_T)*N)

如:int a[10];
(1)变量a对应10个连续int组成的内存块,这块内存的表示值类型是int*
          int*p=a;
(2)malloc(sizeof(int)*10)申请10个连续int的空间
解释为:申请一个int[10]空间,则该空间表示值类型应该为int* 
      int *p=(int*)malloc(sizeof(int)*10);

3. 举例 

#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
	int* ptr = NULL;
	ptr = (int*)malloc(5 * sizeof(int));
	if (ptr != NULL)//判断ptr指针是否为空
	{
		int i = 0;
		for (i = 0; i < 5; i++)
		{
			*(ptr + i) = 0;
		}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;
	return 0;
}

(二)静态、全局指针和malloc 

  • 初始化静态或全局变量时,不能调用函数,下面的代码声明一个静态变量,并试图用malloc初始化:
static int* pi = malloc(sizeof(int));//会产生错误信息,对于全局变量也一样
  • 对于静态变量,可以通过在后面用一个单独的语句给变量分配内存来避免这个问题
static int* pi;
pi = malloc(sizeof(int));
  • 对于全局变量,不能用单独的赋值语句,因为全局变量在函数可执行语句外部声明,而赋值语句这类代码必须出现在函数中

二、free

 

  • free的参数是由malloc,calloc或者realloc函数分配的内存地址,这块内存会被返还给堆。尽管指针仍然指向这块区域,但是此时应该将它看成指向垃圾数据。稍后可能重新分配这块区域,并将其装进不同的数据

(一)free释放内存前后 

//举例
#include <iostream>
#include<stdlib.h>
using namespace std;
int main()
{
	int* p = (int*)malloc(sizeof(int));
	if (p == NULL)
		exit(-1);
	*p = 20;
	cout << &p << " " << p << endl;//000000C61DDAFAD8 000002B2956376C0
	free(p);
	cout << &p << " " << p;//000000C61DDAFAD8 000002B2956376C0
	return 0;
}
  •  free函数执行前后瞬间内存的分配情况

  • 虚线框表示内存已经释放,但是仍然有可能包含原值,p变量仍然指向地址000002B2956376C0,这种情况称为迷途指针

(二)将已经释放的值赋值NULL

int* p = (int*)malloc(sizeof(int));
free(p);
p = NULL;

   

  •  这种方法可以解决迷途指针类问题

(四)常见的动态内存错误

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

void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}

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

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

3. 对非动态开辟内存使用free释放 

//错误举例:
int num;
int* p = &mun;
free(p);

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

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

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

int* p = (int*)malloc(sizeof(int));
int* p2 = p;
free(p);
.......
free(p2);

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

char* p;
while (1)
{
	p = (char*)malloc(sizeof(char));
	cout << endl;
}
  • p变量指向堆上的内存。然而,在它指向另外一块内存之前没有释放这块内存,最终,程序会用光内存然后非正常终止,即使没有终止,至少内存的利用效率也不高 

三、calloc

(一)calloc

 

  • calloc也用于动态内存分配,calloc的参数包括所需元素的数量每个元素的字节数,根据这些值,它能够计算出总共需要分配的内存
  • calloc在返回指向内存的指针之前会把它初始化为0 
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0 
#include <iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	assert(p != NULL);
	free(p);
	p = NULL;
	return 0;
}

(二)用memset和malloc 实现calloc一样的功能 

1. memset

 

2. 举例 

int* p = (int*)calloc(5, sizeof(int));
//等价形式
int* p = (int*)malloc(5 * sizeof(int));
memset(p, 0, sizeof(int));

四、realloc 

  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值调整之后内存起始位置(新指针)
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 的空间 
  • realloc函数用于修改一个原先已经分配的内存块大小

(一)realloc在调整内存空间的是存在两种情况

1. 原有空间之后有足够大的空间 

  •  要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化

2. 原有空间之后没有足够大的空间

  • 原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用,把旧的内存复制到新区域,这样函数返回的是一个新的内存地址 
#include <iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;
int main()
{
	int* p = (int*)malloc(40);
	assert(p != NULL);
	int* p2 = (int*)realloc(p, 40);
	assert(p2 != NULL);
	p = p2;
	return 0;
}

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

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

相关文章

【数据结构与算法】队列-模拟实现队列以及设计循环队列

文章目录队列的概念链表实现栈设计循环队列总结队列的概念 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;…

图的搜索(DFS、BFS)

图的搜索&#xff08;图的遍历&#xff09;是指从图的任一顶点出发&#xff0c;访问图的所有顶点&#xff0c;且每个顶点只访问一次。 深度优先搜索 DFS概念&#xff1a; 深度优先搜索 (Depth-First Search&#xff0c;DFS)是从某个顶点v1出发对图进行搜素&#xff0c;每一步…

第八章 面向对象编程(中级)

一、访问修饰符&#xff08;P279&#xff09; 1. 基本介绍 java提供四种访问控制修饰符号&#xff0c;用于控制方法和属性&#xff08;成员变量&#xff09;的访问权限&#xff08;范围&#xff09;&#xff1a; &#xff08;1&#xff09;公开级别&#xff1a;用 public 修饰…

2018-NIPS-owards Sparse Hierarchical Graph Classifiers

2018-NIPS-owards Sparse Hierarchical Graph Classifiers Paper: https://arxiv.org/abs/1811.01287 Code: 对稀疏分类分级图 作者提出以往的图分类方法中通常使用单个全局池化步骤来聚合节点特征或手动设计的固定启发式算法&#xff0c;这样做会丢失信息&#xff0c;所以将…

ABB机器人系统输入输出信号System Input和Output详解(二)

ABB机器人系统输入输出信号System Input和Output详解(二) 上一次和大家分享了系统输入信号System Input相关的内容,具体可参考以下链接中的内容: ABB机器人系统输入输出信号System Input和Output详解(一) 本次和大家分享系统输出信号的相关内容: System Output类型: 可…

数据挖掘,计算机网络、操作系统刷题笔记38

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记38 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

91年的印度程序员开发博客网站每月已赚2500美元以及他的创业历程

他是谁 Sai Krishna &#xff0c;程序员、美食家、电影爱好者。 目前正在开发和维护的superblog.ai的收入情况&#xff08;截止2023年1月27日&#xff09;&#xff1a; 此前他是 SpotPlay 的联合创始人兼首席技术官。此外&#xff0c;他开发的应用程序和游戏的下载量已达数百…

CodePlus | C# 网页所有图片批量下载

C# 网页所有图片批量下载 文章目录C# 网页所有图片批量下载前言演示效果操作步骤第一步&#xff1a;安装CodePlus扩展库第二步&#xff1a;提取链接程序第三步&#xff1a;取网页源码第四步&#xff1a;设置前后缀第五步&#xff1a;执行下载更多演示结束语前言 今天想着换一个…

CANoe-添加自定义示例工程

CANoe自带的示例工程我们讲过很多次了。当安装CANoe软件时,会在安装界面让用户选择是否安装Sample Configurations。如果勾选了,则会在电脑的的C盘自带需要的示例工程,路径为:C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 15.3.89 当然你也可以在CANoe软…

Netty 之 DefaultPromise 源码解析

在解析Netty源码时&#xff0c;在解析NioEventLoop 创建过程中&#xff0c;有一段这样的代码。 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {if (nThreads < 0) {throw new I…

Java深拷贝和浅拷贝Map对象

目录1 将Map深拷贝到另一个Map对象当中2 浅拷贝Map1 将Map深拷贝到另一个Map对象当中 今天赋值的时候遇到的小坑 相关文章推荐&#xff1a; Java克隆方式避免频繁创建对象优化方案 https://blog.csdn.net/ZGL_cyy/article/details/126556907 1.需求说明 将一个MapA对象中所…

Fork之前创建了互斥锁,要警惕死锁问题

文章目录Fork之前创建了互斥锁&#xff0c;要警惕死锁问题使用GDB进行调试如何解决该问题&#xff1f;是否还有别的问题&#xff1f;结论参考文献Fork之前创建了互斥锁&#xff0c;要警惕死锁问题 下面的这段代码会导致子进程出现死锁问题&#xff0c;您看出来了吗&#xff1f…

【MFC】使用MFC框架(10)

MFC不仅仅是一个类库&#xff0c;而且是一个所谓的“设计框架”&#xff0c;注入了很多开发理念和设计思想。类库与框架的区别可以理解为“食材”与“火锅”套餐的区别——火锅套餐已经标明了开发者必须接受已定的一些规则&#xff0c;包括“Message Mapping消息映射机制”、“…

忽略语法细节,从整体上理解函数

从整体上看&#xff0c;C语言代码是由一个一个的函数构成的&#xff0c;除了定义和说明类的语句&#xff08;例如变量定义、宏定义、类型定义等&#xff09;可以放在函数外面&#xff0c;所有具有运算或逻辑处理能力的语句&#xff08;例如加减乘除、if else、for、函数调用等&…

配置中心-开源系统对比分析

一、为什么需要配置中心 1、配置实时生效 传统的静态配置方式要想修改某个配置只能修改之后重新发布应用&#xff0c;要实现动态性&#xff0c;可以选择使用数据库&#xff0c;通过定时轮询访问数据库来感知配置的变化。轮询频率低感知配置变化的延时就长&#xff0c;轮询频率…

运放电路中电容的作用-运算放大器

在运放电路中&#xff0c;大家可能会经常看到这么几个电容&#xff0c;分别是&#xff1a; 1、电源VCC到地 2、反馈输入输出引脚之间 3、正负两输入端之间的电容 就算不要这几个电容&#xff0c;电路好像也能工作&#xff0c;但电路设计一般都会加上&#xff0c;那么这几个电…

软件无线电之数字下变频(Matlab实例)

软件无线电之数字下变频 1 原理 在通信系统中&#xff0c;为了易于信号发射以及实现信道复用&#xff0c;传输的信号发射频率一般很高。 在接收机中&#xff0c;为了降低信号的载波频率或是直接去除载波频率得到基带信号&#xff0c;通常将接收信号与本地振荡器产生的本振信…

Java循环综合案例

文章目录Java循环综合案例案例一&#xff1a;逢 7 跳过案例二&#xff1a;数组元素求和案例三&#xff1a;判断两个数组是否相同案例四&#xff1a;查找元素在数组中的索引案例五&#xff1a;数组元素反转案例六&#xff1a;评委打分案例七&#xff1a;随机产生验证码Java循环综…

那些年我们拿下了 Zynq

小菜鸟的 Zynq 学习经验分享~ 资料来源&#xff1a;黑金 Zynq7035 开发板配套资料&#xff0c;完全适合于 Zynq 学习。 获取方式&#xff1a;【51爱电子】回复【Zynq7000】即可获取资料链接&#xff01;本资料仅供学习使用&#xff0c;切勿商用。 另外四个是关于 Altera FPGA…

跨域和cookie

本文以前端的视角来探讨浏览器的跨域和cookie问题。 一、跨域 跨域简介&#xff1a; 为什么会出现跨域&#xff1f; 出于浏览器的同源策略限制&#xff0c;浏览器会拒绝跨域请求。 什么情况下出现跨域&#xff1f; 不同源就会跨域。同源即&#xff1a;协议、域名、端口号…