C/C++ 动态内存分配与它的指针变量

news2024/11/13 18:01:38

一、什么是内存的动态分配

全局变量分配在内存中的静态存储区。局部变量(包括形参)分配在内存中的动态存储区,这个存储区是一个称为的区域。除此之外,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;
	//...后面可以继续使用p
	return 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]);
	}
	//...后面可以继续使用p
	free(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*类型,然后赋值给p3
	p2 = (char*)p3;//将p3的值转换为char*类型,然后赋值给p2
	printf("%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;
}

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

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

相关文章

HighTec 工程配置详解1

目录 HighTec 工程配置详解编译配置构建配置管理器编译属性编译步骤编译环境变量编译日志编译配置TriCore C CompilerTriCore C LinkerHighTec 工程配置详解 编译配置 构建配置管理器 管理器内,可以创建各种不同用途的配置项。例如用于生产工程的 ROM 配置,用于调试工程的…

【跨代码仓库合并方案】

1、背景&#xff1a; 1、wiser绑定的uiidA的定制修改内容和ELKO绑定的uiidB基本是一样的&#xff0c;需要手动粘贴同步&#xff0c;增加测试保障风险&#xff0c;还会浪费开发资源投入&#xff1b; 2、施耐德wiser和elko面板两套面板基本一致&#xff0c;但是经过new art升级后…

关于浙大MEM项目报考的一些高频但很少有答案的一些问题……

2024年浙大MEM提前批面试申请已经进入材料评审阶段&#xff0c;考生们按照自己的节奏备考等待即可。 针对今年的项目报考&#xff0c;其实有很多细节问题考生们平时很关注的&#xff0c;但获取官方的回应往往会有一定的难度&#xff0c;杭州达立易考教育根据平时的咨询积累了一…

【牛客】CM11链表分割

题目分析&#xff1a; 以链表head->4->2->1->6->0->8->7为例&#xff0c;分割后应该为head->4->2->1->0->6->8->7 定义两个链表&#xff0c;less存储比x小的所有节点&#xff0c;greater存储比x大的所有节点 遍历原链表&#xff0c;…

swiper不生效/切换不生效,点击切换按钮activeIndex值不对应问题@令狐张豪

原因&#xff1a;因为把new Swiper放在mounted实例化的时候可能v-for并未执行完成结构还未完全生成 错误&#xff1a;先执行了swiper实例化后循环的&#xff1b;正确&#xff1a;先循环完数据确保数据完整循环完成后再执行swiper实例化&#xff1b; 解决方案&#xff1a;watch…

临床数据 5. 如何构建微卫星不稳定性结直肠癌预后评分系统?

临床数据分析方案 桓峰基因公众号推出临床数据分析方案教程&#xff0c;整理如下&#xff1a; 临床数据 1. 临床基因突变数据如何发高分&#xff1f; 临床数据 2. 基于NGS的胃癌诊疗全过程的临床应用方案 临床数据 3. 肿瘤微小残留病灶(MRD)如何发文章&#xff1f; 临床数据 4.…

时间复杂度函数图像

复杂度一览 f(n)阶函数y1O(1)常数函数ylogxO(logn)对数函数yxO(n)线性函数yxlogxO(nlogn)线性对数函数yx^2O(n^2)二次函数yx^3O(n^3)三次函数y2^xO(2^n)指数函数 对比图一览 对比结果在线预览 参考 时间复杂度比较及时间复杂度对应函数&#xff0c;函数图像

STL标准模板库 字符与字符串 string,string_view,const char *

文章目录 字符与字符串计算机如何表达字符可显示字符控制字符关于控制字符的一个冷知识 C 语言字符C 语言中的字符类型 charchar 即整数”思想应用举例C 语言帮手函数关于 char 类型的一个冷知识 C 语言中的字符串“0结尾字符串”知识点应用举例C 语言转义符% 和 \ 的异同 C 字…

flutter 打包iOS安装包

flutter iOS Xcode打包并导出ipa文件安装包 1、 Xcode配置 1、 启动打包 1、 等待打包 1、 打包完成、准备导出ipa 1、 选择模式 1、 选择配置文件 1、 导出 1、 选择导出位置 1、 得到ipa

Python数据可视化工具——Pyecharts

目录 1 简介绘图前先导包 2 折线图3 饼图4 柱状图/条形图5 散点图6 箱线图7 热力图8 漏斗图9 3D柱状图10 其他&#xff1a;配置项 1 简介 Pyecharts是一款将python与echarts结合的强大的数据可视化工具 Pyecharts是一个用于生成echarts图表的类库。echarts是百度开源的一个数据…

火山引擎DataLeap的Data Catalog系统公有云实践 (下)

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 Data Catalog公有云遇到的挑战 Data Catalog经历了一个从0到1在火山引擎公有云部署并逐步优化和迭代发布10版本的过程&#xff0c;在这个过程中经历不少挑战&#…

Codeforces Round 888 (Div. 3)(视频讲解全部题目)

[TOC](Codeforces Round 888 (Div. 3)&#xff08;视频讲解全部题目&#xff09;) Codeforces Round 888 (Div. 3)&#xff08;A–G&#xff09;全部题目详解 A Escalator Conversations #include<bits/stdc.h> #define endl \n #define INF 0x3f3f3f3f using namesp…

数据结构:顺序表(C实现)

个人主页 水月梦镜花 个人专栏 C语言 &#xff0c;数据结构 文章目录 一、顺序表二、实现思路1.存储结构2.初始化顺序表(SeqListInit)3.销毁顺序表(SeqListDestroty)4.打印顺序表(SeqListPrint)5.顺序表尾插(SeqListPushBack)and检查容量(SeqListCheckCapacity)6.顺序表头插(Se…

大数据面试题之Elasticsearch:每日三题(六)

大数据面试题之Elasticsearch:每日三题 1. 为什么要使用Elasticsearch&#xff1f;2.Elasticsearch的master选举流程&#xff1f;3.Elasticsearch集群脑裂问题&#xff1f; 1. 为什么要使用Elasticsearch&#xff1f; 系统中的数据&#xff0c;随着业务的发展&#xff0c;时间…

如何创建一个容器并运行docker镜像

文章目录 如何创建一个容器并使用docker镜像docker命令解析nacos启动成功 访问 进入容器&#xff0c;修改配置文件 接上集 CentOS 7安装Docker https://blog.csdn.net/qq_39017153/article/details/131955100 如何创建一个容器并使用docker镜像 还是打开镜像容器官网https://hu…

JS如何获取最近一个月或指定天数的日期,并以数组的形式存储

JS如何获取最近一个月或指定天数的日期,并以数组的形式存储 代码 num为传递的天数 (传递30查最近一个月) get_date(num) {let dateArray []//获取今天日期let myDate new Date()let today myDate.getFullYear() - (myDate.getMonth() 1) "-" myDate.getDate(…

升讯威在线客服系统是如何实现对 IE8 完全完美支持的(怎样从 WebSocket 降级到 Http)【干货】

简介 升讯威在线客服与营销系统是基于 .net core / WPF 开发的一款在线客服软件&#xff0c;宗旨是&#xff1a; 开放、开源、共享。努力打造 .net 社区的一款优秀开源产品。 完整私有化包下载地址 &#x1f4be; https://kf.shengxunwei.com/freesite.zip 当前版本信息 发布…

【unity之IMGUI实践】敌方逻辑封装实现【六】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

技术实力加速企业上云,联想混合云获评专有云优秀案例入选混合云全景图四大方向

7月25-26日&#xff0c;由中国信息通信研究院、中国通信标准化协会联合主办的第十届可信云大会在京顺利召开。大会重磅发布了云计算白皮书&#xff08;2023年&#xff09;、《混合云产业全景图&#xff08;2023&#xff09;》、中国算力服务研究报告、中国云计算发展指数报告等…

Stream流之distinct去重详细用法

前言 distinct方法在Stream流中可以进行集合中的去重操作&#xff0c;但是要按照集合中的数据类型具体来定义。简单数据类型和自定义数据类型操作不同。 简单数据类型 这里以List集合为例&#xff0c;并且集合中数据类型为Integer。简单数据类型直接调用Stream中的distinct方…