C/C++ 动态内存分配与它的指针变量
一、什么是内存的动态分配
全局变量分配在内存中的静态存储区。局部变量(包括形参)分配在内存中的动态存储区,这个存储区是一个称为栈的区域。除此之外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。这些数据是存放在一个特别的自由存储区,称为堆区。可以根据需要向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针来引用。
也就是说,全局变量分配于静态存储区,局部变量分配于栈,动态内存分配在堆。
二、怎样建立内存的动态分配
对于内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,realloc,free这四个函数。
1.用malloc函数开辟动态内存
malloc的函数原型为:
void *malloc(unsigned int size);
malloc函数的作用是:
在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。此函数的值(即“返回值”)是所分配区域的第一个字节的首地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的第一个字节。如:
malloc(100);
//开辟100字节的临时分配域,函数值为其第一个字节的地址。
注意指针的基类型为void,即不指向任何类型的数据,只提供一个地址。如果此函数未能成功执行(例如内存空间不足),则返回空指针(NULL),失败只有一种情况,就是申请的内容太大,超出堆能提供的最大连续空间。
2.用calloc函数开辟动态内存
calloc的函数原型为:
void*calloc(unsigned n,unsigned size);
calloc的函数的作用是:
在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。
用calloc函数可以为一维数组开辟动态内存存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组。函数的指针指向所分配域的第一个字节,此函数的值(即“返回值”)是所分配区域的第一个字节的首地址;如果不成功,返回空指针(NULL)。如:
p=calloc(50,4);
//开辟50个长度为4个字节的临时分配域,把首地址赋给指针变量p
calloc和malloc最大的区别为:申请完空间之后calloc会将每个元素的值直接置为0。
3.用realloc函数重新分配动态内存
realloc函数的原型:
void *realloc(void*p,unsigned int size);
参数的意义:p:旧内存的地址;size:新申请的内存大小,以字节为单位
realloc函数的作用是:
如果已经通过malloc函数或calloc函数获得了动态内存空间,想改变其大小,可以用realloc函数重新分配。用realloc函数将p所指向的动态内存空间的大小改变为size。p的值不变。函数值成功返回时返回新的动态内存的首地址,如果重分配不成功,返回空指针(NULL)。失败只有一种情况就是申请的内容太大,超出堆能提供的最大连续空间。如:
realloc(p,50);
//将p指向的动态内存空间的大小改变为50字节
用malloc实现realloc扩容:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{int n = 10;int *p=(int*)malloc(n * sizeof(int));assert(p != NULL);int i;for (i = 0; i < n; i++){p[i] = i;}for (i = 0; i < n; i++){printf("%d ", p[i]);}printf("\n");//1发现p申请的太少,于是重新申请int*q=(int *)malloc(2* n * sizeof(int));//1申请新内存//2搬家for (i = 0; i < 2*n; i++){q[i] =i;}for (i = 0; i < 2*n; i++){printf("%d ", q[i]);}free(p);//3释放p的内存p = q;//4接收新内存q = NULL;//...后面可以继续使用preturn 0;
}
realloc实现扩容:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main()
{int n = 10;int* p = (int*)malloc(n * sizeof(int));assert(p != NULL);int i;for (i = 0; i < n; i++){p[i] = i;}for (i = 0; i < n; i++){printf("%d ", p[i]);}printf("\n");//1发现p申请的太少,于是重新申请p = (int*)realloc(p, 2 * n * sizeof(int));for (i = 0; i < 2 * n; i++){p[i] = i;}//2搬家for (i = 0; i < 2 * n; i++){printf("%d ", p[i]);}//...后面可以继续使用pfree(p);return 0;
}
4.用free函数释放动态存储区
free函数的原型:
void free(viod*p);
free函数的作用是:
释放指针变量p所指向的动态内存空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。free函数无返回值。如:
free(p);
//释放指针变量p所指向的已分配的动态内存空间
free崩溃的原因:
1.p移动了(p++),因为申请p的时候有一个n的大小,如果p移动,找不到头信息
2.找不到尾信息
3.重复释放了一段内存
以上4个函数malloc,calloc,realloc,free的声明都在stdlib.h的头文件中,在用到这些函数时应该用#include<stdlib.h>
指令把stdlib.h头文件包含在程序文件中。
三、void指针类型
C99允许使用基类型为void的指针类型。可以定义一个基类型为void的指针变量(即void*型变量),它不指向任何类型的数据。
注意:不要把“指向void类型”理解为能指向“任何类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。在将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。
例如:
int main()
{int a = 3; //定义a为整型变量int* p1 = &a;//p1指向int型变量char* p2;//p2指向char型变量void* p3;//p3为无类型指针变量(基类型为void型)p3 = (void*)p1;//将p1的值转换为void*类型,然后赋值给p3p2 = (char*)p3;//将p3的值转换为char*类型,然后赋值给p2printf("%d", *p1);//合法,输出整型变量a的值void* p3 = &a; printf("%d", *p3);//错误,p3是无指向的,不能指向a
}
说明:
地址信息应该包含位置信息和基类型的信息。 一定要注意基类型的信息,即存放存放在以此地址标志的存储单元中的数据类型,否则无法实现对数据的存取。所以对于void*
类型的指针,这种指针无指向,在这种无指向的地址所标志的存储单元中是不可以存储任何数据的,也就是说无法通过这种地址对内存存取数据。
什么情况下会用到void*类型的指针:
在调用动态存储分配函数(如malloc、calloc、realloc函数)时会用到void*
类型的指针。用户用这些函数开辟动态存储区时,希望获得此动态存储区的起始地址,以便利用该动态存储区。
C99规定malloc、calloc、realloc函数返回void*
指针,使其“无指向”,这种指针称为“空类型指针”,它不指向任一种具体的数据类型,只提供一个地址。这是C有关地址应用的一种特殊情况。
这种空类型指针在形式上和其他指针一样,遵循C语言对指针的有关规定,它也有基类型,只是它的基类型是void
,可以这样定义:
void*p; //定义p是void*型的指针变量
void*型指针代表“无指向的地址”,这种指针不指向任何类型的数据。不能企图通过它存取数据,在程序中它只是过渡性的,只有转换为有指向的地址,才能存取数据。
四、建立动态内存分配区和使用void指针
【例题】建立动态数组,输入5个学生的成绩,另外用一个函数检查其中有无低于60分的,输出不合格的成绩。
【思路】用malloc开辟一个动态自由区域,用来存放5个学生的成绩,会得到这个动态域的第一个字节的地址,他的基类型是void型。用一个基类型是int的指针变量p来指向动态数组的各元素,并输出它们的值。但必须先把malloc函数返回的void指针转换为整型指针,然后赋给p1。
#include <stdio.h>
#include <stdlib.h> //程序中用了malloc函数,应包含stdlib.h
void check(int* p)//定义check函数,形参是int*指针
{printf("不合格的成绩为:");for (int i = 0; i < 5; i++){if (p[i] < 60)printf("%d ", p[i]);//输出不合格成绩}printf("\n");
}
int main()
{int* p1 = (int*)malloc(5 * sizeof(int)); //p1是int型指针,开辟动态内存,将地址转换为int*型,然后放在p1中for (int i = 0; i < 5; i++){scanf("%d", &p1[i]);//输入5个学生的成绩}check(p1);//调用check函数free(p1);return 0;
}
五、断言assert
一种比较保守的编程方式:每一次都要验证动态申请内存的返回值是否为空,这里就需要用到断言assert,assert函数的声明都在assert.h的头文件中,在用到这个函数时应该用#include<assert.h>
指令把assert.h头文件包含在程序文件中。
【例题】输出n个连续自然数中的所有素数
void Getprimer(int n)
{int* p = (int*)malloc(n * sizeof(int));//堆assert(p != NULL);for (int i = 0; i < n; i++){p[i] = 1;}p[1] = p[0] = 0;//0和1不是素数for (int i = 2; i < n; i++){for (int j = i + 1; j < n; j++){if (j % i == 0)p[j] = 0;}}for (int i = 2; i < n; i++){if (p[i] == 1){printf("%d是素数\n", i);}}free(p);
}
int main()
{int n;printf("请输入n的大小:");scanf("%d", &n);Getprimer(n);return 0;
}
相关文章:

C/C++ 动态内存分配与它的指针变量
一、什么是内存的动态分配 全局变量分配在内存中的静态存储区。局部变量(包括形参)分配在内存中的动态存储区,这个存储区是一个称为栈的区域。除此之外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据&…...

UE5初学者快速入门教程
虚幻引擎是一系列游戏开发工具,能够将 2D 手机游戏制作为 AAA 游戏机游戏。虚幻引擎 5 用于开发下一代游戏,包括Senuas Saga: Hellblade 2、Redfall(来自 Arkane Austin 的合作射击游戏)、Dragon Quest XII: The Flames of Fate、…...

论文笔记--FEDERATED LEARNING: STRATEGIES FOR IMPROVING COMMUNICATION EFFICIENCY
论文笔记--FEDERATED LEARNING: STRATEGIES FOR IMPROVING COMMUNICATION EFFICIENCY 1. 文章简介2. 文章概括3 文章重点技术3.1 联邦学习(federated learning, FL)3.2 Structured updates3.3 Sketched Update 4. 文章亮点5. 原文传送门 1. 文章简介 标题:FEDERATE…...

STM32MP157驱动开发——按键驱动(异步通知)
文章目录 “异步通知 ”机制:信号的宏定义:信号注册 APP执行过程驱动编程做的事应用编程做的事异步通知方式的按键驱动程序(stm32mp157)button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “异步通知 ”机制: 信号的宏定义&#x…...

医疗器械维修工程师心得
彩虹医械维修技能班9月将开展本年第三期长期班,目前咨询人员也陆续多了起来,很多刚了解到医疗行业的,自身也没有多少相关的基础,在咨询时会问到没有基础能否学的会? 做了这行业的都知道,无论多么复杂的设备…...

Vue3 Radio单选切换展示不同内容
Vue3 Radio单选框切换展示不同内容 环境:vue3tsviteelement plus 技巧:v-if,v-show的使用 实现功能:点击单选框展示不同的输入框 效果实现前的代码: <template><div class"home"><el-row …...

FreeRTOS之二值信号量
什么是信号量? 信号量(Semaphore),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代 码段不被并发调用。 信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用&am…...

ChatGPT API进阶调用指南
原文:ChatGPT API进阶调用指南 ChatGPT API 进阶调用指南 ChatGPT API 是基于 OpenAI 的 GPT模型的一个强大工具,可以用于构建各种对话式应用。以下是一些使用 Markdown 语法的进阶调用指南,以帮助您更好地利用 ChatGPT API。 设置用户角色…...

人工智能术语翻译(四)
文章目录 摘要MNOP 摘要 人工智能术语翻译第四部分,包括I、J、K、L开头的词汇! M 英文术语中文翻译常用缩写备注Machine Learning Model机器学习模型Machine Learning机器学习ML机器学习Machine Translation机器翻译MTMacro Average宏平均Macro-F1宏…...

kubernetes持久化存储卷
kubernetes持久化存储卷 kubernetes持久化存储卷一、存储卷介绍二、存储卷的分类三、存储卷的选择四、本地存储卷之emptyDir五、本地存储卷之 hostPath六、网络存储卷之nfs七、PV(持久存储卷)与PVC(持久存储卷声明)7.1 认识pv与pvc7.2 pv与pvc之间的关系7.3 实现nfs类型pv与pvc…...

【Rust笔记】意译解构 Object Safety for trait
意译解构Object Safety for trait 借助【虚表vtable】对被调用成员函数【运行时内存寻址】的作法允许系统编程语言Rust模仿出OOP高级计算机语言才具备的【专用多态Ad-hoc Polymorphism】特性。 计算机高级语言中的“多态”术语是一个泛指。它通常可被细化为 基于继承关系的“子…...

Spring Boot单元测试入门指南
Spring Boot单元测试入门指南 JUnit是一个成熟和广泛应用的Java单元测试框架,它提供了丰富的功能和灵活的扩展机制,可以帮助开发人员编写高质量的单元测试。通过JUnit,开发人员可以更加自信地进行重构、维护和改进代码,同时提高代…...

《面试1v1》如何能从Kafka得到准确的信息
🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结…...

2023秋招面试题持续更新中。。。
目录 1.八股文渐进式MVVM三次握手,四次挥手viteajax组件化和模块化虚拟dom原理流程浏览器内核浏览器渲染过程回流和重绘nextTick 2.项目相关1.声明式导航和编程式导航重写push和replace方法:性能优化图片懒加载路由懒加载 http请求方式 1.八股文 渐进式…...

Java | 数组排序算法
一、冒泡排序 冒泡排序的基本思想是对比相邻的元素值,如果满足条件就交换元素值,把较小的元素移到数组前面,把较大的元素移到数组后面(也就是交换两个元素的位置),这样较小的元素就像气泡一样从底部升到顶…...

android studio 连接SQLite数据库并实现增删改查功能
功能代码及调试代码 package com.example.bankappdemo;import android.annotation.SuppressLint; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.util.Log; import android.view.View; im…...

跑步适合戴什么样的耳机、最好的跑步耳机推荐
每个人对于运动的方式都不尽相同,但大多数热爱运动的朋友都离不开音乐的陪伴。运动和带有节奏感的音乐能够激发我们更多的热情和动力。特别是在夏日的时候,我非常喜欢跑步。在酷热的天气里,如果没有音乐的伴随,跑步会变得单调乏味…...

物联网的通信协议
物联网的通信协议 目录 物联网的通信协议一、UART串口通信1.1 串口通信1.2 异步收发1.3 波特率1.4 串口通信协议的数据帧1.5 优缺点1.5.1 优点1.5.2 缺点 二、I^2^C2.1 I^2^C2.2 I^2^C2.3 数据有效性2.4 起始条件S和停止条件P2.5 数据格式2.6 协议数据单元PDU2.7 优缺点2.7.1 优…...

【业务功能篇56】SpringBoot 日志SLF4J Logback
3.5.1 日志框架分类与选择 3.5.1.1 日志框架的分类 日志门面 (日志抽象)日志实现JCL(Jakarta Commons Logging) SLF4J(Simple Logging Facade for Java)Jul(Java Util Logging) , Log4j , Log4j2 , Logback 记录型日志框架 Jul (Java Util Logging):JDK中的日志…...

leetcode 53. 最大子数组和
2023.7.28 要求找最大和的 连续子数组, 我的思路是用一个temp记录局部最优值,用ans记录全局最优值。 然后在每次for循环进行一个判断:当前遍历元素temp值 是否大于当前遍历元素的值,如果大于,说明temp值是帮了正忙的&a…...

js 下载url返回的excel数据,并解析为json
XLSX GitHub地址:https://github.com/SheetJS/sheetjs/blob/github/dist/xlsx.full.min.js 需要先引入:XLSX.full.min.js // 下载文件的请求 fetch(downloadFileUrl).then(response > {return rsp.blob() }).then(data > {let reader new FileR…...

图文教程:使用 Photoshop、3ds Max 和 After Effects 创建被风暴摧毁的小屋
推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 在 Photoshop 中设置图像 步骤 1 打开 Photoshop。 打开 Photoshop 步骤 2 我已经将小屋的图像导入到Photoshop中以演示 影响。如果您愿意,可以使用其他图像。 图片导入 步骤 3 由于小…...

学习Maven Web 应用
Maven Web 应用 本章节我们将学习如何使用版本控制系统 Maven 来管理一个基于 web 的项目,如何创建、构建、部署已经运行一个 web 应用。 创建 Web 应用 我们可以使用 maven-archetype-webapp 插件来创建一个简单的 Java web 应用。 打开命令控制台,…...

page allocation stalls for 问题调研
一.现象分析和内存管理基本概念介绍 最近有一台linux出现卡死的状态,系统不反应,无法ssh登录,只能通过电源关机重启操作恢复,重启后登录系统后台,拉取kernel日志,如下 Jul 12 18:48:06 kernel: [141294.374983] send process: page allocation stalls for 10108ms, orde…...

JUC并发工具类
一、ReentrantLock 特点:独占、可重入、公平/非公平、可中断、支持多个条件变量 1、常用api ReentrantLock实现了Lock接口,Lock类规范定义了如下方法 lock():获取锁,调用该方法的线程会获取锁,当锁获得后࿰…...

【雕爷学编程】MicroPython动手做(10)——零基础学MaixPy之神经网络KPU
早上百度搜“神经网络KPU”,查到与非网的一篇文章《一文读懂APU/BPU/CPU/DPU/EPU/FPU/GPU等处理器》,介绍各种处理器非常详细,关于“KPU”的内容如下: KPU Knowledge Processing Unit。 嘉楠耘智(canaan)号…...

MySQL~SQL语句
一、SQL 1.什么是SQL? Structured Query Language:结构化查询语言 每一种数据库操作的方式存在不一样的地方,称为“方言”。 2.SQL通用语法 SQL 语句可以单行或多行书写,以分号结尾 可使用空格和缩进来增强语句的可读性 MyS…...

从零开始构建基于YOLOv5的目标检测系统
本博文从零开始搭建基于YOLOv5模型的目标检测系统(具体系统参考本博主的其他博客),手把手保姆级完成环境的搭建。 (1)首先Windows R输入cmd命令后打开命令窗口,进入项目目录,本博文以野生动物…...

PDF尺寸修改:等比绽放(标准面单100*150mm)
PDF修改尺寸 需要注意:第一个方法返回的是转换后PDF的base64。第二个方法返回的是文件流,这个方法才是转的核心。 /*** 修改PDF尺寸** param pdfUrl PDF链接* param pdfWidthInMillimeters 指定宽 mm* param pdfHeightInMillimeters 指…...

C++ - list介绍 和 list的模拟实现
list介绍 list 是一个支持在常数范围内,任意位置进行插入删除的序列式容器,且这个容器可以前后双向迭代。我们可以把 list 理解为 双向循环链表的结构。 于其他结构的容器相比,list在 任意位置进行插入和函数的效率要高很多;而li…...