C++初学(12)

news2024/9/21 4:35:16

9bd22158e4984b7fa430b846ed1ac488.jpeg

6652ca19230f4795949e469bbc5564a0.png前段时间去旅游了,回来继续写。

 

12、指针、数组和指针算术

对上一篇进行的补充

#include <iostream>
int main()
{
	using namespace std;
	double wages[3] = { 10000.0,20000.0,30000.0 };
	short stacks[3] = { 3,2,1 };

	double* pw = wages;
	short* ps = &stacks[0];
	
	cout << "pw= " << pw << ", *pw= " << *pw << endl;
	pw += 1;
	cout << "add 1 tp the pw pointer:" << endl;
	cout << "pw= " << pw << ", *pw= " << *pw << endl << endl;
	cout << "ps= " << ps << ", *ps= " << *ps << endl;
	ps += 1;
	cout << "add 1 tp the ps pointer:" << endl;
	cout << "ps= " << ps << ", *ps= " << *ps << endl << endl;

	cout << "access two elements with array natation" << endl;
	cout << "stack[0]= " << stacks[0]
	     << ", stacks[1] = " << stacks[1] << endl;
	cout << "access two elements with pointer natation" << endl;
	cout << "*stacks= " << *stacks
		<< ",*(stacks+1)= " << *(stacks + 1) << endl;
	cout << sizeof(wages) << " =size of wages array" << endl;
	cout << sizeof(pw) << " =size of pw pointer" << endl; 
		return 0;
}

f56a513b5eea4f2eb6899b64ab495c42.png

12.1、程序说明

在一般的情况下,C++将数组名解释为数组第一个元素的地址。因此下面的语句将pw声明为指向double类型的指针。

double* pw=wages;
存在下面等式:
wages=&wages[0]=第一个元素的地址

该程序在表达式&stacks[0]中显示地使用地址运算符来将ps指针初始化为stacks数组的第一个元素。

接着来分析pw和*pw的值:前者为地址,后者为存储在该地址的值。上面提到ps指针指向数组的第一个元素,所以*pw的值为10000。接着程序将pw加1,数字地址的值将加8,这使得pw的值为第二个元素的地址,因此*pw的值是20000。

现在来看一下数组表达式stacks[1]。C++编译器将该表达式看作是*(stacks+1),这意味着先计算数组第二个元素的地址,然后找到存储在那里的值。(运算符优先级要求使用括号,如果不使用括号,将给*stacks加1而不是stacks+1)

12.2、指针小结

12.2.1、声明指针

要声明指向特定类型的指针,请使用下列格式:

typeName * pointerName;

double *pn;
char * pc;

其中pn和pc都是指针,而double*和char*是指向double的指针和指向char的指针。

12.2.2、给指针赋值

可以对变量名使用&运算符,来获得被命名的内存的地址,new运算符返回未命名的内存的地址。

double * pn;
double * pa;
char *pc;
double bubble=1.6;
pn=&bubble;
pc=new char;
pa=new double[10];

12.2.3、对指针解除引用

对指针解除引用意味着获得指针指向的值。对指针应用解除引用或间接值运算符(*)来解除引用。就像上面的代码一样,pn是指向bubble的指针,而*pn是指向的值。

另一种对指针解除引用的方法是使用数组表示法,例如,pn[0]与*pn是一样的。

12.2.4、区分指针和指针指向的值

如果pt是指向int的指针,则*pt不是指向int的指针,而是完全等同于一个int类型的变量。

12.2.5、数组名

在大多情况下,C++将数组名视为数组的第一个元素的地址。

12.2.6、指针算术

在文章开头就介绍过了,这里再总结一下:C++允许将指针和整数相加。加1的结果等于原来的地址值加上指向的对象占用的总字节数。还可以一个指针将去另一个指针,获得两个指针的差。后一种运算将得到一个整数,仅当两个指针指向同一个数组才有效,得到的是两个元素的间隔。

12.2.7、数组的动态联编和静态联编

使用数组声明来创建数组时,将采用静态联编,即数组的长度在编译时设置:

int tacos[10];

使用new[ ]运算符来创建数组时,将采用动态联编,即将在运行时为数组分配空间,其长度也将在运行时设置。使用完这些数组后,应使用delete[ ]释放其占用的内存:

int size;
cin>>size;
int *pz=new int [size];
…
delete [] pz;

12.2.8、数组表示法和指针表示法

使用方括号数组表示法等同于对指针解除引用,细节看上面12.1所写。

12.3、指针和字符串

数组和指针的特殊关系可以拓展到C风格字符串。

char flower[10]="rose";
cout<<flower<<"s are red"<<endl;

数组名是第一个元素的地址,因此cout语句中的flower是包含字符r的char元素的地址。cout对象认为char的地址是字符串的地址,因此它打印该地址处的字符,然后打印后面的字符,直到遇到空字符(\0)为止。如果给cout提供一个字符的地址,则它将从该字符开始打印,直到遇到空字符。

这里关键不在于flower是数组名,而在于flower是一个char的地址,这意味着可以将指向char的指针变量作为cout的参数,因为它也是char的地址。

如果flower是字符串第一个字符的地址,则后面的表达式"s are red"是什么呢?为了与cout对字符串输出的处理保持一致,这个用引号括起来的字符串也应当是一个地址。在C++中,用引号括起来的字符串像数组名一样,也是第一个元素的地址。

注:在cout和多数C++表达式中,char数组名、char指针以及用引号括起来的字符串常量都被解释为字符串第一个字符的地址。

下面给一段程序,使用了两个函数,函数strlen()(返回字符串的长度)和函数strcpy()(将字符串从一个位置复制到另一个位置),两个都位于头文件cstring里。

int main()
{
	using namespace std;
	char animal[20] = "bear";
	const char* bird = "wren";
	char* ps;
	
	cout << animal << " and "
		<< bird << endl;
	
	cout << "Enter a kind of animal: ";
	cin >> animal;

	ps = animal;
	cout << ps << "!" << endl;
	cout << "Before using strcpy(): " << endl;
	cout << animal << " at " << (int*)animal << endl;
	cout << ps << " at " << (int*)ps << endl;

	ps = new char[strlen(animal) + 1];
	strcpy(ps, animal);
	cout << "After using strcpy(): " << endl;
	cout << animal << " at " << (int*)animal << endl;
	cout << ps << " at " << (int*)ps << endl;
	return 0;
}

90faa15fdf5c46fd8873b21a71edc81d.png

程序说明

程序创建了一个char数组(animal)和两个指向char的指针变量(bird和ps)。该程序首先将animal数组初始化为字符串"bear",就像初始化数组一样。然后将char指针初始化为指向一个字符串:

const char* bird="wren";

”wren“实际表示的是字符串的地址,因此这条语句将”wren”的地址赋给bird指针。字符串字面值是常量,以这种方式使用const意味着可以用bird来访问字符串,但不能修改它。

对于cout来说,使用数组名和指针bird是一样的,毕竟两个都是字符串的地址,cout将显示存储在这两个地址上的两个字符串。

ps = animal;
	cout << ps << "!" << endl;
	cout << "Before using strcpy(): " << endl;
	cout << animal << " at " << (int*)animal << endl;
	cout << ps << " at " << (int*)ps << endl;

在这段代码里,一般来说,如果给cout一个指针,它将打印地址。如果指针的类型是char*,则cout将显示指向的字符串。如果要显示的是字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*。因此ps显示的是字符串"fox",而(int *)ps显示为该字符串的地址。

注:将animal赋给ps并不会复制字符串,而只是赋值地址。这样,这两个指针将指向相同的内存单元和字符串。

要获得字符串的副本,还要做其他工作:首先需要分配内存来存储该字符串,这可以通过声明另一个数组或new来完成。后一种方法是的能够根据字符串的长度来指定所需空间:

ps = new char[strlen(animal) + 1];

字符串“fox”不能填满整个animal数组,因此这样做浪费了空间。上述代码使用strlen()来确定字符串的长度,并将它加1来获得包含空字符时该字符串的长度。随后程序使用new来分配刚好足够存储该字符串的空间。

接下来需要将animal数组中的字符串复制到新分配的空间中。将animal赋给ps是不行的!!因为这样只能修改存储在ps中的地址,从而失去程序访问新分配空间的唯一途径。需要使用库函数strcpy():

	strcpy(ps, animal);

strcpy()函数接受两个参数,第一个目标地址,而第二个是要复制的字符串的地址。

注:应使用strcpy(),而不是赋值运算符来将字符串赋给数组。

12.4、使用new创建动态结构

new既然能用于数组,也能用于结构。将new用于结构有两步组成:创建结构和访问其成员。例如,要创建一个未命名的inflatable类型,并将其地址赋给一个指针,可以这样做:

inflatable * ps=new inflatable;

比较棘手的是访问成员,创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只知道它的地址。C++专门为这种情况提供了一个运算符:箭头成员运算符(->)。该运算符由连字符和大于号组成,可用于指向结构的指针,就像点运算符可用于结构名一样,后面会进行演示。

另一种访问成员的方法是:如果ps是指向结构的指针,则*ps就是结构本身。由于*ps是一个结构,因此(*ps).price是该结构的price成员。

#include <iostream>
struct inflatable
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	using namespace std;
	inflatable* ps = new inflatable;
	cout << "Enter name of inflatable item: ";
	cin.get(ps->name,20);
	cout << "Enter volume in cubic feet: ";
	cin >> (*ps).volume;
	cout << "Enter price:$";
	cin >> ps->price;
	cout << "Name: " << (*ps).name << endl;
	cout << "Volume: " << ps->volume << " cubic feet" << endl;
	cout << "Price:$" << ps->price << endl;
	delete ps;
	return 0;
}

c08e6c48d53a402dbfed1304a87fdf25.png

12.4.1、一个使用new和delete的实例

下面给一段程序,该程序定义了一个函数getname(),该函数返回一个指向输入字符串的指针。该函数将输入读入一个大型的临时数组,然后使用new[ ]创建一个刚好能够存储输入字符串的内存块,并返回一个指向该内存块的指针。可以省略大量的内存。

#include <iostream>
#include <cstring>
using namespace std;
	char* getname(void);
char* getname()
{
	char temp[80];
	cout << "Enter last name: ";
	cin >> temp;
	char* pn = new char[strlen(temp) + 1];
	strcpy(pn, temp);
	return pn;
}

int main()
{
	char* name;
	name = getname();
	cout << name << " at " << (int*)name << endl;
	delete[]name;

	name = getname();
	cout << name << " at " << (int*)name << endl;
	delete[]name;
	return 0;
}

c6c89a2ff90447beadfa0144aa4808fe.png

程序说明:

getname()函数使用cin将输入的单词放到temp数组中,然后使用new分配新内存。获得空间后,getname()使用strcpy()将temp中的字符串复制到新的内存块中,然后返回指针pn。

在main()中,返回值被赋给指针name,它指向getname()函数中分配的内存块。然后程序打印该字符串及其地址。

12.5、自动存储、静态存储和动态存储

12.5.1、自动存储

在函数内部定义的常规变量使用自动存储空间,被称为自动变量。这意味这它所属的函数被调用时自动生成,在该函数结束时消亡。例如上面程序的temp数组。

实际上,自动变量是一个局部变量,其作用域为包含它的代码块(指被包含在方括号中的一段代码)。自动变量通常存储在栈中。这意味这执行代码块时,其中的变量将依次加入到栈中,而在离开代码时将按相反的顺序释放这些变量。(可以看看我前面关于栈的文章)

12.5.2、静态存储 

静态存储是整个程序执行期间都存在的存储方式。使变量变为静态的方式有两种:一是在函数外面定义它;另一种是在声明函数时使用关键字stastic。

12.5.3、动态存储

new和delete运算符提供了一个比前面两个存储更灵活的方法,它们管理一个内存池,在C++中称为自由存储空间堆。该内存池时与前面两个内存是分开的,不受空间限制,譬如在不同的函数间使用。

d28f701eeab9459f8e072317e13e1b71.jpeg

 

 

 

 

 

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

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

相关文章

实时监控Windows服务器:使用Prometheus和Grafana的终极方案

1. 下载并安装 Prometheus 下载 Prometheus&#xff1a; 访问 Prometheus 下载页面。下载适用于 Windows 的压缩包&#xff08;.zip 文件&#xff09;。prometheus-2.53.2.windows-amd64.zip 下载其中一个就行 安装 Prometheus&#xff1a; 解压下载的压缩包到你选择的目录&a…

欧拉远程桌面 安装tigervnc

注意&#xff1a;安装远程tigevnc前提必须已经安装桌面环境&#xff0c;以下为ukui桌面环境&#xff0c;dde稍有区别&#xff1b; 1、关闭selinux 注意&#xff1a;selinux为安全措施也可以加入对应规则 setenforce 0 sed -i s/^SELINUXenforcing.*/SELINUXdisabled/ /etc/sel…

基于大模型的AI论文简报生成系统

1、项目介绍 1.1 项目名称 AI 论文简报生成系统 1.2 项目简介 AI 论文简报生成系统是一款基于当前时间自动获取最新AI研究论文简报的桌面应用程序。通过集成各类大模型Kimi、deepSeek&#xff0c;OpenAI等模型API与多步数据处理脚本&#xff0c;该系统能够在用户指定的时间…

考勤系统选型难?9款工具深度对比助你决策

这篇文章介绍的工具有&#xff1a;Moka、天时考勤管理专家、劳勤、新开普、通达OA、OnTheClock、monday.com、Paycor、Beebole。 很多企业在选择考勤管理系统时都会面临一个难题&#xff1a;市场上的选项太多&#xff0c;功能繁杂&#xff0c;如何才能找到最适合自己业务需求的…

美团外卖新版 web mtgsig 1.2 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、 敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业 用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 有相关问题请第一时间头像或私信联…

计算机毕业设计选题推荐-牧民画像系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

SpringBoot(图书馆)自习室座位预约管理系统 - 附源码与配套论文

摘 要 在数字化转型的浪潮中&#xff0c;自习室座位预约业正积极采用先进的信息技术来优化客户体验和运营效率。本研究旨在开发一款基于Spring Boot后端框架的自习室座位预约客房预订管理系统&#xff0c;以满足现代自习室座位预约对高效、便捷、安全的预订管理需求。 座位预…

【Qt开发】QtCharts图表 在ui上添加QChartView控件并进行绘图配置

【Qt开发】QtCharts图表 在ui上添加QChartView控件并进行绘图配置 文章目录 控件安装和模块导入在ui上添加QChartView控件QChartView图表配置附录&#xff1a;C语言到C的入门知识点&#xff08;主要适用于C语言精通到Qt的C开发入门&#xff09;C语言与C的不同C中写C语言代码C语…

Simple RPC - 04 从零开始设计一个客户端(上)

文章目录 Pre设计Code1. 理解Stub“桩”的实现原理2. 动态生成桩的接口 StubFactory3. 如何来实现工厂方法创建桩动态生成“桩”类的过程步骤概述代码实现 技术点动态代理模式的应用动态代理的应用分析 依赖倒置和SPI Pre Simple RPC - 01 框架原理及总体架构初探 Simple RPC…

Postman文件上传接口测试

接口介绍 返回示例 测试步骤 1.添加一个新请求&#xff0c;修改请求名&#xff0c;填写URL&#xff0c;选择请求方式 2.将剩下的media参数放在请求body里&#xff0c;选择form-data&#xff0c;选择key右边的类型为file类型&#xff0c;就会出现选择文件的按钮Select Files&a…

继承 (上)【C++】

文章目录 继承的定义继承的语法继承权限和继承到子类后父类成员的访问限定符的变化继承到子类后父类成员的访问限定符的变化 子类继承到了父类的什么&#xff1f;继承中的作用域子类和父类之间的赋值转换子类对象可以直接赋值给父类对象&#xff0c;但是父类对象不能直接赋值给…

spring boot 接收第三方mq消息

文章目录 前言一、pom二、配置三、RabbitMQListener总结 前言 mq 用的很少&#xff0c;简单记录一下。 需求&#xff1a;数据提供方采用mq的方式推送数据&#xff0c;我方接收数据后&#xff0c;入库。 一、pom <dependency><groupId>org.springframework.boot&…

基于 Appium 的 App 爬取实战

除了运行 Appium 的基本条件外&#xff0c;还要一个日志输出库 安装&#xff1a; pip install loguru 思路分析 首先我们观察一下整个 app5 的交互流程&#xff0c;其首页分条显示了电影数据&#xff0c; 每个电影条目都包括封面&#xff0c;标题&#xff0c; 类别和评分 4…

Linux下Oracle 11g升级19c实录

1.组件信息 source /home/oracle/.bash_profile11g && sqlplus "/ as sysdba"<<EOF set line 200 col COMP_NAME for a40 select comp_name,VERSION,STATUS from dba_registry; exit; EOF COMP_NAME VERSION …

自动化之响应式Web设计:纯HTML和CSS的实现技巧

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言 响应式Web设计是一种使Web页面在各种设备和屏幕尺寸下都能良好显示的设计方法。随着移动设备的普及&#xff0c;响应式设计已经成为Web开发中的标准实践。本文将探讨如何使用纯HTML和CSS实现响应式Web设计&#xff0c;覆…

测试架构师领导力的原则

目录 一、建立信任关系 二、建立共识 三、通过关系带来安全 四、要身体力行&#xff0c;以身作则 五、适当处理风险&#xff0c;什么是鞭炮&#xff0c;什么是原子弹 测试架构师的领导力是建立在把握和执行的某些原则上---信任&#xff0c;认知&#xff0c;安全&#xff0…

Python 算法交易实验81 QTV200日常推进-重新实验SMA/EMA/RSI

说明 本次实验考虑两个点&#xff1a; 1 按照上一篇谈到的业务目标进行反推&#xff0c;有针对性的寻找策略2 worker增加计算的指标&#xff0c;重新计算之前的实验 内容 工具方面&#xff0c;感觉rabbitmq还是太慢了。看了下&#xff0c;rabbitmq主要还是面向可靠和灵活路…

【软件测试】软件系统测试方案(Word原件)

1. 引言 1.1. 编写目的 1.2. 项目背景 1.3. 读者对象 1.4. 参考资料 1.5. 术语与缩略语 2. 测试策略 2.1. 测试完成标准 2.2. 测试类型 2.2.1. 功能测试 2.2.2. 性能测试 2.2.3. 安全性与访问控制测试 2.3. 测试工具 3. 测试技术 4. 测试资源 4.1. 人员安排 4.2. 测试环境 4.2.…

Openstack 与 Ceph集群搭建(上): 规划与准备

文章目录 写在前面网络架构节点规划软件版本避坑指南 基础配置1. host配置2. 修改hostname名称3. 确保root账号能登录系统4. 配置NTP5. 配置免密登录 写在前面 近期将进行三节点的Openstack、Ceph集群混合部署&#xff0c;本人将详细记录该过程。在此之前&#xff0c;本文为Op…

逆向开发LabVIEW程序的操作与注意事项(无源代码)

1. 概述与准备工作 当手头没有源代码&#xff0c;只有LabVIEW编译后的可执行程序时&#xff0c;逆向开发的难度和复杂性大大增加。需要用到的工具、方法和策略也会有所不同。逆向工程的目标是在没有源代码的情况下重建或理解该程序的功能、结构和行为。涉及CameraLink通讯的程…