C++语言程序设计教程 第5章 构造数据类型 第5章 构造数据类型.

Slides:



Advertisements
Similar presentations
1 第二讲 C++ 编程基础. 2 主要内容 C++ 语言概述 C++ 编程基础 数据的简单输入输出 C++ 的发展 C++ 源程序结构与书写规范 C++ 编译器和集成开发环境.
Advertisements

C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
电子成绩单项目实现.
程序设计实习 3月份练习解答
第一章 C语言概述 计算机公共教学部.
第5章 指 针 教学目标: 掌握指针定义、初始化方法和基本运算; 掌握利用指针访问一维数组、二维数组的方法;
高级语言程序设计 C++程序设计教程(下) 2006年春季学期 与一些教材的区别 偏重理论,不去讨论某个系统的具体使用方法,但会涉及实现技术
VC++系统开发 第二章 C++语言基础 河北经贸大学信息技术学院.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
第六讲 指针与字符串 —— 为什么指针 —— 持久动态内存分配 —— 字符串(字符数组).
資料大樓 --談指標與陣列 綠園.
函數(一) 自訂函數、遞迴函數 綠園.
C++程序设计 第二讲 清华大学软件学院.
项目六 用指针优化学生成绩排名 项目要求 项目分析
结构体和共用体 2 梁春燕 华电信息管理教研室.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
计算机网络讲义 第5章 批量数据处理—数组 一维数组 排序和查找 二维数组 字符串.
第五章 指针 5.1 指针的概念和定义 5.2 指针运算 5.3 指针和数组 5.4 字符串指针 5.5 指针数组 5.6 指向指针的指针
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
第3讲 C++程序控制结构 3.1 顺序结构 3.2 分支结构 3.3 循环结构 3.4 转向控制 3.5 综合案例分析.
C++语言程序设计 第二章 C++简单程序设计.
程序的三种基本结构 if条件分支语句 switch多路开关语句 循环语句 循环嵌套 break,continue和goto语句
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
第六章 指针、引用和动态空间管理 6.1 指针的概念 指针的概念 指针变量的定义和初始化
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
C++大学基础教程 第5章 数组 北京科技大学 信息基础科学系.
六、函数 教学目标: 函数的概念、定义、调用和返回 带自定义函数的程序设计 递推算法 递归思想及算法实现 函数的参数传递方式 C语言程序设计.
C++大学基础教程 第3章 C++控制语句 北京科技大学 信息基础科学系.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
C语言程序设计.
第十章 用户自定义数据类型 目录 学生信息管理系统的开发 结构体数据类型的概述 结构体变量的使用 结构体数组
第二章 基本数据类型及运算 C数据类型概述 基本数据类型 运算符和表达式 混合运算与类型转换 数据的输入输出 顺序程序设计举例.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
C++语言程序设计 C++语言程序设计 第五章 函数 第十一组 C++语言程序设计.
第 二 章 数据类型、运算符与表达式.
C++语言程序设计 C++语言程序设计 第三章 控制语句 第十一组 C++语言程序设计.
C语言大学实用教程 第6章 数组 西南财经大学经济信息工程学院 刘家芬
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第六章 指针 C++程序设计中使用指针可以: 使程序简洁、紧凑、高效 有效地表示复杂的数据结构 动态分配内存 得到多于一个的函数返回值.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
C语言程序设计 李祥 QQ:
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第2章 数据类型、运算符与表达式 本章要点: 基本数据类型 常量和变量 算术运算符和算术表达式 关系运算符和关系表达式
第二章 基本数据类型 ——数据的表示.
第二讲 基本数据类 型及数组等 此为封面页,需列出课程编码、课程名称和课程开发室名称。
C程序设计.
#include <iostream.h>
第四章 函数 丘志杰 电子科技大学 计算机学院 软件学院.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
第三章 高级函数特性.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
變數與資料型態  綠園.
資料!你家住哪裏? --談指標 綠園.
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

C++语言程序设计教程 第5章 构造数据类型 第5章 构造数据类型

第5章 构造数据类型 学习目标 1. 掌握枚举类型的使用; 2. 深入理解数组的概念, 掌握数组应用的一般方法; C++语言程序设计教程 第5章 构造数据类型 第5章 构造数据类型 学习目标 1. 掌握枚举类型的使用; 2. 深入理解数组的概念, 掌握数组应用的一般方法; 3. 深入理解指针的概念,掌握指针的使用; 4. 注意指针与数组的区别,会使用多重指针以及指针与数组的多种混合体,会分配动态数组; 5. 理解字符串的概念,会使用字符串; 6. 理解引用的概念,掌握引用型函数参数的用法; 7. 掌握结构与联合类型的使用,并注意二者的区别。

enum 枚举类型名 {枚举常量1, 枚举常量2,…, 枚举常量n}; C++语言程序设计教程 第5章 构造数据类型 5.1 枚举类型 1. 枚举类型的定义:   “枚举”是指将变量所有可能的取值一一列举出来,变量的取值只限于列举出来的常量。  枚举类型的声明的一般形式如下 : 枚举类型名以及枚举常量为标识符,遵循标识符的取名规则。 在定义一个枚举类型时,定义了多个常量,供枚举类型变量取值,称此常量为枚举常量。当没给各枚举常量指定值时,其值依次默认为0、1、2、…;在定义枚举类型时,也可使用赋值号另行指定枚举常量的值。 enum 枚举类型名 {枚举常量1, 枚举常量2,…, 枚举常量n}; 枚举!

C++语言程序设计教程 第5章 构造数据类型 5.1 枚举类型 枚举类型的定义: 例1: enum weekday { SUN, MON, TUE, WED, THU, FRI, SAT }; 定义了7个枚举常量以及枚举类型weekday。枚举常量具有默认的整数与之对应:SUN的值为0、MON的值为1、TUE为2、…、SAT为6。 例2: enum city{ Beijing,Shanghai,Tianjin=5,Chongqing}; 枚举常量Beijing的值为0,Shanghai的值为1,Tianjin的值指定为5。对于指定值后面的没有指定值的枚举常量,编译器会将前一个常量值加1(下一个整数)赋给它,所以Chongqing的值为6。

C++语言程序设计教程 第5章 构造数据类型 5.1 枚举类型 枚举类型的定义说明: 枚举类型定义了以后就可以使用枚举常量、使用用枚举类型来定义变量,定义枚举变量的方法与定义其它变量的方法一样。 例如: enum city city1, city2; city city1, city2; 用两种方法定义了city1、city2两个枚举类型的变量名。 枚举类型变量也可以在定义枚举类型的同时定义 enum city{ Beijing,Shanghai,Tianjin=5,Chongqing} city1, city2; 在定义枚举类型的同时定义枚举类型变量可以省略枚举类型名 enum { Beijing,Shanghai,Tianjin=5,Chongqing} city1, city2; 在定义变量时,可以顺便给出初值,若不给初值,默认初值为随机的无意义的数。

5.1 枚举类型 枚举类型的使用: C++语言程序设计教程 第5章 构造数据类型 5.1 枚举类型 枚举类型的使用: 用枚举类型建立枚举变量后就可以对枚举变量实施赋值以及进行其它运算了,对枚举变量进行赋值,其值要求为同一枚举类型。否则,在编译时出错。 例如: weekday d1,d2,d3,d4; d1=SUN; d2=6; //错误 d3=Shanghai; //错误 其中对d2所赋之值是整数6,不是枚举常量;可以采用将一个整型值强制转换成同类型的枚举常量赋给枚举变量: 枚举常量、枚举类型的变量可进行算术运算、关系运算。 对枚举类型实施算术、关系运算时,枚举值转换成整型值参加运算,结果为整型值。所以,如果要将结果赋给枚举变量,还要将结果转换成枚举值。 例如: d1=d1+2; //是错误的,因为结果为int型。 需要将它强制转换成枚举型: d1=(weekday)(d1+2);

5.1 枚举类型 注意: 枚举类型的使用: 枚举常量、枚举类型的变量可直接进行各种形式的关系运算。 枚举类型变量不能直接进行输入 C++语言程序设计教程 第4章 函数 5.1 枚举类型 枚举类型的使用: 枚举常量、枚举类型的变量可直接进行各种形式的关系运算。 例如: if(city1==3) ; if(city2>=Beijing); if(Shanghai==1); if(city1>SUN); 枚举类型变量不能直接进行输入 cin>>d1; //错误 注意: 枚举常量是常量,不是变量,所以不能对枚举常量进行赋值。 例如: 在上例中不能进行赋值 Shanghai=Beijing; 枚举常量的值不是列举的字符串,其值为整数。 编译器对赋给枚举变量的对象(数)进行类型检查,如类型不相符则发出警告。 当类型相 同,而值超出此类枚举类型枚举常量范围时,也是正常的。

C++语言程序设计教程 第5章 构造数据类型 5.1 枚举类型 【例5-1】输入城市代号,输出城市名称。 1 2 3 4 5 6 7 8 9 10 11 12 /****************************************************************** * 程序名:p5_1.cpp * * 功能: 枚举类型的使用,输入城市代号,输出城市名称 * *******************************************************************/ #include<iostream> using namespace std; enum city{ Beijing,Shanghai,Tianjin=6,Chongqing}; void main() { int n; cout<<"Input a city number ("<<Beijing-1<<" to exit):"<<endl; cin>>n; 枚举类型定义

5.1 枚举类型 运行结果: 枚举类型的使用 Input a city number (-1 to exit): 1↙ Shanghai 第5章 构造数据类型 5.1 枚举类型 13 14 15 16 17 18 19 20 21 22 23 while(n>=Beijing){ switch(n) { case Beijing: cout<<"Beijing"<<endl; break; case Shanghai: cout<<"Shanghai"<<endl;break; case Tianjin: cout<<"Tianjin"<<endl; break; case Chongqing: cout<<"Chongqing"<<endl; break; default: cout<<"Invalid city number! "<<endl; break; } cin>>n; 枚举类型的使用 运行结果: Input a city number (-1 to exit): 1↙ Shanghai 8↙ Invalid city number! -1↙

C++语言程序设计教程 第5章 构造数据类型 5.2 数组 数组: 数组是一组在内存中依次连续存放的、具有同一类型的数据变量所组成的集合体。其中的每个变量称为数组元素,它们属于同一种数据类型,数组元素用数组名与带方括号的数组下标一起标识。数组可以是一维的,也可以是多维的。 特点: 若干个同类型的数据元素,并且各个数据元素之间存在某种次序关系。这类数据有一个共同的特点:它们有若干个同类型的数据元素,并且各个数据元素之间存在某种次序关系。

5.2.1 一维数组定义与使用 一维数组定义的一般形式为: 说明: 注意: 数组属于构造数据类型,在使用之前必须先进行类型定义。 C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 一维数组定义的一般形式为: 说明: 数组元素的类型可以是void型以外的任何一种基本数据类型,也可以是已经定义过的构造数据类型; 数组名是用户自定义的标识符,用来表示数组的名称,代表数组元素 在内存中的起始地址,是一个地址常量。 常量表达式必须是unsigned int类型的正整数。表示数组的大小或长 度,也就是数组所包含数据元素的个数。 [ ]是数组下标运算符,在数组定义时用来限定数组元素的个数。 数据类型 数组名[常量表达式]; 注意: 数组属于构造数据类型,在使用之前必须先进行类型定义。

5.2.1 一维数组定义与使用 数组元素的类型可以是void型以外的任何一种基本数据类型,也可以是已经定义过的构造数据类型; C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 数组元素的类型可以是void型以外的任何一种基本数据类型,也可以是已经定义过的构造数据类型; 例如:下面定义了2个不同类型的数组: int a[5]; //定义了一个5个元素的整型数组a weekday b[10]; //定义了一个10个元素的枚举数组b,weekday 为已定义的枚举型。 数据类型相同的多个数组可以在同一条语句中予以定义。 例如: int a1[10], a2[20]; //同时定义了两个整型数组 数据类型相同的的简单变量和数组也可以在一个语句中定义。例如: int x, a[20]; //同时定义了一个整型变量和一个整型数组

C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 数组定义之后,系统会将从内存中为其分配一块连续的存储空间,从第1个数据元素开始依次存放各个数组元素。例如,定义的数组a其内存排列(分配)示意图如图5-1所示。 例如: int a[5]; //定义了一个5个元素的整型数组a

5.2.1 一维数组定义与使用 一维数组初始化: 是指在定义数组的同时给数组中的元素赋值。 其一般语法形式为: C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 一维数组初始化: 是指在定义数组的同时给数组中的元素赋值。 其一般语法形式为: {初值1,初值2,…, 初值n}称为初值表,初值之间用逗号,分隔, 所有初值用{ }括起来。 初值可以是一个变量表达式,初值与数组元素的对应关系是:初值i给数组第i个元素;所以,初值个数n不能超过数组的大小。 若初值表中初值个数(项数)小于数组的大小,则未指定值的数组元素被赋值为0;但初值表中项数不能为0。例如: weekday b[10]={MON,WED,FRI}; 当对全部数组元素赋初值时,可以省略数组的大小,此时数组的实际大小就是初值列表中初值的个数。例如: char str[ ] = {'a', 'b', 'c', 'd', 'e' }; //数组str的实际大小为5。 在函数中定义数组时,如果没有给出初值表,数组不被初始化,其数组元素的值为随机值;在函数外定义数组如果没有初始化,其数组元素的值为0。 数组初值表的可以用一个逗号结尾,其效果与没有逗号一样。 数据类型 数组名 [常量表达式] ={初值1, 初值2,…, 初值n};

5.2.1 一维数组定义与使用 下标表达式可以是变量表达式,用来标识数组元素;不同于数组定义时用来确定数组长度的常量表达式。 C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 对一维数组实施的存取操作有两类:存取数组元素与读取数组元素的地址。数组元素是通过数组名及下标来标识的,这种带下标的数组元素也称为下标变量,下标变量可以象简单变量一样参与各种运算。 存取一维数组元素的一般语法形式为: 说明:          下标表达式可以是变量表达式,用来标识数组元素;不同于数组定义时用来确定数组长度的常量表达式。 当定义了一个长度为n的一维数组a,C++规定数组的下标从0开始,依次为0、1、2、3、…、n-1。对应的数组元素分别是a[0]、a[1]、…、a[n-1],因此下标表达式的值要在[0,n-1]范围内。    例如: a[1+2]=100; // 将数组a的第4个元素赋值100。 数组名 [下标表达式];

5.2.1 一维数组定义与使用 【例5-2】学生成绩排序。 分析:学生成绩由键盘输入,当输入一个负数时,输入完毕。 C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 【例5-2】学生成绩排序。 分析:学生成绩由键盘输入,当输入一个负数时,输入完毕。 采用直观的“选择排序法”进行排序,基本步骤如下: ① 将a[0]依次与a[1]~a[n-1]比较,选出大者与a[0]交换;最后a[0]为 a[0]~a[n-1]中最大者; ② 将a[1]依次与a[2]~a[n-1]比较,选出大者与a[1]交换;最后a[1]为 a[1]~a[n-1]中最大者; ③ 同理,从i=2到i=n-1, 将a[i]依次与a[i+1]~a[n-1]比较,选出较大 者存于a[i]中。

/****************************** * p5_2.cpp * * 数组应用--选择排序 * 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /****************************** * p5_2.cpp * * 数组应用--选择排序 * ******************************/ #include<iostream> using namespace std; void main() { const int MaxN=5; int n,a[MaxN],i,j; for (n=0;n<MaxN;n++) { cin>>a[n]; //输入数组元素 if (a[n]<0) break; } for (i=0;i<n;i++) cout<<a[i]<<",\t"; cout<<"n"<<n<<endl; 数组定义 数组使用

运行结果: 19 20 21 22 23 24 25 26 27 28 29 30 31 for (i=0;i<n-1;i++) C++语言程序设计教程 第5章 构造数据类型 19 20 21 22 23 24 25 26 27 28 29 30 31 //对数组元素逐趟进行选择排序 for (i=0;i<n-1;i++) for (j=i+1;j<n;j++) //从待排序序列中选择一个最大的数组元素 if (a[i]<a[j]) { int t; t=a[i]; //交换数组元素 a[i]=a[j]; a[j]=t; } for (i=0;i<n;i++) cout<<a[i]<<",\t"; //显示排序结果 运行结果: 80 90 95 70 -1↙ 95 90 80 70

5.2.1 一维数组定义与使用 数组元素的地址通过数组名来读取,其格式如下: C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 4. 数组的地址 数组元素的地址通过数组名来读取,其格式如下: 由于其地址不是实际的地址值,称这个地址表达式为符号地址表达式。例如: 一维数组元素a[5]的符号地址表达式为a+5。 若a是一个int型数组,数组的符号地址表达式a+n所表达的地址是第n+1个元素a[n]的地址,代表的实际地址值为: a+n*sizeof(int) 而不是:a+n。 数组名+整型表达式;

5.2.1 一维数组定义与使用 使用数组要注意: 在使用数组时最常犯的错误是下标越界,所谓的下标越界就是下标表达式的值 C++语言程序设计教程 第5章 构造数据类型 5.2.1 一维数组定义与使用 使用数组要注意: 在使用数组时最常犯的错误是下标越界,所谓的下标越界就是下标表达式的值 超出了数组定义时给定的元素个数,对于这种错误,编译器无法知道,往往在 运行时出错。因此在程序设计时应格外注意。   数组名是一个地址常量,不能作为左值(赋值的目标)。因此,不能将一个数组 整体拷贝给另外一个数组。 例如: int a[5],c[5],i; a=c; //错误! 正确的方法是将对应的元素进行拷贝,见下列程序段: for(i=0;i<5;i++) a[i]=c[i]; //将数组c中元素的值拷贝到数组c的对应元素中 在函数中,可以将一个一维数组作为函数的形式参数,用来接受一个一维数组传递过来的地址。

数据类型 数组名[常量表达式2][常量表达式1]; C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 二维数组的定义的一般形式为: 其中: 常量表达式1为第1维的元素的个数,常量表达式2为第2维元素的个数。 二维数组a[m][n]是以长度为n的一维数组为元素的数组,因此,等价于如下定义方式: 例如: int M[2][3]; 定义了一个整型二维数组M,数组M也可以用下列方式定义: typedef int M1[3]; // 定义了一个一维整型数组M1; M1 M[2]; // 以M1为类型定义数组M 数据类型 数组名[常量表达式2][常量表达式1]; typedef 数据类型 一维数组名[常量表达式1]; 一维数组名 二维数组名[常量表达式2];

5.2.2 二维数组的定义与使用 数据类型 数组名[行数][列数]; C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 如果一维数组描述排列成一行的数据,那么,二维数组则描述若干行这样的数据。因此,二维数组可以看作是数学上的一个矩阵。第1维元素个数为矩阵的列数,第2维元素个数为矩阵的行数。 二维数组的定义格式可以写成: 定义一个二维数组后,系统为它分配一块连续的内存空间。 二维数组a[m][n]占内存空间的计算公式为: 数据类型 数组名[行数][列数]; sizeof(数组名);或 m*sizeof(a[0]); 或 m*n*sizeof(数据类型)

5.2.2 二维数组的定义与使用 C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 既然一个二维数组是由若干个一维数组排列构成,二维数组在内存中的排列顺序为:先顺序排列每个一维元素,构成一维数组;再将各个一维数组顺序排列,构成二维数组。 int M[2][3]的排列顺序为:先将3个int元素排列组成2个一维数组M[0], M[1]。 M[0]:M[0][0],M[0][1],M[0][2] M[1]:M[1][0],M[1][1],M[1][2] 再将2个一维数组排成一个二维数组。 M: M[0], M[1] 数组M在内存中的排列图如图所示。

数据类型 数组名 [常量表达式2][常量表达式1]=初值表; C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 二维数组的初始化 : 其中初值表具有两种形式:嵌套的初值表,线性初值表。   (1)    嵌套初值表 以二维数组M[m][n]为例,嵌套初值表的格式为: 嵌套初值表由一维初值表嵌套构成,各层构成规则与一维数组的初值表相同。 例如: int M[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}}; //M数组元素被全部初始化 int a[2][3]={{1},{0,0,1}}; //初始化了部分数组元素 int b[][3]={{1,2,3},}; //初始化了全部数组元素 int d[][3]={{1,3,5},{5,7,9}}; //初始化了全部数组元素,省略了高维元素个数 数据类型 数组名 [常量表达式2][常量表达式1]=初值表; M的初值表={M[0]初值表,M[1]初值表,…,M[m-1]初值表} M[i]初值表={M[i][0]初值表, M[i][1]初值表,…,M[i][n-1]初值表};i从0到m-1;

向上取整数(线形初值表项数/低维元素个数) C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 (2)线形初值表 线形初值表与一维数组的初值表相同,初值表的项数不超过各维元素个数的乘积(总元素个数)。 数组元素按内存排列顺序依次从初值表中取值,下列各数组使用了线形初值表,结果与使用嵌套初值表相同。 例如: int M[3][4]={1,2,3,4,3,4,5,6,5,6,7,8}; //M数组元素被全部初始化 int a[2][3]={1,0,0,0,1,1}; //初始化了全部数组元素, 一部分元素未给初值 int b[ ][3]={1,0,0,0,0,0}; //初始化了全部数组元素, 省略了高维元素个数 当使用线形初值表而省略高维元素个数时,高维元素个数为: int b[ ][3]={1,0,0,0};高维元素个数为2 向上取整数(线形初值表项数/低维元素个数)

5.2.2 二维数组的定义与使用 (3)二维数组的存取 存取维数组元素的格式为: 说明: 数组名 [行下标表达式] [列下标表达式] C++语言程序设计教程 第5章 构造数据类型 5.2.2 二维数组的定义与使用 (3)二维数组的存取 存取维数组元素的格式为: 说明: 行下标表达式与列下标表达式的值同样从0开始,a[i][j]表示数组的第i+1行、第j+1列的元素。由于数组元素是变量,可以对其进行各种各种操作。 数组元素如果定义数组a[m][n], 即数组第1维大小为n, 第2维大小为m。a[i][j]的排列位置与在内存中的地址计算公式如下: 例如: a, a[0]: 为数组a的起始地址, 即a[0][0]的地址; a[i]+j: 为数组的第i+1行的第j+1元素的地址,即a[i][j]的地址; 数组名 [行下标表达式] [列下标表达式] a[i][j]的排列位置=第1维大小n*i+j+1; a[i][j]的地址=a的起始地址+(第1维大小n*i+j)*sizeof(数据类型)

二维数组定义 并初始化 二维数组使用 C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /********************************************************************** * p5_3.cpp * * 求学生多门功课的总分,并求所有学生各门功课的平均分 * ***********************************************************************/ #include<iostream> using namespace std; void main() { const int MaxN=100,CourseN=5; int n,score[MaxN][CourseN+1]={0}; float aver[CourseN+1]={0}; for (n=0;n<MaxN;n++) //输入学生成绩 for(int j=0;j<CourseN;j++) cin>>score[n][j]; if (score[n][0]<0) break; //输入-1,结束输入 } for (int i=0;i<n;i++) //计算每个学生的总分 score[i][CourseN]=score[i][CourseN]+score[i][j]; 二维数组定义 并初始化 二维数组使用

C++语言程序设计教程 第5章 构造数据类型 21 22 23 24 25 26 27 28 29 30 31 32 33 34 36 37 for (int j=0;j<CourseN+1;j++) //计算每门课程的平均分 { for (int i=0; i<n; i++) aver[j]=aver[j]+score[i][j]; aver[j]=aver[j]/n; } for (i=0;i<n;i++) //输出每个人的成绩与总分 for(int j=0;j<CourseN+1;j++) cout<<score[i][j]<<"\t"; cout<<endl; cout<<"--------------------------------------"<<endl; for (i=0;i<CourseN+1;i++) //输出每门功课的平均分 cout<<aver[i]<<"\t"; 运行结果: 70 71 72 73 74↙ 82 83 84 85 86↙ 92 93 94 95 96↙ -1 0 0 0 0↙ 70 71 72 73 74 360 82 83 84 85 86 420 92 93 94 95 96 470 ------------------------------------------------------------------------- 81.3333 82.3333 83.3333 84.3333 85.3333 416.667

5.2.3 多维数组 C++语言程序设计教程 (1)三维以及高于三维的数组称为多维数组 第5章 构造数据类型 5.2.3 多维数组 (1)三维以及高于三维的数组称为多维数组 多维数组的定义与二维数组类似,可以一次定义,也可以逐步由低维数组定义。 例如: int b[2][3][4]; //定义了一个三维数组 也可用下列方式步定义: typedef int B1[3][4]; B1 b[2]; 多维数组在内存的排列方式同样是先排低维数组,由低向高依次排列。 如:b[2][3][4]的排列顺序为: b[0][0]:b[0][0][0],b[0][0][1],b[0][0][2],b[0][0][3] b[0] b[0][1]:b[0][1][0],b[0][1][1],b[0][1][2],b[0][1][3] b b[0][2]:b[0][2][0],b[0][2][1],b[0][2][2],b[0][2][3] b[1][0]:b[1][0][0],b[1][0][1],b[1][0][2],b[1][0][3] b[1] b[1][1]:b[1][1][0],b[1][1][1],b[1][1][2],b[1][1][3] b[1][2]:b[1][2][0],b[1][2][1],b[1][2][2],b[1][2][3]

C++语言程序设计教程 第5章 构造数据类型 5.2.3 多维数组 (2)多维数组的初始化与存取 多维数组的初始化形式与二维数组类似:有嵌套初值表、线性初值表两种形式。使用线性初值表初始化时,数组元素按内存排列顺序依次从初值表中取值。 对多维数组进行存取包括对数组元素的存取和对数组元素的地址的读取,当一个多维数组的下标数与维数相同时,为对数组元素的存取。 当下标个数小于维数时表示的是一个地址,表示地址时,下标也不能越界。 如:下列都是b[2][3][4]的地址表达式。 b[1]; //b[1][0][0]的地址; b[2]; //错误,下标越界; b[0]+1; //与b[0][1]相同,b[0][1][0]的地址。 b[1][2]; //b[1][2][0]的地址 b[1][2]+4; //b[1][2][4]的地址,但数组b中没有b[1][2][4]这个元素, 故指向了其它地方。

/****************************************** * 5_4.cpp * 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /****************************************** * 5_4.cpp * * 多维数组的各种地址表达式 * *******************************************/ #include<iostream> using namespace std; void main() { const int K=3,M=4,N=5; int k=2,m=3,n=4; short int b[K][M][N]; cout<<"Add of b: &b="<<&b<<endl; cout<<" b="<<b<<endl; cout<<" &b[0][0][0]="<<&b[0][0][0]<<endl; cout<<"Add of b[k]: &b[k]="<<&b[k]<<endl; cout<<" b[k]="<<b[k]<<endl; cout<<" b+k ="<<(b+k)<<endl; cout<<" &b[0]+k="<<(&b[0]+k)<<endl; cout<<" &b[k][0][0]="<<&b[k][0][0]<<endl;

C++语言程序设计教程 第5章 构造数据类型 19 20 21 22 23 24 25 26 27 28 29 30 31 cout<<"Add of b[k][m]: &b[k][m]="<<&b[k][m]<<endl; cout<<" b[k][m]="<<b[k][m]<<endl; cout<<" b[k]+m ="<<(b[k]+m)<<endl; cout<<" &b[0][0]+k*M+m="<<(&b[0][0]+k*M+m)<<endl; cout<<" &b[k][m][0]="<<&b[k][m][0]<<endl; cout<<" &b[0][0]+(k*sizeof(b[0]))/sizeof(b[0][0])+m="; cout<<(&b[0][0]+(k*sizeof(b[0]))/sizeof(b[0][0])+m)<<endl; cout<<"Add of b[k][m][n]: &b[k][m][n]="<<&b[k][m][n]<<endl; cout<<" b[k][m]+n="<<(b[k][m]+n)<<endl; cout<<" &b[0][0][0]+k*M*N+m*N+n="<<(&b[0][0][0]+k*M*N+m*N+n)<<endl; cout<<" &b[0][0][0]+(sizeof(b[0])*k+sizeof(b[0][0])*m)/sizeof(b[0][0][0])+n="; cout<<(&b[0][0][0]+(sizeof(b[0])*k+sizeof(b[0][0])*m)/sizeof(b[0][0][0])+n)<<endl; } 运行结果: Add of b: &b=0012FEF0 b=0012FEF0 &b[0][0][0]=0012FEF0 Add of b[k]: &b[k]=0012FF40 b[k]=0012FF40 b+k =0012FF40 &b[0]+k=0012FF40 &b[k][0][0]=0012FF40 Add of b[k][m]: &b[k][m]=0012FF5E b[k][m]=0012FF5E b[k]+m =0012FF5E &b[0][0]+k*M+m=0012FF5E &b[k][m][0]=0012FF5E &b[0][0]+(k*sizeof(b[0]))/sizeof(b[0][0])+m=0012FF5E Add of b[k][m][n]: &b[k][m][n]=0012FF66 b[k][m]+n=0012FF66 &b[0][0][0]+k*M*N+m*N+n=0012FF66 &b[0][0][0]+(sizeof(b[0])*k+sizeof(b[0][0])*m)/sizeof(b[0][0][0])+n=0012FF66

C++语言程序设计教程 第5章 构造数据类型 5.2.4 数组与函数 数组名是一个地址,不能当作一个左值,但是可以作为函数的形参,接受实参传送来的地址。当形参接受实参传送来的地址后,形参数组与实参共享内存中的一块空间。函数体通过形参对数组内容的改变会直接作用到实参上。数组名作为形参是数组应用的一个重要方面。 注意: (1)使用数组名传递地址时,虽然传递是地址,但形参与实参的地址 (数组)类型应一致。 (2)形式参数中数组元素个数没有给定,因此,在函数体中,对数组存取的下标可以为任意值而不会出现编译错误。但是,当这个下标超过了实在参数数组的个数范围时,存取的就不是实在参数数组中的内容了。

数组作为函数形式参数 C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /************************************************** * 5_5.cpp * * 利用对一个多维数组的某列排序的函数, * * 将学生某门功课的成绩分班级排序 * **************************************************/ #include<iostream> using namespace std; const col=5; enum dir {Asc,Des}; void sort(int a[][col], int n, int Cn, dir D) //排序 { int t[col]; //用于暂存一行数据 for (int i=0;i<n-1;i++) for (int j=i+1;j<n;j++) //从待排序序列中选择一个最大(小)的数组元素 if (a[i][Cn]<a[j][Cn]&&D==Des||a[i][Cn]>a[j][Cn]&&D==Asc) memcpy(t,a[i],sizeof(t)); //交换数组行 memcpy(a[i],a[j],sizeof(t)); memcpy(a[j],t,sizeof(t)); } 数组作为函数形式参数

C++语言程序设计教程 第5章 构造数据类型 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 void main() { const CourseN=5; int n,score[][CourseN]={{20060101, 1, 82, 86, 0}, {20060203, 2, 80, 80, 0}, {20060204, 2, 86, 90, 0}, {20060205, 2, 90, 83, 0}, {20060102, 1, 75, 86, 0}}; n=sizeof(score)/sizeof(score[0]); for (int i=0;i<n;i++) //计算每个学生的总分 for(int j=2;j<CourseN-1;j++) score[i][CourseN-1]=score[i][CourseN-1]+score[i][j]; sort(score,n,4,Des); //按总分降序排序 sort(score,n,1,Asc); //按班号的升序排序 for (i=0;i<n;i++) //输出每个人的成绩与总分 for(int j=0;j<CourseN;j++) cout<<score[i][j]<<"\t"; cout<<endl; } 数组作为函数形式参数 运行结果: 20060101 1 82 86 168 20060102 1 75 86 161 20060204 2 86 90 176 20060205 2 90 83 173 20060203 2 80 80 160

C++语言程序设计教程 第5章 构造数据类型 5.2.5 字符数组与字符串 存放字符型数据的数组称为字符数组。字符数组也分为一维数组和多维数组。前述的数组的定义及初始化同样适用于字符数组,除此以外,C++对字符数组的初始化还可以使用字符串形式。 1.用字符进行初始化  例如:      char s1[ ]={'C','h','i','n','a'}; char s2[ ][4]={{'H','o','w'},{'a','r','e'},{'y','o','u'}}; 2.用字符串进行初始化 例如: char s3[ ]="China"; char s4[ ][4]={"how", "are", "you"};

C++语言程序设计教程 第5章 构造数据类型 5.2.5 字符数组与字符串 3. 字符数组的使用 字符数组也是数组,我们同样可以通过数组名及下标引用数组中的元素。 为方便对字符与字符串的处理,C++提供了许多专门处理字符与字符串的函数,这些函数原型在各自的头文件中定义。 下表列出了常用的字符与字符串处理函数的调用方法与功能简介,函数原形与详细的功能可以从C++编译器的帮助文档中获得。  常用字符与字符串处理函数 函数的用法 strlen(字符串) 返回字符串的长度(不包括\0) Cstring strset(字符数组, 字符C) 将字符数组中的所有字符都设为指定字符C, 并以\0结尾 strlwr(字符串) 将字符串中的所有字符转换成小写字符 strupr(字符串) 将字符串中的所有字符转换成大写字符 strcmp(串s1, 串s2) 比较两个字符串的大小,即按从左到右的顺序逐个比较对应字符的ASCII码值。若s1大于s2,返回1;若s1小于s2,返回-1;若s1等于s2,返回0。串s1、s2可以是字符串常量

5.2.5 字符数组与字符串 函数的用法 C++语言程序设计教程 第5章 构造数据类型 常用字符与字符串处理函数  常用字符与字符串处理函数 函数的用法  strcpy(串s1, 串s2) 将字符串s2拷贝到s1所指的存储空间中,然后返回s1。 其中, 串s2可以是字符串常量 Cstring strcat(串s1, 串s2) 返回字符串的长度(不包括\0) Ctype toupper(字符) 将字符数组中的所有字符都设为指定字符C, 并以\0结尾 tolower(字符) 将字符串中的所有字符转换成小写字符 Cstdlib atoi(字符串) 将字符串中的所有字符转换成大写字符 atol(字符串) 比较两个字符串的大小,即按从左到右的顺序逐个比较对应字符的ASCII码值。若s1大于s2,返回1;若s1小于s2,返回-1;若s1等于s2,返回0。串s1、s2可以是字符串常量 atof(字符串)

/********************************* * p5_6.cpp * * 字符串排序与查找 * 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /********************************* * p5_6.cpp * * 字符串排序与查找 * **********************************/ #include<iostream> using namespace std; const NameLen=20; void order(char name[][NameLen],int n) //字符串排序 { char temp[NameLen]; for(int i=0;i<n-1;i++) //选择排序 for(int j=i+1;j<n;j++) if(strcmp(name[i],name[j])>0) //比较两个字符串的大小 strcpy(temp,name[i]); //字符串交换 strcpy(name[i],name[j]); strcpy(name[j],temp); }

运行结果: 1 AnZijun 2 ChenHailing 3 CuiPeng 4 GongJing 5 HuangPin 第5章 构造数据类型 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int find(char name[][NameLen],int n,char aname[NameLen]) { for(int i=0;i<n;i++) if(strcmp(name[i],aname)==0) //找到,返回位置。 return i+1; else if(strcmp(name[i],aname)>0) //未找完,但找不到,返回0 return 0; return 0; //找完,找不到,返回 } void main() { charNameTab[][NameLen]={"GongJing","LiuNa","HuangPin","AnZijun", "LianXiaolei","ChenHailing","CuiPeng","LiuPing"}; char aname[NameLen]; int n=sizeof(NameTab)/NameLen; order(NameTab,n); for(int i=0;i<n;i++) //输出排序后的各姓名 cout<<i+1<<'\t'<<NameTab[i]<<endl; cout<<"Input a name:"; cin>>aname; if(n=find(NameTab,n,aname)) cout<<"Position:"<<n<<endl; else cout<<"Not found!"<<endl; 运行结果: 1 AnZijun 2 ChenHailing 3 CuiPeng 4 GongJing 5 HuangPin 6 LianXiaolei 7 LiuNa 8 LiuPing Input a name:LiuPing↙ Position:8

5.3 指针 指针是C++语言最重要特性之一,也是C++的主要难点。 第5章 构造数据类型 5.3 指针 指针是C++语言最重要特性之一,也是C++的主要难点。 指针提供了一种较为直观的地址操作的手段,正确地使用指针,可以方便、灵活而有效地组织和表示复杂的数据。 指针在C++程序中扮演着非常重要的角色,从某种程度上而言,如果不能深刻的理解指针的概念,正确而有效的掌握指针,就不可能真正学好C++,但是指针也是我们最容易产生困惑并导致程序出错的原因之一。

C++语言程序设计教程 第5章 构造数据类型 5.3 指针 1. 地址与指针 地址: 当定义一个变量后,内存中将会划出一块由若干个存储单元组成的区域,用于保存该变量的数据。在内存里每个存储单元都有各自的编号,称为地址。 指针: 在C++中,提供了指针类型,它是一种用于存放内存单元地址的变量类型,地址就存储在这种指针类型的变量中。正因为指针变量存储的是地址,用它来指明内存单元,所以形象地称这种地址变量为指针。

5.3 指针 2. 指针变量的定义 数据类型是指针变量所指向对象的数据类型,它可以是基本数据类型, 也可以是构造数据类型以及void 类型。 C++语言程序设计教程 第5章 构造数据类型 5.3 指针 2. 指针变量的定义 每存储一个地址,就要定义一个指针变量。定义指针变量的格式如下: 数据类型是指针变量所指向对象的数据类型,它可以是基本数据类型, 也可以是构造数据类型以及void 类型。 变量名是用户自定义的标识符。 *表示声明的变量是一个指针变量,而不是普通变量。 例如:    int *ip; //定义了一个int型的指针变量ip float *fp; //定义了一个float型指针变量fp typedef int A[10]; A *ap; // 定义了一个A类型的指针变量ap sizeof(ip)=sizeof(fp)=sizeof(ap)=4; 数据类型 * 变量名;

C++语言程序设计教程 第5章 构造数据类型 5.3 指针 3.指针的初始化与赋值 定义了一个指针,只是得到了一个用于存储地址的指针变量。若指针变量既没有初始化,也没有赋值,其地址值是一个随机的数。 (1)不要将一个非地址常量、变量以及无意义的实际地址赋给指针变量。如: int *p=100; //错误,100是一个int型常量,不是一个地址常量。 int *p=(char *)100; //危险!100是一个无意义的实际地址, 可能指向危险区域。 (2)可以使用一个已初始化的指针去给另一个指针赋值,但类型必须一致如果不一致,可 进行强制类型转换。 char *p=NULL; int *ip=(int *)p+100; //将char型指针强制转化成int型指针。 (3)对于基本数据类型的变量、数组元素我们可以使用取地址运算符&来获得它们的地址, 但是也只有类型一致才能赋值。 int a[10]; //定义int型数组 int *i_pointer=a; //定义并初始化int型指针 (4)有一种特殊的void类型指针,可以存储任何的类型地址;但将一个void类型的地址赋值 给非void类型的指针变量,要使用类型强制转换。 void v; //错误,不能定义void类型的变量 void *vp; //定义void类型的指针 数据类型 *指针变量名=初始地址表达式;

C++语言程序设计教程 第5章 构造数据类型 5.3 指针 4.指针运算 指针变量存放的是地址,因此指针的运算实际上就是地址的运算,但正是由于指针的这一特殊性,使指针所能进行的运算受到了一定的限制。指针通常进行下列几种运算:赋值运算、取值运算、算术运算、相减运算、比较运算。 (1)* 和&运算 *称为指针运算符。出现在数据定义语句中时,* 在数据类型与变量之间,是一个二元运算符,用来定义指针变量;出现指针变量表达式左边时,是一个一元运算符,表示访问指针所指对象的内容。 例如: int a[4]={1,2,3}; int *ip=&a[2]; cout<<*ip; // 输出ip指向单元的内容,内容为整型数3 *ip=100; // 将100赋给a[2];

5.3 指针 运行结果: (2)指针与整数的加减运算 typedef char A[10]; int *p1=b[1][0]; 第5章 构造数据类型 5.3 指针 (2)指针与整数的加减运算 指针的加减运算与普遍变量的加减运算不同,由于指针存储的是变量的内存地址,指针加上或减去一个整数n,表示指针从当前位置向后或向前移动n个sizeof(数据类型)长度的存储单元。因此对于不同的数据类型,n的实际大小就不同。 例如程序段: int b[2][3][4]; typedef char A[10]; int *p1=b[1][0]; int *p2=(int *)b[1]; int *p3=(int *)(b+1); double *pd=(double *)p3; A *pa=(A *)p3; cout<<p1<<","<<p2<<","<<p3<<","<<pd<<","<<pa<<endl; cout<<p1+1<<","<<p2+1<<","<<p3+1<<","<<pd+1<<","<<pa+1<<endl; 运行结果: 0013FF80, 0013FF80, 0013FF80, 0013FF80, 0013FF80 0013FF84, 0013FF84, 0013FF84, 0013FF88, 0013FF8A

5.3 指针 (3)指针自增、自减运算 (4)两指针相减 C++语言程序设计教程 第5章 构造数据类型 5.3 指针 (3)指针自增、自减运算 指针的自增、自减运算是指针加减运算的特例。指针的自增或自减表示指针从当前位置向后或向前移动sizeof(数据类型)长度的存储单元。 例如:程序段: int *p, *q, a=5; p=&a; p++; //指针p后移4个字节 *p++; //先读取p指向的变量a的值5,然后使指针p后移4个字节 (*p)++; //读取p指向的变量a的值,然后使p指向的变量a自增1  *++p; //先使指针p后移4个字节,然后读取p指向的变量的值 ++*p; //将p指向的变量a自增1 *q++=*p++; //这是一种常用的表达式,依次执行:*q=*p, q++, p++ (4)两指针相减 当两个指针指向同一数组时,两个指针的相减才有意义。两个指针相减的结果为一整数,表示两个指针之间数组元素的个数。   

C++语言程序设计教程 第5章 构造数据类型 5.3 指针 (5)两个指针的比较运算     两个指针的比较一般用于下列两种情况:一是比较两个指针所指向的对象在内存中的位置关系;二是判断指针是否为空指针。  5. void类型指针 指向void类型的指针是一种不确定类型的指针,它可以指向任何类型的变量。实际使用void型指针时,只有通过强制类型转换才能使void型指针得到具体变量的值。在没有转换前void型指针不能进行指针的算术运算。 例如: void *vp; //定义了一个void型指针vp    int i=6, *ip;    vp=&i; // vp指向整型变量i cout<<“i=”<<*vp<<endl; // 错误 cout<<"i="<<*(int *)p<<endl; ip=(int *)vp; //ip指向vp指向的变量i     cout<<"i="<<*ip<<endl;

C++语言程序设计教程 第5章 构造数据类型 5.3.2 指针与字符串 字符型指针: 用于存放字符型变量的地址,而字符串的本质是以\0结尾的字符数组,一个字符型指针存储了字符数组的第一个元素的地址,也就存储了字符串的地址,这个指针就指向了字符串。 在定义一个字符数组时,可以将一个字符串常量作为初值,但将字符串常量作为初值赋给字符数组和将字符串常量作为初值赋给字符指针变量,二者的含义是不同的。 例如: char str[5]="abcd"; char *p_str="abcd"; 在上述字符串定义有下列不同: (1) 字符数组str[5]被赋值为“abcd”,因此,数组str的五个数组元素的值分别为 字符‘a’、‘b’、‘c’、‘d’和‘\0’。字符指针p_str被赋值为“abcd”,则意味着 指针p_str的值为字符串常量"abcd"的第一个字符'a'在内存中的地址。 (2) 字符指针p_str比str多分配了一个存储地址的空间,用于存储字符串的首地 址。

C++语言程序设计教程 第5章 构造数据类型 5.3.3 指针与数组 1 使用指针操作符*存取数组 指针的加减运算的特点使得指针操作符特别适合处理存储在一段连续内存空间中的同类型数据。这样,使用指针操作符来对数组及其元素进行操作就非常方便。 (1) 一维数组的指针操作   当定义数组一维数组T a[N] (T为类型),下式为存取数组元素a[i]的等效方式:   *(a+i); 而a+i为a[i]的地址。 (2) 多维数组的指针操作 对于多维数组,使用指针同样可以灵活访问数组元素。若定义数组 T b[K][M][N], 下式为存取数组元素a[i][j][k]的等效方式: 例如: *(*(*(b+i)+j)+k); 其中:*(*(b+i)+j)+k为b[i][j][k]的地址,即&b[i][j][k]; *(*(b+i)+j)为b[i][j][0]的地址,即b[i][j]; *(b+i)为b[i][0][0]的地址,即b[i];

5.3.3 指针与数组 2 指针数组 指针数组是以指针变量为元素的数组,指针数组的每个元素都是同一类型的指针变量。 注意: C++语言程序设计教程 第5章 构造数据类型 5.3.3 指针与数组 2 指针数组     指针数组是以指针变量为元素的数组,指针数组的每个元素都是同一类型的指针变量。 在指针数组的定义中有两个运算符:*和[ ],运算符[ ]的优先级高于*,所以*p[N]等价于*( p[N]),p [N]表示一个数组,而*表示数组元素为指针变量。 例如: int *p_a[5]; 定义了一个指针数组,其中有5个数组元素,每个数组元素都是一个int类型的指针。 注意: 对指针数组的存取同样可以使用指针方式、数组方式、以及指针与数组结合的方式。一维指针数组与二维指针数组对应,可采用存取二维数组的方式存取。 指针数组的每一个元素都是一个指针,因此必须先赋值,后引用。

5.3.3 指针与数组 3)数组指针 数组指针是指向数组的指针。 C++语言程序设计教程 第5章 构造数据类型 5.3.3 指针与数组 3)数组指针     数组指针是指向数组的指针。 虽然运算符[ ]的优先级高于*,但是,在数组指针的定义式中,(* 指针名)改变了这种优先级,它表明定义的首先是一个指针,然后才是什么类型的指针。 例如: int (* a_p) [5]; 等效于下列定义方式: ①typedef int I_A[5]; ②I_A *a_p;

C++语言程序设计教程 第5章 构造数据类型 5.3.4 多重指针 如果已经定义了一个指针类型,我们再定义一个指针,用于指向已经定义的指针变量,后面定义的指针变量就是一个指向指针的指针变量,简称指向指针的指针,这样的指针也称二重(级)指针。 三重及以上的指针统称为多重指针。 例如: int **pp; 定义了一个二级指针变量,等效于下列定义方式: typedef int * P; P *p; 二级指针常用来指向一个指针数组。 例①: int a[2][3]={1,2,3,4,5,6}; //声明并初始化二维数组 int *p_a[3]; //声明整型指针数组 p_a[0]=a[0]; //初始化指针数组元素 p_a[1]=a[1]; pp=p_a;

5.3.4 多重指针 注意:在使用二级指针时经常容易犯两类错误。 (1)类型不匹配 C++语言程序设计教程 第5章 构造数据类型 5.3.4 多重指针 注意:在使用二级指针时经常容易犯两类错误。 (1)类型不匹配 例如: pp=a; //错误,因为pp是一个int **型变量,a是一个int [2][3]型的地址; pp=&a; //错误,pp是一个int **型变量,&a是一个(*)int [2][3]型的地址; pp=&a[0];//错误, pp是一个int **型变量,&a[0]是一个(*)int [3]型的地址 (2)指针没有逐级初始化 例如: int i=3; int **p2; *p2=&i; **p2=5; cout<<**p2; 虽然上述程序段编译、连接均没有错误,但运行时出错。其原因在于int **p2;只是定义了一个指针变量,变量中的内容(地址)是一个无意义的地址,而*p2=&i是对无意义的内存单元赋值,这样是错误与危险的。正确的作法是从第一级指针开始,逐级初始化。

5.3.4 多重指针 3)逐级初始化多级指针 例②: int i=3; int **p2; int *p1; C++语言程序设计教程 第5章 构造数据类型 5.3.4 多重指针 3)逐级初始化多级指针 例②: int i=3; int **p2; int *p1; *p1=&i; //初始化一级指针 p2=*p1; //初始化一级指针 **p2=5; //通过指针给变量i赋值 cout<<**p2; //结果为5 上述两个二级指针在内存中 的分布如右图所示。

5.3.4 多重指针 注意: 当一个二级指针指向一个指针数组后,对数组元素的存取可以使用指针方式、数组方式、混合方式: 例如: C++语言程序设计教程 第5章 构造数据类型 5.3.4 多重指针 当一个二级指针指向一个指针数组后,对数组元素的存取可以使用指针方式、数组方式、混合方式: 例如: p[i][j]; //存取数组元素a[i][j]的数组方式; *(*(p+i)+j); //存取数组元素a[i][j]的指针方式; *(p[i]+j); //存取数组元素a[i][j]的混合方式; 一般而言,二重指针就足够使用了,三重指针使用场合就很少了,使用多重指针同样要: 注意: 不管是多少重指针,定义后,只建立了一个指针变量,分配一个指针变量的空间。 要初始化多重指针,要从第一层开始,逐步向高层进行。

C++语言程序设计教程 第5章 构造数据类型 5.3.5 动态内存分配 在C++中,动态内存分配技术可以保证程序在运行过程中根据实际需要申请适量的内存,使用结束后还可以释放。C++通过new运算和delete运算来实现动态内存分配。 1. new运算 new运算的作用是按指定类型和大小动态的分配内存。 基本语法形式为:   其中: 数据类型可以是基本数据类型,也可以是用户自定义的复杂数据类型。 new运算符在堆(内存)中创建一个由类型名指定类型的对象,如果创建成功, 返回对象的地址;否则返回空指针NULL。 初值列表给出被创建对象的初始值。 由于返回的是地址,所以要用事先定义一个类型相同的指针变量来存储这个 地址。 指针变量=new 类型名 (初值列表);

5.3.5 动态内存分配 1)new创建指定类型对象 例①: int *ip; C++语言程序设计教程 第5章 构造数据类型 5.3.5 动态内存分配 1)new创建指定类型对象 例①: int *ip; ip=new int(5); //ip指向1个初值为5的int型对象 也可以使用一条语句定义: int *ip=new int(5); 说明: 首先定义了一个整型指针ip; 然后申请内存,创建一个int型数据对象,并将该数据对象初始化为5; 最后返回创建的数据对象的地址,存入ip。

5.3.5 动态内存分配 2)new创建动态数组时 其语法格式如下: 其中: 下标表达式与数组初始化时的常量表达式不同,可以是变量表达式。 C++语言程序设计教程 第5章 构造数据类型 5.3.5 动态内存分配 2)new创建动态数组时 使用new运算可创建一个数据对象,也可以创建同类型的多个对象----数组。由于数组大小可以动态给定,所创建的对象称为动态数组。new创建动态数组时,需要给出数组的结构说明。 其语法格式如下: 其中: 下标表达式与数组初始化时的常量表达式不同,可以是变量表达式。 用new申请失败时,返回NULL。申请一个动态数组,往往需要较大的空间,因此,在程序中需要对new的返回值进行判断,看是否申请成功。 例②: int *pa; pa=new int [5]; //ip指向5个未初始化的int型数据对象的首地址 指针变量=new 类型名 [下标表达式];

指针变量=new 类型名T [下标表达式1][ 下标表达式2][…] C++语言程序设计教程 第5章 构造数据类型 5.3.5 动态内存分配 3)new创建多维数组 使用new也可创建多维数组,其语法形式如下: 其中: 当用new创建多维数组时,只有下标表达式1可以任意结果是正整数的表达式,而其它下标表达式必须是值为正整数的常量表达式。 如果内存申请成功,new运算返回一个指向新分配内存首地址的指针,它是一个T类型数组的指针,而不是T类型指针。数组元素的个数为除最左边一维(最高维)外各维下标表达式的乘积。 例③: int *pb; pb = new int[3][4][5]; //错误,new操作产生的是指向一个int[4][5]的二维int //型数组的指针 int (*pb)[4][5]; pb = new int[3][4][5]; //正确,针pb既可以作为指针使用,也可以像一个 //三维数组名一样使用 指针变量=new 类型名T [下标表达式1][ 下标表达式2][…]

5.3.4 多重指针 2、delete运算 当程序不再需要由new分配的内存空间时,可以用delete释放这些空间。 其语法格式为: C++语言程序设计教程 第5章 构造数据类型 5.3.4 多重指针 2、delete运算 当程序不再需要由new分配的内存空间时,可以用delete释放这些空间。 其语法格式为: 如果删除的是动态数组,则语法格式为: 其中: 括号[ ]表示用delete释放为多个对象分配的地址,[ ]中不需要说明对象的个数。 不管建立的动态数组是多少维,释放的格式都是一样。 对于例①②③分配的空间,释放语句如例④: delete ip; delete [] pa; delete [] pb; delete 指针变量名; delete [ ] 指针变量名;

C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 /********************************* * p5_7.cpp * * 动态二维矩阵 * **********************************/ #include<iostream> using namespace std; void main() { int m,n; int **dm; cout<<"input matrix size m,n:"; cin>>m>>n; dm=new int * [m]; //建立m个指针,存储m行 for(int i=0;i<m;i++) //为每行分配n个空间 if((dm[i]=new int [n])==NULL) exit(0); for (i=0;i<m;i++) //输入矩阵元素 for(int j=0;j<n;j++) cin>>dm[i][j]; }

input matrix size m,n:2 3↙ 1 2 3↙ 4 5 6↙ 1 2 3 4 5 6 注意: 运行结果: C++语言程序设计教程 第5章 构造数据类型 22 23 24 25 26 27 28 29 30 31 for (i=0;i<m;i++) //输出输出矩阵元素 { for(int j=0;j<n;j++) cout<<dm[i][j]<<"\t"; cout<<endl; } for(i=0;i<m;i++) //释放m个指针指向的空间; delete [ ] dm[i]; delete [ ] dm; //释放m个指针 注意: 各用new运算符申请分配的内存空间,必须用delete释放; delete作用的指针必须是由new分配内存空间的首地址; 对于一个已分配内存的指针,只能用delete释放一次; new和delete运算实现了堆空间的动态分配,它在带来方便的同时也潜伏了可能 的隐患:使用new进行内存分配之后,忘记了使用delete运算进行内存回收,即 “内存泄露”,如果程序长时间运行,则有可能因内存耗尽而使系统崩溃,所 以对new和delete要养成配对使用的良好习惯。 运行结果: input matrix size m,n:2 3↙ 1 2 3↙ 4 5 6↙ 1 2 3 4 5 6

C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 1、指针作为函数参数 指针作为函数参数是一种地址传递方式。当需要在不同的函数之间传递大量数据时,程序执行时调用函数的开销就会比较大,这时,如果需要传递的数据是存放在一个连续的内存区域中,就可以采用指针作为函数参数,只传递数据的起始地址,而不必传递数据的值,这样就会减小开销,提高效率。 指针可以作为函数的形参,也可以作为函数的实参。如果以指针作为函数的形参,在调用时实参将值传递给形参,也就是使实参和形参指针变量指向同一内存地址,这样在子函数运行过程中,通过形参指针对数据值的改变也同样影响着实参所指向的数据值,从而实现参数双向传递的目的,即通过在被调用函数中直接处理主调函数中的数据而将函数的处理结果返回其调用者。 【例5-8】用传指针方式实现两个数据交换 分析:用传值的方式无法实现两个数据交换。用指针作为形参,从实参传入要交换数据的地址,在函数体内将指针指向的两个数据交换存储位置,这样通过“釜底抽薪”的方式实现了数据交换。为了实现不同类型的数据交换,形式参数采用void指针。

C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /********************************************** * p5_8.cpp * * 实现两个数据交换的通用程序 * ***********************************************/ #include <iostream> using namespace std; void swap_i(int *num1,int *num2) //整型数交换 { int t; t=*num1; *num1=*num2; *num2=t; } void swap(void *num1,void *num2,int size) // 所有类型数据交换 char *first=(char *)num1,*second=(char *)num2; for(int k=0;k<size;k++) char temp; temp=first[k]; 指针作为函数形式参数

before swap:c1=John c2=Antony after swap: c1=Antony c2=John 第5章 构造数据类型 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 first[k]=second[k]; second[k]=temp; } void main() { int a=3,b=6; double x=2.3,y=4.5; char c1[8]="John",c2[8]="Antony"; cout<<"before swap:a="<<a<<" b="<<b<<endl; swap_i(&a,&b); cout<<"after swap: a="<<a<<" b="<<b<<endl; cout<<"before swap:x="<<x<<" y="<<y<<endl; swap(&x,&y,sizeof(x)); cout<<"after swap: x="<<x<<" y="<<y<<endl; cout<<"before swap:c1="<<c1<<" c2="<<c2<<endl; swap(&c1,&c2,sizeof(c1)); cout<<"after swap: c1="<<c1<<" c2="<<c2<<endl; 运行结果: before swap:a=3 b=6 after swap: a=6 b=3 before swap:x=2.3 y=4.5 after swap: x=4.5 y=2.3 before swap:c1=John c2=Antony after swap: c1=Antony c2=John  

C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 2 指针型函数 除void类型的函数之外,每个被调用函数在调用结束之后都要有返回值,指针也可以作为函数的返回值。当一个函数声明其返回值为指针类型时,这个函数就称为指针型函数。 指针型函数定义形式为: 其中, 数据类型表明函数返回指针的类型;*表示函数的返回值是指针型,可以使用多个*返回多重指针。 使用指针型函数的最主要目的就是要在函数调用结束时把大量的数据从被调用函数返回到主调函数中,而通常非指针型函数调用结束后,只能返回一个值。 在调用指针型函数时,需要一个同类型的指针变量接受返回的值。 数据类型  * 函数名(参数表);

C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /******************************** * p5_9.cpp * * 超长整数加法 * ********************************/ #include <iostream> using namespace std; char *ladd(char *s1, char *s2) { int n1,n2,n; char *res,c=0; n1=strlen(s1); //n1=数字串s1的长度 n2=strlen(s2); //n2=数字串s2的长度 n = n1>n2 ? n1 : n2; //数字串s1,s2最大长度 res=new char [n+2]; //申请存结果串的内存 for(int i=n+1;i>=0;i--) //将s1从低位开始搬到res, res[i] = i>n-n1 ? s1[i-n-1+n1] : '0';

C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 for(i=n;i>=0;i--) { char tchar; tchar = i>n-n2 ? res[i]-'0'+s2[i-n+n2-1]-'0'+c : res[i]-'0'+c; //将数字符变成数 c = tchar>9 ? 1 : 0; //设进位 res[i] = c>0 ? tchar-10+'0' : tchar+'0'; //将数字变成数字字符 } return res; void main() { char num1[100],num2[100],*num; cin>>num1>>num2; num=ladd(num1,num2); cout<<num1<<"+"<<num2<<"="<<num<<endl; delete [ ] num; 运行结果: 12345678901234↙ 99999↙ 12345678901234+99999=012345679001233

C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 3 指向函数的指针 在程序运行时,不仅数据要占用内存空间,程序的代码也被调入内存并占据一定的内存空间。每一个函数都有函数名,实际上,这个函数名就是该函数的代码在内存的起始地址。 当调用一个函数时,编译系统就是根据函数名找到函数代码的首地址,从而执行这段代码。由此看来,函数的调用形式:函数名(参数表),其实质就是:函数代码首地址(参数表)。 函数指针: 就是指向某个函数的指针,它是专门用于存放该函数代码首地址的指针变量。一旦定义了某个函数指针,那么,它就与函数名有同样的作用,在程序中就可以象使用函数名一样,通过指向该函数的指针来调用该函数。

5.3.6 指针与函数 函数指针的定义语法形式如下: 其中: C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 函数指针的定义语法形式如下: 其中: 数据类型为函数指针所指函数的返回值类型;形参表则列出了该指针所指函数的形参类型和个数。 函数指针名与*外面的圆括号()是必须的,圆括号改变了默认的运算符的优先级,使得该指针变量被解释为指向函数的指针。如果去掉圆括号,将被解释为函数的返回值为指针。 数据类型 (*函数指针名)(形参表);

5.3.6 指针与函数 函数指针在使用之前也要进行赋值,使指针指向一个已经存在的函数代码的起始地址。语法形式为: C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 函数指针在使用之前也要进行赋值,使指针指向一个已经存在的函数代码的起始地址。语法形式为: 赋值符右边的函数名所指出的必须是一个已经声明过的,和函数指针具有相同返回类型和相同形参表的函数。在赋值之后,就可以通过函数指针名来直接引用该指针所指向的函数。即:该函数指针可以与函数名一样,出现在函数名能出现的任何地方。 调用函数指针指向的函数有如下两种格式: 例如: int add(int a,int b); //定义函数 int (*fptr)(int a,int b); //定义函数指针 fptr=add; // 函数指针赋值,或fptr=&add; 函数指针名=函数名; ① 函数指针名(实参表); ② (* 函数指针名) (实参表);

5.3.6 指针与函数 说明: 采用下列任何一种形式调用函数sum: add(1,2); //用函数名调用函数sum C++语言程序设计教程 第5章 构造数据类型 5.3.6 指针与函数 采用下列任何一种形式调用函数sum:     add(1,2); //用函数名调用函数sum  (*fptr)(1,2); //用指向函数的指针调用函数sum     fptr(1,2); //用指向函数的指针调用函数sum 【例5-10】用指向函数的指针实现各种算术运算的菜单程序。 分析:为了实现菜单功能,需要定义一个函数指针数组来存储各函数名(地址),调用时通过各数组元素指向的函数名来调用对应的函数。 说明: 虽然三种调用形式的结果完全相同,当用指向函数的指针调用函数add()时,习惯上使用 (*fptr)(1,2),因为这种形式能更直观地说明是用指向函数的指针来调用函数。 指向函数的指针常用来实现菜单程序,根据不同的选择执行菜单项中对应的功能,各功能由指向函数的指针实现。

C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /******************************* * p5_10.cpp * * 菜单程序 * ********************************/ #include <iostream> using namespace std; int add(int a,int b) { return a+b; } int sub(int a,int b) { return a-b; int mul(int a,int b) { return a*b; int divi(int a,int b) { if (b==0) return 0x7fffffff; else return a/b; int (*menu[ ])(int a,int b)={add,sub,mul,divi};

Select operator: 1: add 2: sub 3: multiply 运行结果: C++语言程序设计教程 第5章 构造数据类型 21 22 23 24 25 26 27 28 29 30 31 32 void main() { int num1,num2,choice; cout<<"Select operator:"<<endl; cout<<" 1: add"<<endl; cout<<" 2: sub"<<endl; cout<<" 3: multiply"<<endl; cout<<" 4: divide"<<endl; cin>>choice; cout<<"Input number(a,b): "; cin>>num1>>num2; cout<<"Result:"<<menu[choice-1](num1,num2)<<endl; } 运行结果: Select operator: 1: add 2: sub 3: multiply

C++语言程序设计教程 第5章 构造数据类型 5.3.7 指针常量与常指针 1. 指针常量 如果在定义指针变量时,指针变量前用const修饰,被定义的指针变量就变成了一个指针类型的常变量,指针类型的常变量简称为指针常量。 定义指针常量格式如下:            修饰符const与指针变量紧邻,说明指针变量不允许修改。既然指针变量的值不能修改,所以一定要在定义时给出初值。 例如: char * const p="ABCDE"; //定义了一个指针常量 p=NULL; //错误,指针常量被赋给一个地址常量 p="1234"; //错误,指针常量被赋给一个地址常量 p=(char *)q; //错误,指针常量被赋给一个地址变量的值          因为const修饰的是指针变量,而不是指针指向的值。所以指针指向的值可以更改: *p='1'; //正确,更改指针指向的值。 p[1]='1'; //正确,用数组访问方式更改指针指向的值。 数据类型* const 指针变量 = 变量名;

C++语言程序设计教程 第5章 构造数据类型 5.3.7 指针常量与常指针 2. 常指针 如果在定义指针变量时,数据类型前用const修饰,被定义的指针变量就是指向常量的指针变量,指向常量的指针变量简称为常指针。 定义常指针的格式如下 定义一个常指针后,指针指向的值就不能被更改,即不能通过指针变量直接更改指针指向的值。 const char * p="ABCDE"; //定义了一个常指针 *p='1'; //错误,试图更改指针指向的值。 为了防止通过一个非常指针修改常指针指向的值,将一个常指针赋给一个非常指针是错误的: char *q; q=p; //错误,将一个常指针赋给非常指针。 const 数据类型* 指针变量 = 变量名; 或 数据类型 const * 指针变量 = 变量名;

5.3.7 指针常量与常指针 注意:常数组的所有元素必须全部赋初值。 C++语言程序设计教程 第5章 构造数据类型 5.3.7 指针常量与常指针 可以将一个非常指针赋给一个常指针,这是因为const修饰的是指针指向的内容,而不是指针的值(地址值)。 p=NULL; //正确 p=q; //正确 const用在数组的类型前修饰数组元素,数组元素为常量的数组的称为常数组,常数组的元素不可改变,也不可将地址赋值给非常指针变量。 const int a[3]={1,2,3}; //定义常数组; a[0]=0; //错误,常数组的元素不可修改 int *p=a; //错误,常数组的地址不能赋给非常指针变量 注意:常数组的所有元素必须全部赋初值。 const int a[]={1,2,3}; //正确 const char a[]={'1','2','3'}; //正确 const int a[10]={1,2,3}; //错误,常数组元素没有全部赋初值

C++语言程序设计教程 第5章 构造数据类型 5.3.7 指针常量与常指针 3. 常指针常量 指针常量保护指针的值不被修改,常指针保护指针指向的值不被修改,为了将两者同时保护,可以定义常指针常量,常指针常量意为一个指向常量的指针,指针值本身也是一个常量。 常指针类型通常用作函数的形参,以防止在函数体内通过形参修改实参指向的值,以保护实参。 常指针常量定义格式如下: 其中: 左边的const与数据类型相结合,表明数据的值是常量;右边的const用在变量前,表明变量的值是常量。 定义一个常指针常量后,修改指针的值与修改指针指向内容的值都是错误的: char *q; const char * const p="ABCDE"; //定义了一个常指针常量 q=p; //错误,将一个常指针赋给非常指针。 p=q; //错误,试图改变指针常量的值。 const 数据类型* const 指针变量 = 变量名; 或 数据类型 const * const 指针变量 = 变量名;

C++语言程序设计教程 第5章 构造数据类型 5.4 引用类型 引用: 引用是已存在变量的别名,对引用型变量的操作实际上就是对被引用变量的操作。当定义一个引用型变量时,需要用已存在的变量对其初始化。 定义一个引用型变量的语法格式为: 其中: 数据类型应与被引用变量的类型相同; &是引用运算符, 在这里是二元操作符; 变量名为已定义的变量;    例如:     int x;     int & refx=x;//refx是一个引用型变量,它被初始化为对整型变量x的引用 数据类型  & 引用变量名 = 变量名;

C++语言程序设计教程 第5章 构造数据类型 5.4.1 引用类型变量的说明及使用 当定义一个引用变量后,系统并没有为它分配内存空间。refx与被引用变量x具有相同的地址,即refx与x使用的是同一内存空间。对引用变量值的修改就是对被引用变量的修改,反之亦然。 例如: x=3; cout<<refx; //结果为3 refx=5; cout<<x; //结果为5 引用变量的内存图如图5-6。         

C++语言程序设计教程 第5章 构造数据类型 5.4.2 引用与函数 1. 引用作为函数的参数   当引用作为函数的形参,在进行函数调用时,进行实参与形参的结合,其结合过程相当于定义了一个形参对实参的引用。因此,在函数体内,对形参进行运算相当于对实参进行运算。 与指针相比,引用作为函数参数具有两个优点: ① 函数体的实现比指针简单。用指针作为形参,函数体内形参要带着*参加运算;而用引用作为形参,函数体内参加运算的为形参变量。 ② 调用函数语法简单。用指针作为形参,实参需要取变量的地址;而用引用作为形参,与简单传值调用一样,实参为变量。

before swap: x=3 y=5 after swap: x=5 y=3 运行结果: 1 2 3 4 5 6 7 8 9 10 11 C++语言程序设计教程 第5章 构造数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /*************************************** * p5_11.cpp * * 用传引用的方式实现两个数交换 * ****************************************/ #include <iostream> using namespace std; void swap(int &refx,int &refy) { int temp; temp=refx; refx=refy; refy=temp; } void main() int x=3,y=5; cout<<"before swap: x="<<x<<" y="<<y<<endl; swap(x,y); cout<<"after swap: x="<<x<<" y="<<y<<endl; 运行结果: before swap: x=3 y=5 after swap: x=5 y=3

C++语言程序设计教程 第5章 构造数据类型 5.4.2 引用与函数 2. 引用作为函数的返回值     函数返回值类型为引用型,在函数调用时,若接受返回值的是一个引用变量,相当于定义了一个对返回变量的引用。若接受返回值的是一个非引用变量,函数返回变量的值赋给接受变量。 如果函数返回值类型为引用型,则要求返回值为左值。这样,函数调用式可以当作左值。 1 2 3 4 5 6 7 8 9 10 11 12 13 /*********************************** * p5_12.cpp * * 使用引用作为函数的返回值 * ************************************/ #include <iostream> using namespace std; int max1(int a[ ],int n) //求数组a[ ]中元素的最大值 { int t=0; for(int i=0;i<n;i++) if(a[i]>a[t]) t=i; return a[t]+0; }

C++语言程序设计教程 第5章 构造数据类型 5.4.2 引用与函数 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int& max2(int a[],int n) //求数组a[]中元素的最大值 { int t=0; for(int i=0;i<n;i++) if(a[i]>a[t]) t=i; return a[t]; } int& sum(int a[ ],int n) //求数组a[]中元素的和 int s=0; s+=a[i]; return s;

5.4.2 引用与函数 m1=10 m2=10 m3=10 m4=-858993460 -35 运行结果: 28 29 30 31 32 C++语言程序设计教程 第5章 构造数据类型 5.4.2 引用与函数 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 void main() { int a[10]={1,2,3,4,5,6,7,8,9,10}; int m1=max1(a,10); int m2=max2(a,10); int &m3=max2(a,10); int &m4=sum(a,10); cout<<"m1="<<m1<<endl; cout<<"m2="<<m2<<endl; cout<<"m3="<<m3<<endl; cout<<"m4="<<m4<<endl; m3+=10; max2(a,10)-=100; cout<<sum(a,10)<<endl; } int型变量接受函数返回的int &型值 运行结果: m1=10 m2=10 m3=10 m4=-858993460 -35 通过引用变量修改返回变量的

5.4.3 常引用 常引用: 定义一个常引用后,就不能通过常引用更改引用的变量的值。 const 数据类型& 引用变量 = 变量名; 第5章 构造数据类型 5.4.3 常引用 常引用:   如果在定义引用变量时用const修饰,被定义的引用就是常引用。 定义常引用格式如下:   定义一个常引用后,就不能通过常引用更改引用的变量的值。 例如: int i (100); const int & r = i; 当试图使用r=200去更改i的值是错误的,但通过i本身可以改变i的值: 例如: i=200; 此时r的值变成200。 const 数据类型& 引用变量 = 变量名;

C++语言程序设计教程 第5章 构造数据类型 5.4.3 常引用 常引用类型常用作函数的形参类型,把形参定义为常引用类型时,这样在函数体内就不能通过形参改变实参的值,保证了函数调用时实参是“安全”的。这样的形参称为只读形参。 void fun(const int& x, int& y) { x=y; // 错误,x不可修改 y=x; }      注意: 形参为常引用类型时,实参可以是常量、变量表达式;但如果为非常引用类型时,实参必须为左值。对void fun(const int& x, int& y), 调用fun(100,200)是错误的, 调用fun(100,a)是正确的(a为变量)。

C++语言程序设计教程 第5章 构造数据类型 5.5 结构与联合 数组是由类型相同的数据元素构成的。然而,程序中往往需要处理一些由同类型数据元素所构成的数据。 例如,一个学生的基本情况由学号、姓名、性别、出生日期、成绩等数据元素构成,这些数据元素具有不同的数据类型,如果使用独立的不同数据类型的变量来描述这些信息,那么,变量之间的关系不能体现出来。C++也提供了描述不同数据类型的组合体的方法:结构和联合。 注: C++语言是在C语言的基础上改进和发展而来的,所以,有人将早期的C++语言中的结构体称为“带函数的结构体”,即我们即将要学习的C++中的“类(class)”。

5.5.1 结构 结构类型: 结构类型将不同数据类型组合成一个整体类型,是一种“用户自定义构造数据类型”。 定义结构类型的格式如下: C++语言程序设计教程 第5章 构造数据类型 5.5.1 结构 结构类型:    结构类型将不同数据类型组合成一个整体类型,是一种“用户自定义构造数据类型”。 定义结构类型的格式如下: 其中: struct 是关键字,表示定义的是一个结构类型; 结构类型名必须是一个合法的标识符,结构类型名省略时表示定义的是一个 无名的结构体; 结构类型的成员数量不限,各成员构成成员表;数据类型可以是基本数据类 型,也可以是构造数据类型; 结构类型定义的结束符;不能省略。 struct 结构类型名 { 数据类型1 成员名1; 数据类型2 成员名2; … … 数据类型 n 成员名n; } ;

5.5 结构与联合 注意: C++语言程序设计教程 例如,下面定义了一个学生信息的结构类型: 第5章 构造数据类型 5.5 结构与联合 例如,下面定义了一个学生信息的结构类型: enum gender {man, ferman}; struct student { long no,birthday; //学号,生日 char name[22]; //姓名 gender sex; //性别 float score; //成绩 }; 注:结构体及结构体变量的具体用法,有兴趣的同学请参考教材示例程序【例5-13】,在第7章 类与对象中我们将进一步对其进行深入学习。 注意: 结构类型是由多个成员类型组合而成,所以结构类型变量所占内存的大小理论上应为各 个成员所占内存大小之和;为了提高对内存的存取速度,C++分配各个结构成员的内存空 间以字为单位,以保证其地址在字的整数倍处,所以结构成员内存空间存在间隙。 定义了一个结构类型,但并没有定义变量,结构类型中的成员名既不能当作类型名也不 能当作变量使用。 score=95; //错误,成员名不能当作变量 cout<<sizeof(name); //成员名不能当作类型名

C++语言程序设计教程 第5章 构造数据类型 5.5.2 联合 1.联合类型的定义 联合类型是一种与结构类型类似的数据类型,它提供了一种可以将几种不同类型数据存放于同一段内存,对其中各个成员可以按名存取的机制。 联合类型定义的语法形式为: 与结构类型不同,联合类型虽然由多个成员类型组合而成,但联合类型变量的各个成员拥有共同的内存空间,联合类型变量所占内存的大小应为各个成员所占内存大小的最大者。 union 联合类型名 { 数据类型1 成员名1; 数据类型2 成员名2; … … 数据类型 n 成员名n; } ;

5.5.2 联合 C++语言程序设计教程 例如,下面定义了联合类型: union UData { char Ch; 第5章 构造数据类型 5.5.2 联合 在联合类型中,如果其中有构造数据类型,其大小为其中最长的基本数据类型的整数倍。 例如,下面定义了联合类型: union UData { char Ch; short Sint; long Lint; unsigned Uint; float f; double d; char str[10] }; sizeof(UData)的理论值=sizeof(str)=10; sizeof(UData)的实际值=sizeof(double)*2=16;

5.5.2 联合 联合变量在内存中的排列 注: 联合类型及联合类型变量的具体用法,有兴趣的同学请参考教材示例程序【例5-14】。 C++语言程序设计教程 第5章 构造数据类型 5.5.2 联合 联合变量在内存中的排列 当定义联合变量后,联合变量在内存中获得了共同的内存,由于各个成员的数据类型不同,因此长度也不同。各成员在内存排列均从低地址开始,遵循“低地址低字节,高地址高字节”的原则。 例如: UData u; strcpy(u.str,“123456789"); //给成员u.str赋初值 内存图如下: 注: 联合类型及联合类型变量的具体用法,有兴趣的同学请参考教材示例程序【例5-14】。

C++语言程序设计教程 第5章 构造数据类型 本章小结 ◇ 枚举类型实质是一组整型符号常量,其中的每个常量都可进行各种运算,对 一个枚举变量赋值时一定要类型一致。 ◇ 数组是一组同类型变量,可通过数组名加下标存取其中的单个变量。各个数 组元素在内存中顺序排列,数组名表示的是数组的起始地址。可以使用指针 运算符,用指针方式存取数组元素。 ◇ 一个多维数组是以低维数组为元素的数组,多维数组在内存中的排列与一维 数组相同,即从低地址到高地址顺序排列。数组名、高维名表示的是数组的 地址。 ◇ 数组名表示的地址不能是左值,但可以当作函数的形式参数,接受实参传送 的地址。 ◇ 以0作为结尾符的字符数组为字符串,数组大小与字符串长度的关系为: sizeof(s) =strlen(s)+1; ◇ 地址变量称为指针,所有地址变量的长度都是4个字节。 ◇ 地址变量存储数组的地址时(指针指向了数组),可通过指针名以指针方式 存取数组元素,也可将指针名当作数组名以数组方式存取元素。但指针比数 组多了一个存储地址的内存空间,因此指针名可以作为左值。

C++语言程序设计教程 第5章 构造数据类型 本章小结 ◇ 指针指向的动态申请的数组称为动态数组,动态数组所占的内存空间需要在 程序中进行释放。 ◇ 指针可作为函数的形参,接受实参传送的地址。 ◇ 引用是一个已存在变量的别名,它与被引用的对象共有内存单元,因此定义 一个引用型变量时一定要以一个已存在的变量作为初值。引用作为函数的 形参时,可用不赋初值,在函数体内引用变量代替实参进行运算,对形参 的改变反映到实参上。 ◇ 引用型函数返回对象被一个引用型变量接受,引用型变量成为返回对象的别 名;若被一个非引用变量接收,则将返回变量的值赋给接受变量。引用型函 数还可以的是一个左值,接受右值对象的值。 ◇ 常指针、常引用类型通常用作函数的形参,以防止在函数体内通过形参修改 实参指向的值,以保护实参。 ◇ 结构类型是各种已存在与已定义类型的组合体,同类型结构变量之间赋值等 同于每一个成员之间的赋值,其中数组成员的赋值等同于数组的拷贝。 ◇ 联合类型是各种已存在与已定义类型的共同体,联合变量各成员拥有共同的 内存空间。