1.7.1 指针和指针变量 int i = 5; int *p = &i; C++中定义一个指针变量可按下列格式:

Slides:



Advertisements
Similar presentations
第九章 指针 西安工程大学.
Advertisements

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
第8章 指针 21世纪高等学校精品规划教材.
第 6 章 第 6 章 指 针 指 针 1.
Chap 11 指针进阶 11.1 奥运五环色 11.2 字符定位 11.3 用链表构建学生信息库.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
6.4 字符串与指针 1. 用字符数组存放一个字符串.
第六节 二维数组和指针 二维数组的地址 对于一维数组: (1)数组名array表示数组的首地址, 即array[0]的地址;
8.1 指针的概念 8.2 指针变量 8.3 指针变量的基础类型 8.4 指针的运算 8.5 指针与一维数组 8.6 指针应用实例
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
Using C++ The Weird Way Something about c++11 & OOP tricks
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第 十 章 指 针.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
走进编程 程序的顺序结构(二).
辅导课程六.
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第二章 Java语言基础.
第8章 指针.
第六章 指针 指针的概念 指针的运算 指向变量的指针 指向数组的指针 指向函数的指针 二级指针 主讲:李祥 时间:2015年10月.
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第七章 操作符重载 胡昊 南京大学计算机系软件所.
C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系.
第一章 函数与极限.
9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
C语言复习3----结构体.
1.3 C语言的语句和关键字 一、C语言的语句 与其它高级语言一样,C语言也是利用函数体中的可执行 语句,向计算机系统发出操作命令。按照语句功能或构成的不 同,可将C语言的语句分为五类。 goto, return.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
顺序表的删除.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
指针 几个概念:  指针也是一种数据类型,具有指针类型的变量,称为指针变量。
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第6讲 指针与引用 6.1 指针 6.2 引用.
<编程达人入门课程> 本节内容 内存的使用 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
C语言程序设计 第一章 数据类型, 运算符与表达式 第二章 顺序程序设计 第三章 选择结构程序设计 第四章 循环控制 第五章 数组.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
第4章 Excel电子表格制作软件 4.4 函数(一).
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第九节 赋值运算符和赋值表达式.
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
3.16 枚举算法及其程序实现 ——数组的作用.
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
ASP.NET实用教程 清华大学出版社 第4章 C#编程语言 教学目标 教学重点 教学过程 2019年5月5日.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
学习任务三 偏导数 结合一元函数的导数学习二元函数的偏导数是非常有用的. 要求了解二元函数的偏导数的定义, 掌握二元函数偏导数的计算.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
本节内容 结构体.
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
C语言程序设计 第9章 结构体.
C语言程序设计 第8章 指针.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
基本知识 数据类型、变量、常量、运算符.
第八章 指 针 北京邮电大学出版社.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
第六章 复合数据类型 指针的声明与使用 数组的声明与使用 指针与数组的相互引用 字符串及相关库函数 new与delete
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
Presentation transcript:

1.7.1 指针和指针变量 int i = 5; int *p = &i; C++中定义一个指针变量可按下列格式: <数据类型> *<指针变量名1>[,*<指针变量名2>,...]; int *pInt1, *pInt2; // pInt1,pInt2是指向整型变量的指针 float *pFloat; // pFloat是一个指向实型变量的指针 char *pChar; // pChar是一个指向字符型变量的指针,它通常用来处理字符串 需要说明的是,绝大多数情况下,都可以将指针变量简称为“指针”。

1.7.2 &和*运算符 C++中有两个专门用于指针的运算符: &(取地址运算符)、*(取值运算符) 运算符“&”只能对变量操作,作用是取该变量的地址。运算符“*” 是用来取指针或 地址所指内存单元中存储的内容。 例如: int a = 3; // 整型变量,初值为3 int *p = &a; // 指向整型变量的指针,其值等于a的地址 int b = *p; // 将指针所指的地址中的内容赋值给b,值为3。 上述赋值是在指针变量定义时进行的;当然,也可以在程序中进行赋值。例如: int *pi; // 指向整型变量的指针 pi = p; // 将指针p的地址赋给指针pi,使得它们都是指向a的指针, // 它等价于pi = &a; 注意在pi前没有*。

1.7.3 指针运算 1. 指针的算术运算 在实际应用中,指针的算术运算主要是对指针加上或减去一个整数。指 针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例 如,若有: int *ptr; 指针变量ptr加上整数n后,即ptr = ptr + n。 编译器会把指针ptr的值加上sizeof(int)*n,在32位机器中,sizeof(int)等 于4。由于地址是以字节为单位的,故ptr所指向的存储单元向高地址方 向移动了sizeof(int)*n字节。这里的int是指针变量ptr的数据类型,若定义 成float型,则ptr = ptr + n是使ptr向高地址方向移动了sizeof(float)*n字节。

1.7.3 指针运算 因此, <指针变量> = <指针变量> + n 它是使指针变量所指向的存储单元向高地址方向移动了sizeof(指针变量 类型)*n个字节。类似的: <指针变量> = <指针变量> - n 它是使指针变量所指向的存储单元向低地址方向移动了sizeof(指针变量 类型)*n个字节。 当n为1时,指针变量的上述加减运算就是指针变量的自增(++)、自减(--) 运算。

1.7.3 指针运算 2. 指针的关系运算 两个指针变量的关系运算是根据两个指针变量值的大小来进行比较。在 实际应用中,通常是比较两个指针反映地址的前后关系或判断指针变量 的值是否为0。例如: [例Ex_PointerOp] 将字符数组a中的n个字符按相反顺序存放 #include <iostream.h> void main() { char a[] = "Chinese"; char *p1 = a, *p2 = a, temp; while (*p2!='\0') p2++; p2--; // 将p2指向a的最后一个元素 while (p1<p2) temp = *p1; *p1 = *p2; *p2 = temp; // 交换内容 p1++; p2--; cout<<a<<endl; // 输出结果 }

1.7.4 指针和数组 数组中所有元素都是依次存储在内存单元中的,每个元素都 有相应的地址。C++又规定数组名代表数组中下标为0的元 素的地址,即数组的首地址。注意:数组名表示的首地址是 一个地址(指针)常量。例如,当有下列的数组定义时: int a[5]; 则a所表示的地址就是元素a[0]的地址,a是一个地址(指针) 常量,a++是不合法的。需要说明的是,下标运算符[]具有下 列含义: a[i] = *(a+i) 这是因为a是一个地址(指针),a+i表示a[i]的地址值,它等价 于&a[i],因而a[i]=*(a+i)。

1.7.4 指针和数组 在指针操作中,若定义了下列指针: int *pi; 则 pi = a; // 等价于pi = &a[0]; 通过指针能引用数组元素。例如: *(pi+1) = 1; 和 a[1] = 1; 是等价的。由于指针变量和数组的数组名在本质上是一样,都是反映地 址值。因此指向数组的指针变量实际上也可像数组变量那样使用下标, 而数组变量又可像指针变量那样使用指针。例如:pi[i]与*(pi+i)及a[i]是等 价的,*(a+i)与*(pi+i) 是等价的。

1.7.4 指针和数组 [例Ex_SumUsePointer] 用指针运算来计算数组元素的和 #include <iostream.h> void main() { int a[6]={1, 2, 3, 4, 5, 6}; int *p = a; // 用数组名a给指针初始化 int sum = 0; for (int i=0; i<6; i++) sum += *p; p++; } cout<<sum<<"\n"; 运行结果为21。

1.7.4 指针和数组 用指针运算时,要注意分析。例如: [例Ex_ArrayAndPointer] 分析下列程序的输出结果 #include <iostream.h> void main() { int a[ ]={5, 8, 7, 6, 2, 7, 3}; int y, *p = &a[1]; y = (*--p)++; cout<<y<<"\n"; } 程序中,最难理解的语句是“y = (*--p)++;”,由于取值运算符“*”和前缀自减运算符 “--”处于相同的优先级,但它们的结合方向是自右至左,因此先运算--p,也就是 a[0]的地址,(*--p)是元素a[0]的值,为5;再运算“y = (*--p)++;”,它相当于“y = (*--p); (*--p) = (*--p)+1;”,故最终结果为5。

1.7.4 指针和数组 [例Ex_MultiArrayAndPointer] 分析下列程序的输出结果 #include <iostream.h> void main() { int a[3][3]={1, 2, 3, 4, 5, 6, 7, 8, 9}; int y = 0; for (int i=0; i<3; i++) for (int j=0; j<3; j++) y += (*(a+i))[j]; cout<<y<<"\n"; } 程序中,“y += (*(a+i))[j];”是理解本程序的关键。事实上,*(a+i)就是a[i],因而 (*(a+i))[j]就是a[i][j]。这里的“y += (*(a+i))[j];”语句就是求数组a中各个元素之和, 结果是45。

1.7.5 指针和结构体 指针也可指向结构体类型变量,例如: [例Ex_StructPointer] 指针在结构体中的应用 #include <iostream.h> #include <string.h> struct PERSON { int age; // 年龄 char sex; // 性别 float weight; // 体重 char name[25]; // 姓名 }; void main() struct PERSON one; struct PERSON *p; // 指向PERSON类型的指针变量 p = &one; p->age = 32; p->sex = ’M’; p->weight = (float)80.2; strcpy(p->name, "LiMing"); cout<<”姓名:”<<(*p).name<<endl; cout<<”姓别:”<<(*p).sex<<endl; cout<<”年龄:”<<(*p).age<<endl; cout<<”体重(Kg):”<<(*p).weight<<endl; }

1.7.5 指针和结构体 运行结果如下: 程序中,“->”称为指向运算符,它的左边必须是一个指针变量,它等效于指针变量所指向的结构体类型变量,如p->name和(*p).name是等价的,都是引用结构PERSON类型变量one中的成员name,由于成员运算符“.”优先于“*”运算符,所以(*p).name中的*p两侧括号不能省 若将结构体变量看成一个整体,那么指向结构体变量数组的指针操作和指向数组的指针操作是一样的。例如若有: PERSON many[10], *pp; pp = many; // 等价于pp=&many[0]; 则pp+i与many+i是等价的,(pp+i)->name与many[i].name是等价的,等等。

1.7.6 多级指针 如果指针变量中存储的是另一个指针变量的地址,或指向指针类型的指 针变量,称为多级指针。 [例Ex_MultiPointer] 使用多级指针 #include <iostream.h> void main() { int num = 4; int *pnum = &num; int **ppnum = &pnum; cout<<**ppnum<<endl; **ppnum = 8; cout<<”num = ”<<num<<” = ”<<**ppnum<<endl; } 运行结果是: 程序中,ppnum是一个多级 (二级)指针变量,它是指向 pnum指针变量的指针,而 pnum是指向num整型变量的 指针,注意它们之间的层次 关系: *ppnum与pnum等价, *pnum与num等价。 (2) &num与pnum等价, &pnum与ppnum等价。 (3) **ppnum与num等价, &(&num) 与ppnum等价。

1.7.7 指针和函数 指针既可以作为函数的形参和实参,又可以作为返回值,应用非常广 泛。 1. 指针作为函数的参数 函数的参数可以是C++语言中任意合法变量,自然,也可以是一个指 针。 如果函数的某个参数是指针,对这一个函数的调用就是按地址传递的函 数调用,简称传址调用。由于函数形参指针和实参指针指向同一个地 址,因此形参内容的改变必将影响实参。在实际应用中,函数可以通过 指针类型的参数带回一个或多个值。

1.7.7 指针和函数 [例Ex_SwapUsePointer] 指针作为函数参数的调用方式 #include <iostream.h> void swap(int *x, int *y); void main() { int a = 7, b = 11; swap(&a, &b); cout<<“a = ”<<a<< “, b = ”<<b<<"\n"; } void swap(int *x, int *y) int temp; temp = *x; *x = *y; *y = temp; cout<<"x = "<<*x<<", y = "<<*y<<"\n"; 结果是:

1.7.7 指针和函数 传递指针的函数调用实现过程如下: (1) 函数声明中指明指针参数,即示例中的“void swap(int *x, int *y);”; (2) 函数调用的实参中指明变量的地址,即示例中的“swap(&a, &b);”; (3) 函数定义中对形参进行间接访问。对*x和*y的操作,实际上就是访问函数的实参变量a和b,通过局部变量temp的过渡,使变量a和b的值被修改。

1.7.7 指针和函数 2. 返回指针的函数 函数可以返回一个指针,该指针指向一个已定义的任一类型的数据。定义返回指 针的函数格式如下: <函数类型> * <函数名>( <形式参数表> ){ <函数体> } 它与一般函数定义基本相同,只不过在函数名前面增加了一个“*”号,用来指明函 数返回的是一个指针,该指针所指向的数据类型由函数类型决定。例如:

1.7.7 指针和函数 [例Ex_PointerRreturn] 返回指针的函数:用来将一个字符串逆序输出 #include <iostream.h> char* flip(char *str) { char *p1, *p2, ch; p1 = p2 = str; while (*p2 != '\0') p2++; p2-- ; while (p1<p2) ch = *p2; *p2 = *p1; *p1 = ch; // 交换字符 p1++; p2--; } return str; void main() char str[] = "ABCDEFGH"; cout<<flip(str)<<"\n"; 运行结果为:

1.7.7 指针和函数 3. 指向函数的指针 同变量相似,每一个函数都有地址。指向函数地址的指针称 为“函数指针”。函数指针指向内存空间中的某个函数,通过 函数指针可以调用相应的函数。 函数指针的定义如下: <函数类型>( * <指针名>)( <参数表> ); 例如: int (*func)(char a, char b); 就是定义的一个函数指针。int为函数的返回类型,*表示后 面的func是一个指针变量名。该函数具有两个字符型参数a 和b。

1.7.7 指针和函数 需要说明的是,由于()的优先级大于*,所以下面是返回指针的函数定 义而不是函数指针定义: int *func(char a, char b); 一旦定义了函数指针变量,就可以给它赋值。由于函数名表示该函数 的入口地址,因此可以将函数名赋给指向函数的指针变量。但一般来 说,赋给函数指针变量的函数的返回值类型与参数个数、顺序要 和函数指针变量相同。

1.7.7 指针和函数 例如 int fn1(char a, char b); int *fn2(char a, char b); int fn3(int n); int (*fp1)(char x, char y); int (*fp2)(int x); fp1 = fn1 ; // 正确,fn1函数与指针fp1指向的函数一致 fp1 = fn2 ; // 错误,fn2函数的返回值类型与指针fp1指向的函数不一致 fp2 = fn3 ; // 正确,fn3函数与指针fp2指向的函数一致 fp2 = fp1 ; // 错误,两个指针指向的函数不一致 fp2 = fn3(5) ; // 错误,函数赋给函数指针时,不能加括号 函数指针变量赋值后,就可以使用指针来调用函数了。调用函数的格式如下: ( * <指针名>)( <实数表> ); 或 <指针名>( <实数表> ); 例如: (*fp2)(5); 或 fp2(5) ;

1.7.7 指针和函数 [例Ex_FuncPointer1] 函数指针的使用 #include <iostream.h> double add(double x, double y) { return (x+y); } double mul(double x, double y) { return (x*y); } void main() { double (*func)(double,double); // 定义一个函数指针变量 double a, b; char op; cout<<"输入两个实数及操作方式,'+'表示'加', '*'表示乘: "; cin>>a>>b>>op; if (op == '+') func = add; // 将函数名赋给指针 else func = mul; cout<<a<<op<<b<<"="<<func(a,b)<<endl; // 函数调用 } 结果如下:

1.7.7 指针和函数 函数指针变量可用作函数的参数。例如: [例Ex_FuncPointer2] 函数指针变量可用作函数的参数 #include <iostream.h> double add(double x, double y) { return (x+y); } double mul(double x, double y) { return (x*y); } void op(double(*func)(double,double), double x, double y) { cout<<"x = "<<x<<", y = "<<y<<", result = "<<func(x,y)<<"\n"; } void main() cout<<"使用加法函数: "; op(add, 3, 7); cout<<"使用乘法函数: "; op(mul, 3, 7); 运行结果为:

1.7.7 指针和函数 与一般变量指针数组一样,函数指针也可构成指针数组。例如: [例Ex_FuncPointerArray] 函数指针数组的使用 #include <iostream.h> void add(double x, double y) { cout<<x<<" + "<<y<<" = "<<x+y<<"\n"; } void sub(double x, double y) cout<<x<<" - "<<y<<" = "<<x-y<<"\n"; void mul(double x, double y) cout<<x<<" * "<<y<<" = "<<x*y<<"\n"; void div(double x, double y) cout<<x<<" / "<<y<<" = "<<x/y<<"\n";

void (. func[4])(double, double) = {add, sub, mul, div}; void main() { double x = 3, y = 7; char op; do cout <<"+ ------- 相加\n" <<"- ------- 相减\n" <<"* ------- 相乘\n" <<"/ ------- 相除\n" <<"0 ------- 退出\n"; cin>>op; switch(op) case '+': func[0](x, y); break; case '-': func[1](x, y); break; case '*': func[2](x, y); break; case '/': func[3](x, y); break; case '0': return; } }while(1);

1.7.8带参数的主函数main() 到目前为止,我们所接触到的main()函数都是不带参数的。但在实际应用中,程 序有时需要从命令行输入参数。例如: c:\>copy file1 file2 这是一个常用的DOS命令。当它运行时,操作系统将命令行参数以字符串的形式 传递给main()。为了能使程序处理这些参数,需要main()带有参数,其最常用的 格式是: 数据类型 main(int argc, char * argv[]) 其中,第一个int型参数用来存放命令行参数的个数,实际上argc所存放的数值比 命令行参数的个数多1,即将命令字(或称为可执行文件名,如copy)也计算在 内。第二个参数argv是一个一维的指针数组,用来存放命令行中各个参数和命令 字的字符串,且规定: argv[0]存放命令字 argv[1]存放命令行中第一个参数 argv[2]存放命令行中第二个参数 argv[3]存放命令行中第三个参数 … 这里,argc的值和argv[]各元素的值都是系统自动赋值的。

1.7.8带参数的主函数main() [例Ex_Main] 处理命令行参数 #include <iostream.h> void main(int argc, char *argv[]) { cout<<"这个程序的程序名是:"<<argv[0]<<"\n"; if (argc<=1) cout<<"没有参数!"; else int nCount = 1; while(nCount < argc) cout<<"第"<<nCount<<"个参数是:"<<argv[nCount]<<"\n"; nCount++; } 程序编译连接后,将Ex_Main.exe复制到C盘,然后切换到DOS命令提示符进行测试。 C:\>Ex_Main ab cd E F 运行结果为:

1.7.9 new和delete 在C++中,使用运算符new和delete能有效地、直接地进行动态内存的分配和释 放。 则返回0。例如: double *p; p = new double; *p = 30.4; // 将值存在在开辟的单元中 系统自动根据double类型的空间大小开辟一个内存单元,并将地址放在指针p 中。当然,也可在开辟内存单元时,对单元里的值进行初始化。例如上述代码可 写成: p = new double(30.4); 运算符delete操作是释放new请求到的内存。例如: delete p; 它的作用是将p指针的内存单元释放,指针变量p仍然有效,它可以重新指向另一 个内存单元。

1.7.9 new和delete 需要注意的是: (1) new和delete须配对使用。也就是说,用new为指针分配内存,当使用结束之后,一定要用delete来释放已分配的内存空间。 (2) 运算符delete必须用于先前new分配的有效指针。如果使用了未定义的其它任何类型的指针,就会带来严重问题,如系统崩溃等。 (3) new可以为数组分配内存,但当释放时,也可告诉delete数组有多少个元素。 例如: int *p; p = new int[10]; // 分配整型数组的内存,数组中有10元素 if ( !p ) { cout<<”内存分配失败!”; exit(1); // 中断程序执行 } for (int i=0; i<10; i++) p[i] = i; // 给数组赋值 ... delete [10]p; // 告诉delete数组有多少个元素,或delete [ ]p;

1.7.10 引用 C++中提供了一个与指针密切相关的特殊数据类型——“引用”。引用是一个变量 的别名,定义引用类型变量,实质上是给一个已定义的变量起一个别名,系统 不会为引用类型变量分配内存空间,只是使引用类型变量与其相关联的变量使 用同一个内存空间。 定义引用类型变量的一般格式为: <数据类型> &<引用名> = <变量名> 或 <数据类型> &<引用名> ( <变量名>) 其中,变量名必须是一个已定义过的变量。例如: int a = 3; int &ra = a; 这样,ra就是一个引用,它是变量a的别名。所有对这个引用ra的操作,实质上 就是对被引用对象a的操作。例如: ra = ra +2; 实质上是a加2,a的结果为5。但是如果给引用赋一个新值,结果会怎样的呢?

1.7.10 引用 [例Ex_Reference] 给引用重新赋值 #include <iostream.h> void main() { int a; int &ra = a; a = 5; cout<<"a = "<<a<<"\n"; cout<<"ra = "<<ra<<"\n"; cout<<"a的地址是:"<<&a<<"\n"; cout<<"ra的地址是:"<<&ra<<"\n"; int b = 8; ra = b; cout<<"b = "<<b<<"\n"; cout<<"b的地址是:"<<&b<<"\n"; }

1.7.10 引用 运行结果: 程序中,引用ra被重新赋值为变量b。但从运行结果可以看出,ra 与a的地址仍然相同,只不过它们的值都等于b的值。

1.7.10 引用 从这个例子可以看出,引用与指针的最大区别是:指针是一个变量,可以把它再赋值成指向别处的地址,而引用一旦初始化后,其地址不会再改变。 当然,在使用引用时,还需要注意的是: (1) 定义引用类型变量时,必须将其初始化。而且引用变量类型必须与为它初始化的变量类型相同。例如: float fVal; int &rfVal = fVal; // 错误:类型不同 (2) 当引用类型变量的初始化值是常数的,则必须将该引用定义成const类型。例如: const int &ref = 2; // const类型的引用

1.7.10 引用 (3) 不能引用一个数组,这是因为数组是某个数据类型元素的集合,数组名表示 该元素集合空间的起始地址,它自己不是一个真正的数据类型。例如: int a[10]; int &ra = a; // 错误:不能建立数组的引用 (4) 可以引用一个结构体。例如 struct PERSON { int age; // 年龄 char name[25]; // 姓名 }; PERSON one; PERSON &rone = one; (5) 引用本身不是一种数据类型,所以没有引用的引用,也没有引用的指针。例 如: int a; int &ra = a; int &rra = ra; // 正确,变量a的另一个引用 int& *p = &ra; // 错误:企图定义一个引用的指针

1.7.11函数的引用传递 如果以引用作为参数,则既可以实现指针所带来的功能,而且简便自然。 一个函数能使用引用传递的方式是在函数定义时将形参前加上引用运算符“&”。例如: [例Ex_SwapUseReference] 引用作为函数参数的调用方式 #include <iostream.h> void swap(int &x, int &y); void main() { int a(7), b(11); swap(a, b); cout<<“a = ”<<a<< “, b = ”<<b<<"\n"; } void swap(int &x, int &y) int temp; temp = x; x = y; y = temp; cout<<"x = "<<x<<", y = "<<y<<"\n"; 结果是:

1.7.11函数的引用传递 需要注意的是,绝对不要返回不在作用域内的变量的引用,因为一旦变量退出作用域,对它的引用也没有意义了。 [例Ex_RefReturn] 返回引用的函数的使用 #include <iostream.h> double area; double &CalArea(double r) { area = 3.141593 * r * r; return area; } void main() double c = CalArea(5.0); double &d = CalArea(10.0); cout<<c<<"\n"; cout<<d<<"\n"; 运行结果为: 需要注意的是,绝对不要返回不在作用域内的变量的引用,因为一旦变量退出作用域,对它的引用也没有意义了。

1.7.12简单链表 1. 链表概述 链表是一种动态数据结构,它的特点是用一组任意的存储单元(可以是连 续的,也可以是不连续的)存放数据元素。一个简单的链表具有如图1.6所 示的结构形式。 a1 a2 a3 a4 ^ a5 头指针 head 图1.6 简单链表的结构形式

1.7.12简单链表 在C++中,实现一个单链表结构比较简单,例如: struct NODE { int data; NODE *next; }; 它是单链表结构的最简单形式,它用到了前面的结构体类型。其中, *next是指针域,用来指向该结点的下一个结点,data是一个整型变量, 用来存放结点中的数据;当然,data可以是任何数据类型,包括结构类 型。然后,定义所需要的指针变量: NODE *head; // 定义头指针 NODE *p; // 定义一个结点 这时,(*p).data或p->data用来指向结点p的数据域,(*p).next或p->next 用来指向结点p的下一个结点。

1.7.12简单链表 2. 链表的输出 由于链表中的各个结点是由指针链接在一起的,因此只要知道链表的头指针(即 head),那么就可以定义一个指针p,先指向第一个结点,输出p所指向的结点数 据,然后根据结点p找到下一个结点,再输出,直到链表的最后一个结点(指针为 空)。程序如下: void OutputList(NODE *head) { NODE *current = head; while (current != NULL) cout<<current->data<<"\t"; current=current->next; } cout<<"\n";

1.7.12简单链表 3. 链表的插入 如果要在链表中的结点a之前插入新结点b,则需要考虑下列几种情况: (1) 插入前链表是一个空表,这时插入新结点b后,链表如图1.7(a)所示,实线表示插入前的指针,虚线 为插入后的指针(下同)。 (2) 若a是链表的第一结点,则插入后,结点b为第一个结点,如图1.7(b)所示。 (3) 若链表中存在a,且不是第一个结点,则首先要找出a的上一个结点ak,令ak的指针域指向b,令b的 指针域指向a,即可完成插入,如图1.7(c)所示。 (4) 若链表中不存在a,则先找到链表的最后一个结点an,并令an的指针域指向结点b,而b结点的指针 域为空。如图1.7(d)所示。 图1.7 链表的插入  b head ( a ) a ... ( b ) ( c ) ak ( d ) an

1.7.12简单链表 程序如下(设aData是结点a中的数据,bData是结点b中的数据): void InsertList(NODE **head, int aData, int bData) { NODE *p, *a, *b; b = (NODE *)new(NODE); // 分配一个新结点b b->data = bData; p = *head; if (p == NULL) // 若链表是空,符合情况(1) *head = b; // 将b作为第一个结点 b->next = NULL; } else if (p->data == aData) { // 若a是第一个结点,符合情况(2) b->next = p; *head = b; } else while (p->data!=aData && p->next!=NULL) { // 查找结点a a = p; p = p->next; } if (p->data==aData) { // 有结点a,符合情况(3) a->next = b; b->next = p; { // 没有结点a,符合情况(4) p->next = b; b->next = NULL;

1.7.12简单链表 4.链表的删除 (1) 如果要在链表中删除结点a,并释放被删除的结点所占的存储空间,则需要考虑下列几 种情况: (2) 若要删除的结点a是第一个结点,则把head指向a的下一个结点。如图1.8(a)所示。 (3) 若要删除的结点a存在于链表中,但不是第一个结点,则应使a的上一个结点ak的指针 域指向a的下一个结点ak+1。如图1.8(b)所示。 (4) 空表或要删除的结点a不存在,则不作任何改变。 图1.8 链表的删除 ( a ) head a a1 ... ( b ) ak+1 ak

1.7.12简单链表 4.链表的删除 程序如下: void DeleteList(NODE **head, int aData) // 设aData是结点a中的数据 { NODE *p, *a; p = *head; if (p==NULL) return; // 若是空表,符合情况(3) if (p->data==aData) { // 若a是第一个结点,符合情况(1) *head = p->next; delete p; }else while (p->data!=aData&&p->next!=NULL) { // 查找结点a a = p; p = p->next; } { // 有结点a,,符合情况(2) a->next = p->next; delete p;

1.7.12简单链表 以上的OutputList、InsertList和DeleteList是操作链表的三个函数,使用它们可形成完整的程序: [例Ex_SimpleList] 使用简单链表 #include <iostream.h> struct NODE { int data; NODE *next; }; NODE *head = NULL; int data[6] = {25,41,17,98,5,67}; void InsertList(NODE **head, int aData, int bData); void DeleteList(NODE **head, int aData); void OutputList(NODE *head); void main() for (int i=0; i<6; i++) InsertList(&head, data[0], data[i]); OutputList(head); DeleteList(&head, 98); DeleteList(&head, 41); }

1.7.12简单链表 [例Ex_SimpleList] 使用简单链表 结果如下: 代码中,若将在指定结点之前插入结点的函数InsertList改为在链表最后添加结点,设函数名为AppendList,则这样的函数应如何实现?