清华郑莉C++语言程序设计学习笔记(3)- 数组、指针、字符串

10 篇文章 7 订阅

第六章 数组、指针、字符串

6.1 数组的定义与初始化

6.1.1 数组的定义与使用

定义:类型说明符 数组名[常量表达式][常量表达式]
数组名的构成方法与一般变量名相同,例如int a[5][3]表示二维数组,第一维有5个下标(0~4),第二维有3个下标(0-2)。
使用:先定义,后使用,大多数时候逐个引用数组元素。

6.1.2 数组的存储与初始化

一维数组存储:数组元素在内存中顺序存放,它们的地址连续,元素间物理地址上的相邻对应着逻辑次序上的相邻。
在这里插入图片描述
一维数组初始化
1、列出全部元素的初始值,例如:static int a[10]={0,1,2,3,4,5,6,7,8,9};
2、只给一部分元素指定初值,例如:static int a[10]={0,1,2,3,4};
3、列出全部数组元素初值时,可以不指定数组长度,例如:static int a[]={0,1,2,3,4,5,6,7,8,9};
二维数组的存储:按行存放
在这里插入图片描述
二维数组的初始化
1、将所有初值写在一个{}内,按顺序初始化,例如:static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
2、分行列出二维数组元素的初值,例如:static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
3、只对部分元素初始化,例如:static int a[3][4]={{1},{0.6},{0,0,11}};
4、列出全部初始值时,第1维下标个数可以省略,例如:static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};static int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
如果不作任何初始化,局部作用域的非静态数组中会存在垃圾数据,static数组中的数据默认初始化为0;如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为0

例:求Fibonacci数列的前20项,将结果输出
在这里插入图片描述

6.1.3 一维数组应用举例

例:循环从键盘读入选择题答案,计算并输出每组答案的正确率,直到输入ctrl+z为止,每组连续输入5个答案,每个答案可以是’a’…‘d’
在这里插入图片描述

6.2 数组作为函数参数

数组元素作实参,与单个变量一样。数组名作参数,形、实参数都应是数组名,类型要一样,传送的是数组首地址对函数的形参数组的改变会直接影响到实参数组

6.3 对象数组

由一个个对象构成数组,只要对象属于同一类型。
对象数组的定义:类名 数组名[元素个数]
对象数组元素的访问:数组名[下标].成员名
对象数组初始化
1、数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。通过初始化列表赋值,例:Point a[2]={Point(1,2),Point(3,4)};
2、如果没有为数组元素指定显式初始值,数组元素使用默认值初始化(调用默认构造函数)。
数组元素的构造和析构
1、构造数组时,元素所属的类未声明构造函数,则采用默认构造函数。
2、各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。
3、各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。
4、当数组中每一个对象被删除时,或程序结束时内存空间要被释放,系统都要调用一次析构函数。

例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4 基于范围的for循环

处理容器类时很好用

例:
在这里插入图片描述

6.5 指针的定义和运算

6.5.1 指针的概念、定义和指针运算

内存空间的访问方式:通过变量名访问、通过地址访问
指针:内存地址,用于间接访问内存单元。
指针变量:用于存放地址的变量
在这里插入图片描述
在这里插入图片描述
与地址相关的运算:指针运算符*和地址运算符&,互为逆运算。

6.5.2 指针的初始化和赋值

初始化语法形式:存储类型 数据类型 * 指针名=初始地址 例:int *pa = &a;
注意事项
1、用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致。
2、可以用一个已有合法值的指针去初始化另一个指针变量。
3、不要用一个内部非静态变量去初始化static指针。
指针变量赋值运算:指针名=地址
注意事项
1、地址中存放的数据类型与指针类型必须相符;
2、向指针变量赋得值必须是地址常量或变量,不能是普通整数;
例如:通过地址运算&求得已定义得变量和对象的起始地址。
3、动态内存分配成功时返回的地址;
例外:整数0可以赋给指针,表示空指针
4、允许定义或声明指向void类型的指针,该指针可以被赋予任何类型对象的地址。
例:void *general
指针空值nullptr:C++11使用nullptr关键字,是表达更准确,类型安全的空指针。
在这里插入图片描述
在这里插入图片描述
指向常量的指针const指针:不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。如只允许读取,不允许覆盖和改写时。
在这里插入图片描述
指针类型的常量:若声明指针常量,则指针本身的值不能被改变。
在这里插入图片描述

6.5.3 指针的算数运算、关系运算

指针类型的算数运算:指针与整数的加减运算、指针++,–运算
指针p加n:指针当前指向位置的前方或后方第n个数据的起始位置。
指针++,–:指向下一个或前一个完整数据的起始。
运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置。当指针指向连续存储的同类型数据时,才有意义。
在这里插入图片描述
指针类型的关系运算
指向相同类型数据的指针之间可以进行各种关系运算;
指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的;
指针可以和0之间进行等于或不等于的关系运算,例如p==0p!=0

6.6 指针与数组

6.6.1 用指针访问数组元素

定义指向数组元素的指针int a[10], *pa; pa=&a[0];pa=a;
等效的形式a[i], *(pa+i), *(a+i), pa[i]是等效的

6.6.2 指针数组

定义:数组的元素是指针类型
Point *pa[2]; //由pa[0], pa[1]两个指针组成

例:利用指针数组存放矩阵(行与行之间不一定依次存放)
在这里插入图片描述
在这里插入图片描述

6.7 指针与函数

6.7.1 以指针作为函数参数

为什么需要用指针做参数:需要数据双向传递时(引用也可以达到此效果);需要传递一组数据,只传首地址运行效率比较高。

例:读入三个浮点数,将整数部分和小数部分分别输出
在这里插入图片描述
在这里插入图片描述
例:指向常量的指针做形参 (指针所指向的数据只能读取,不能修改)
在这里插入图片描述

6.7.2 指针类型的函数

即函数的返回值是指针
定义形式: 存储类型 数据类型 *函数名() {}
注意:不要将非静态局部地址用作函数的返回值,非法地址。返回的指针要确保在主调函数中是有效、合法的地址。
错误例子
在这里插入图片描述
正确例子
在这里插入图片描述
正确的例子:在子函数中通过动态内存分配new操作取得的内存地址返回给主函数是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记释放(delete),避免内存泄漏。
在这里插入图片描述

6.7.3 指向函数的指针

函数指针定义形式:存储类型 数据类型 (*函数指针名)()
含义:函数指针指向的是程序代码存储区。
函数指针典型用途:实现函数回调
通过函数指针调用的函数,例如将函数指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法。调用者不关心谁是被调用者,需知道存在一个具有特定原型和限制条件的被调用函数。

例:编写一个计算函数compute,对两个整数进行各种计算。有一个形参为指向具体算法函数的指针,根据不同的实参函数,用不同的算法进行计算。
编写三个函数:求两个整数的最大值、最小值、和,分别用这三个函数作为实参,测试compute函数。
在这里插入图片描述
在这里插入图片描述

6.8 对象指针

定义形式:类名 *对象指针名; Point a(5,10); Point *ptr; ptr=&a;
通过指针访问对象成员:对象指针名->成员名 ptr->getx();相当于(*ptr).getx();
在这里插入图片描述
this指针:隐藏于类的每一个非静态成员函数中,指出成员函数所操作的对象。当通过要给对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。
例如:Point类的getX函数中的语句return x; 相当于 return this->x;

错误例子:
在这里插入图片描述
在这里插入图片描述

6.9 动态内存分配

6.9.1 动态分配与释放内存

动态申请内存操作符new:new 类型名T(初始化参数列表)
功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。
结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。
释放内存操作符delete:delete 指针p
功能:释放指针p所指向的内存,p必须是new操作的返回值。注意:不是删除指针。
在这里插入图片描述
在这里插入图片描述

6.9.2 申请和释放动态数组(一)

分配:new 类型名T [数组长度] 数组长度可以是任何整数类型的表达式,在运行时计算
释放:delete[] 数组名p 释放指针p所指向的数组,p必须是用new分配得到的数组首地址。
在这里插入图片描述
动态创建多维数组:new 类型名T[第一维长度][第二维长度] 如果内存申请成功,new运算返回一个指向新分配内存首地址的指针。
在这里插入图片描述
在这里插入图片描述
例:动态创建3维数组
在这里插入图片描述

6.9.3 申请和释放动态数组(二)

将动态数组封装成类:更加简介,便于管理,可以在访问数组元素前检查下标是否越界。

例:动态数组类(可以看到析构函数的作用)
在这里插入图片描述
在这里插入图片描述

6.10 智能指针

unique_ptr:不允许多个指针共享资源,可以用标准库中的move函数转移指针。
shared_ptr:多个指针共享资源
weak_ptr:可复制shared_ptr,但其构造或者释放对资源不产生影响。

6.11 vector对象

为什么需要vector
1、封装任何类型的动态数组,自动创建和删除。
2、数组下标越界检查。
定义:vector<元素类型> 数组对象名(数组长度) vector<int> arr(5) 建立大小为5的int数组
vector对象的使用:对数组元素的引用与普通数组具有相同过的形式 vector对象名[下标表达式] vector数组对象名不表示数组首地址
获取数组长度用size函数 :vector对象名.size()
在这里插入图片描述
基于范围的for循环配合auto举例:
在这里插入图片描述

6.12 对象复制与移动

6.12.1 深层复制与浅层复制

浅层复制:实现对象间数据元素的一一对应复制。
深层复制:当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.12.2 移动构造

移动构造:C++11标准中新的构造方法。C++11之前,如果要将源对象的状态转移到目标对象只能通过复制,在某些情况下,我们没必要复制对象,只需要移动它们,即将源对象资源的控制权全部交给目标对象。
在这里插入图片描述
何时?有可被利用的临时对象
移动构造函数class_name(class_name &&)

例:函数返回含有指针成员的对象
版本一:使用深层复制构造函数 返回时构造临时对象,动态分配将临时对象返回到主调函数,然后删除临时对象。
在这里插入图片描述
在这里插入图片描述
版本二:使用移动构造函数 将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过程。
在这里插入图片描述
在这里插入图片描述

6.13 字符串

6.13.1 C风格字符串

字符串常量:例如“program”,各字符连续、顺序存放,每个字符占一个字节,以’\0’结尾,相当于一个隐含创建的字符常量数组。“program”出现在表达式中,表示这一char数组的首地址。
首地址可以赋给char常量指针const char *STRING1="program";
用字符数组存储字符串(C风格字符串)

char str[8]={'p','r','o','g','r','a','m','\0'};
char str[8]="program";
char str[]="program";

在这里插入图片描述
用字符数组表示字符串的缺点
1、执行连接、拷贝、比较等操作,都需要显式调用库函数,很麻烦。
2、当字符串长度很不确定时,需要new动态创建字符数组,最后要用delete释放,很繁琐。
3、字符串实际长度大于为它分配的空间时,会产生数组下标越界的错误。

6.13.2 string类

string类常用的构造函数

string(); //默认构造函数,建立一个长度为0的串
string s1;
string(const char *s); //用指针s所指向的字符串常量初始化string对象
string s2 = "abc";
string(const string& rhs); //复制构造函数
string s3=s2;

string类常见操作
在这里插入图片描述
例:
在这里插入图片描述
getline输入整行字符串
1、要包含string头文件,例如getline(cin,s2);
2、输入字符串时,可以使用其他分隔符作为字符串结束的标识(例如逗号、分号),将分隔符作为getline的第3个参数即可,例如getline(cin, s2, ',');
在这里插入图片描述

6.14 综合实例

个人银行账户管理程序:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值