C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系.

Slides:



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

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
第 6 章 第 6 章 指 针 指 针 1.
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
6.4 字符串与指针 1. 用字符数组存放一个字符串.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
第六节 二维数组和指针 二维数组的地址 对于一维数组: (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++语言程序设计教程 第5章 构造数据类型 第5章 构造数据类型.
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第 十 章 指 针.
第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针
Object-Oriented Programming in C++ 第一章 C++的初步知识
辅导课程六.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
C++语言程序设计 第六章 数组 指针与字符串 清华大学 郑 莉.
C语言程序设计基础 第8章 指针 刘新国.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第二章 Java语言基础.
第8章 指针.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第五章 指针 5.1 指针的概念 5.2 指针与数组 5.3 字符串指针.
9.1 地址、指针和变量 9.2 指针运算 9.3 指针与数组 9.4 函数与指针 9.5 程序综合举例 9.6 上机实训.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第三章 控制语句 第十一组 C++语言程序设计.
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
第二章 Java基本语法 讲师:复凡.
指针 几个概念:  指针也是一种数据类型,具有指针类型的变量,称为指针变量。
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
物件導向程式設計 CH2.
第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 函数(一).
第九节 赋值运算符和赋值表达式.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C程序设计 实验二 数据类型、运算符和表达式 第6讲
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第二章 Java基本语法 讲师:复凡.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
C语言程序设计 第8章 指针.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
第八章 指 针 北京邮电大学出版社.
資料!你家住哪裏? --談指標 綠園.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
<编程达人入门课程> 本节内容 有符号数与无符号数 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
Presentation transcript:

C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系

第六章 指针和引用 6.1 指针的概念 6.2 指针的运算 6.3 指针和函数(6.3.2和6.3.4不要求) 6.4 指针和字符串 6.5 通过指针访问数组(6.5.3和6.5.4不要求) 6.6 指针访问动态内存 6.7 引用概念

6.1 指针的概念

内存空间的访问 变量的两种属性 地址运算符:& 内存空间的访问方式 变量内容 变量地址 int var; var = 5; //表示变量var中存取的内容 &var //表示变量var在内存中的起始地址 内存空间的访问方式 通过变量名访问 通过地址访问

几个概念 指 针 static int *i_pointer=&i; 概念 指针:变量的地址,用于间接访问变量 指针变量:用于存放地址的变量 内存用户数据区 变量 i 变量 j 变量 i_pointer 3 6 2000 2004 3010 概念 指针:变量的地址,用于间接访问变量 指针变量:用于存放地址的变量 目标变量:指针变量存放的地址对应的变量 指 针 声明 例:static int i; static int *i_pointer=&i; 指向整型变量的指针 引用 例1: i=3; 例2: *i_pointer=3; 2000 3 i_pointer *i_pointer i

指针变量的定义和初始化 [存储类型] 数据类型 *指针名=初始地址; int *pa=&a; 语法形式 例: int a; 注意事项 [存储类型] 数据类型 *指针名=初始地址; 例: int a; int *pa=&a; 注意事项 用变量地址作为初值时,该变量必须在指针初始化之前已说明过,且变量类型应与指针类型一致。 可以用一个已赋初值的指针去初始化另一 个指针变量。 char ch1=’Y’, ch2=’A’; char *pch1=&ch1, *pch2=&ch2;

例1 指针的概念 #include <iostream.h> void main() { int a; int *pa=&a; 内存用户数据区 #include <iostream.h> void main() { int a; int *pa=&a; a=10; cout << " a: " << a << endl; cout << “ *pa: " << *pa << endl; cout << “ &a: " << &a << endl; cout << " pa: " << pa << endl; cout << "&pa: " << &pa<< endl; } pa 0012FF78 0012FF7C a 0012FF7C 10

6.2 指针的运算

6.2 指针的运算 表6.1 指针的运算

6.2.1 指针的赋值运算 指针的赋值运算一定是地址的赋值。用来对指针变量赋值的可以是: 同类型变量的地址; 同类型的已经初始化的指针变量; 指针变量名=地址 指针的赋值运算一定是地址的赋值。用来对指针变量赋值的可以是: 同类型变量的地址; 同类型的已经初始化的指针变量; 其他同类型的指针。 此外,也可以用0或者NULL对指针变量赋值。使得变量包含的是“空指针”,即不指向任何的内存物理地址。即,空指针是允许的。

指针的赋值运算 “地址”中存放的数据类型与指针类型必须相符。 必须注意:不同类型的指针是不可以互相赋值的。 在指针赋值时,不存在类型自动转换的机制。 向指针变量赋的值必须是地址常量或变量,不能 是普通整数。但可以赋值为整数0,表示空指针。

例6.1 观察以下指针赋值运算的结果。如果将注释去掉,结果将如何? #include <iostream> using namespace std; void main() {int va1=100,*pva1; float vf1=1.0,*pvf1,*pvf2; int *pva2=NULL; cout<<"value of pva2 is "<<pva2<<endl; pva1=&va1; pvf1=pvf2=&vf1; cout<<pva1<<" "<<&va1<<endl; cout<<pvf1<<" "<<pvf2<<endl; cout<<&pva1<<endl; cout<<&pvf1<<endl; cout<<&pvf2<<endl; //pvf1=pva1; } 内存用户数据区 pva2 0012FF68 pvf2 0012FF6C 0012FF74 pvf1 0012FF70 vf1 1.0 0012FF74 pva1 0012FF78 0012FF7C va1 100 0012FF7C

例 指针的定义、赋值与使用 #include<iostream.h> void main( ) { int *i_pointer; //声明int型指针i_pointer int i; //声明int型数i i_pointer=&i; //取i的地址赋给i_pointer i=10; //int型数赋初值 cout<<"Output int i="<<i<<endl; //输出int型数的值 cout<<"Output int pointer i="<<*i_pointer<<endl; //输出int型指针所指地址的内容 }

6.2.2 间接引用运算 间接引用运算符“*”是一种一元运算符,它和指针变量连用,对指针所指向的内存地址单元进行间接访问。使用的格式是: *指针变量 如果指针变量iptr指向整型变量va,*iptr就是变量va的内容

例6.2 对变量的直接访问和间接访问:写出以下程序运行结果。 #include <iostream> using namespace std; void main() {char ch1='a',*ch; int k1=100; ch=&ch1; //指针ch指向变量ch1 cout<<"*ch="<<*ch<<endl; //间接访问 *ch='B'; cout<<"ch1="<<ch1<<endl; //直接访问 ch1=k1; cout<<"*ch="<<*ch<<endl; //间接访问 } 运行结果: *ch=a ch1=B *ch=d

例6.3 定义指向指针的指针变量。观察对这种指针变量间接访问的结果。 #include <iostream> using namespace std; void main() { int va=100,*pva,**ppva; //ppva是指向指针的指针 int k1=100; pva=&va; cout<<"*pva="<<*pva<<endl; //间接访问结果是整型数 ppva=&pva; cout<<"*ppva="<<*ppva<<endl; //间接访问结果是地址 cout<<"pva="<<pva<<endl; //就是指针pva的内容 } 运行结果: *pva=100 *ppva=0x0012FF7C pva=0x0012FF7C

6.2.2 间接引用运算

指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。 6.2.3 指针的算术运算 指针可以进行的算术运算只有加法和减法。 指针可以和一个整数n做加法或者减法运算。 指针p和整数n相加(相减)的含义是指向当前指向位置p的前方或后方第n个数据的地址。

例6.4 通过指针的间接访问,输出下标为偶数的数组元素的值。 #include <iostream> using namespace std; void main() { int k1[10]={11,24,37,44,58,66,79,86,93,108},*k; k=&k1[0]; for(int i=0;i<10;i=i+2) cout<<"k1["<<i<<"]="<<*(k+i)<< " "; cout<<endl; } 指针常量的下标法k1[i] 指针变量的下标法k[i] 指针常量的指针法*(k1+i) 指针变量的指针法*(k+i)

6.2.3 指针的算术运算 指针和指针的直接加法是没有意义的,也是不允许的。 指针和指针的减法是可以进行的,其意义是求出两个指针之间可以存放几个指定类型的数据。 不允许用一个整数减一个指针。

6.2.4指针的关系运算和逻辑运算 相同类型的指针可以进行各种关系运算。比较两个指针相等还是不相等。 进行指针“大于”、“小于”的比较,只是要判定指针在内存中的相对位置。

6.2.4指针的关系运算和逻辑运算 指向不同数据类型的指针,指针和一般的整数比较是没有意义的,也是不允许的。 惟一可以和指针比较的整数是0。通过指针和0的比较来判定指针本身是不是空指针。即指针可以和零之间进行等于或不等于的关系运算。例如:p==0或p!=0 指针可以进行“逻辑非”运算

6.2.5 void类型指针 void类型的指针就是“无类型”指针。声明的方式如下: void *<指针名>;

void类型指针的使用: 任何其他类型的指针都可以赋值给void指针。必须注意,这样赋值后的void指针的类型仍然是void。

void类型指针的使用: 要通过void类型指针访问内存的数据,必须进行指针类型的强制转换,才可以通过指针间接引用访问内存数据。 某种类型的指针 void指针,进行具体操作后 强制转换

memcpy函数 函数原型: void *memcpy(void *dest,const void *src, size_t count);

复制字符数据,10个字节 复制整型数据,12个字节 例6.5 使用memcpy通用复制函数复制数组。 #include <iostream> using namespace std; #include <string.h> void main() { char src[10]="012345678"; char dest[10]; char* pc=(char*)memcpy(dest,src,10); cout <<pc <<endl; int s1[3]={1,2,3}; int d1[3]; int *pi=(int*)memcpy(d1,s1,12); cout<<*pi<<" "<<*(pi+1)<<" "<<*(pi+2)<<endl; } 复制字符数据,10个字节 复制整型数据,12个字节 运行结果: 012345678 1 2 3

6.2.5 void类型指针 void类型指针还有一个具体的应用:显示字符指针的内容。除了字符指针外,其他指针都可以直接用cout语句来输出地址值。但是,用cout输出字符指针时,则是输出它所指向的字符串。可以将字符指针强制转换为void指针,再用cout语句输出,就可以看到地址值。如: char *pch="Hello C++"; cout<<pch<<endl; cout<<(void*)pch<<endl; int a[3]={1,2,3}; cout << a << endl; cout << *a << endl; char c[]=“hello”; cout << c << endl; cout << *c << endl; cout << c+1 << endl; cout <<(void*) c << endl;

6.3 指针和函数

6.3 指针和函数 在程序设计中,指针有很多应用。其中之一就是用指针作为函数的参数,从而形成了C++函数调用中的另一种调用方式:地址调用。

6.3.1 指针作为函数的参数 用指针作为函数参数,实现地址调用,必须满足以下条件: 函数的形式参数是指针变量; 函数的实参数是内存的地址,具体来说可以是数组名、变量的地址、用变量地址初始化的指针; 形参指针类型和实参地址类型必须相同。

6.3.1 指针作为函数的参数 满足以上条件后,这样的函数调用在使用上有以下的特点: 实参传递给形参的是内存的地址,所以形参指针指向实参变量; 形参指针通过间接引用,直接访问实参变量,包括改变实参变量的值; 函数调用后,可以保留对实参变量的操作结果,如果有多个实参,就可以有多个实参变量在函数调用中得到修改。

变量的地址作为实参数 指针变量作为形式参数 例6.6 编写数据交换的函数。在main中调用这个函数,交换main中定义的变量。 #include<iostream> using namespace std; void Swap(int *a, int *b); void main() { int x(5), y(10); cout<<"主函数变量的值: x="<<x<<" y="<<y<<endl; Swap(&x,&y); cout<<"返回后变量的值: x="<<x<<" y="<<y<<endl; } void Swap(int *a, int *b) { int t; t=*a; *a=*b; *b=t; cout<<"函数中完成了交换:*a="<<*a<<" *b="<<*b<<endl; 变量的地址作为实参数 指针变量作为形式参数 运行结果: 主函数变量的值:x=5 y=10 函数中完成了交换:*a=10 *b=5 返回后变量的值: x=10 y=5

6.3.1 指针作为函数的参数 程序中用变量x和y的地址作实参,传递给指针a和b,如图6.1(a)。通过间接引用*a和*b进行交换,实际上就是x和y进行交换,如图6.1(b)。

例6.7 指针变量指向一个数组。用指针变量作为实参调用一个函数。在函数中指针指向数组的第二个元素。观察函数返回后,实参指针值有无变化。 #include<iostream> using namespace std; void Move(int *a); void main() { int x[5]={10,20,30,40,50}, *px=x; cout<<"调用前的*px="<<*px<<endl; Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; else cout<<"也向前移动,*px变为"<<*px<<endl; } void Move(int *a) { a=a+1; cout<<"函数中完成了指针移动:*a="<<*a<<endl; 指针作为实参数 指针变量作为形式参数 运行结果: 调用前的*px=10 函数中完成了指针移动:*a=20 调用后的px没有变化*px还是10

2009.05.12

6.3.3 传递参数的保护:指针和常量 通过数组名的地址调用,可以改变实参数组的内容。 但是,并不是所有以数组名作为实参的函数调用,都需要改变数组的值。例如,在调用一个求数组最大值的函数时,就不希望数组的值发生变化。希望在函数中能够限制对数组元素的修改。 使用常指针可以达到这个目的。

6.3.3 传递参数的保护:指针和常量 常指针是指向常量的指针(Pointer to Constant data)的习惯说法。就是规定指针所指向的内容不可以通过指针的间接引用来改变。 常指针说明的格式是: const <类型名> *<指针名>; 例如: const int *ptint; 指针ptint的类型是(const int *),也就是指向一个恒定的整型数。

例6.10 常指针示例。观察以下程序的运行。 常指针声明 注释去掉会出现编译错误 #include <iostream> using namespace std; void main() {int ia=10, ib=20; const int *ptint; ptint=&ia; //用ia地址初始化 cout<<*ptint<<endl; ptint=&ib; //改变为ib的地址 ib=ib+100; //ib本身仍然可以改变 //*ptint=100; //语句错误:左值是常量 } 常指针声明 注释去掉会出现编译错误 运行结果: 10 120

6.3.3 传递参数的保护:指针和常量 指针常量(Pointer constant)。 指针本身的内容是个常量,不可以改变。 指针常量声明的格式是: <类型名> *const <指针名>=<初值>; 例如: char ch, *const ptch=&ch; 数组名就是数组的首地址。现在可以说:数组名就是一个指针常量。

语句有错:常量不能当左值 语句有错,地址类型不同 例6.11 指针常量示例。指出以下程序的错误。 #include <iostream> using namespace std; void main() { int a=10, b=100; int *const pa=&a; //pa是指针常量 cout<<*pa<<endl; *pa=20; //指针常量的间接引用是允许的 cout<<a<<endl; pa=&b; const int c=50; // int *const pc=&c; } 语句有错:常量不能当左值 语句有错,地址类型不同 // 错误语句注释掉后运行结果: 10 20

例6.12 用常指针作形参,函数printString可以输出数组的内容,不可以对数组修改。 #include <iostream> using namespace std; void printString( const char * ); void main() { char phrase[] = "C++ is a modern programming language"; cout << "The string is:\n"; printString( phrase ); cout << endl; } // main函数结束 void printString( const char *Ptarray ) { while(*Ptarray) cout << *Ptarray++; } 数组名作实参数 常指针作形式参数 不使用常指针也是可以完成打印。但是没有保护了。

6.4 指针和字符串

6.4.1 字符串处理的两种方式 C++字符串常量是用双引号括起的字符序列,并以字符‘\0’作为结束标志。如 "This is a string"。 字符串常量存放在内存的某个区域,有自己固定的首地址。 如果将字符串常量的首地址看成是指针,这种指针既是常指针,也是指针常量。

6.4.1 字符串处理的两种方式 C++处理字符串有两种方式:数组方式和指针方式。 数组方式是将字符串存入字符数组后,再进行处理。一般可以在声明数组的时候用字符串来初始化: char string_array[]="a nice day!"; 指针方式是用字符串常量来初始化一个字符指针: char *string_pt="a nice day!";

6.4.1 字符串处理的两种方式 常量不能放在等式左边 运行时会出错

复制array1到array3,空间不够,有运行错误 按实际数组大小,复制array1到array3,没有问题 例6.14 strcpy和strncpy的比较。 #include <iostream> #include <string> using namespace std; void main() { int n; char *array1 = "Happy Birthday to You"; char array3[ 15 ]; char array2[ 25 ]; strcpy( array2, array1 ); cout << "The string in array1 is: " << array1 << "\nThe string in array2 is: " << array2 << '\n'; /*strcpy(array3,array1); cout<<array3<<endl; */ n=sizeof(array3); strncpy( array3, array1, n-1 ); // 复制array1n-1个字符到array3 array3[ 14 ] = '\0'; // 添加'\0' 到array3 cout << "The string in array3 is: " << array3 << endl; } 复制array1到array2,没有问题 复制array1到array3,空间不够,有运行错误 不包括提示的运行结果 Happy Birthday to You Happy Birthday 按实际数组大小,复制array1到array3,没有问题

6.5 通过指针访问数组

6.5 通过指针访问数组 指针和数组有天然的联系。因为数组名本身就是地址,也就是某种类型的指针。将指针和数组名联系起来,访问数组就多了一种方法。 虽然一维数组名和二维数组名都是地址,都可以看作是某种指针,但是指针的类型是不同的。因此,通过指针访问一维数组和二维数组的方法是不同的。

6.5.1 通过指针访问一维数组 要通过指针访问一维数组,必须首先声明一个和数组类型相同的指针,并且用数组名来对指针初始化,如: int A[10], *pa=A; 然后,就可以用多种方式访问数组元素: 数组名和下标,如A[0]、A[4]; 指针和下标,如pa[0]、pa[4]; 指针加偏移量的间接引用,如*(pa+4); 数组名加偏移量的间接引用,如*(A+4); 指针自加后的间接引用,如*pa++。

例6.15 求数组内所存放的字符串的长度。 #include <iostream> using namespace std; void main() { char ChArray[]="This is a string.",*ptch; int i,j,k,offset1,offset2; ptch=ChArray; //指针初始化 for(i=0;ChArray[i]!='\0';i++); cout<<"The length of the string is:"<<i<<endl; for(j=0;ptch[j]!='\0';j++); cout<<"The length of the string is:"<<j<<endl; for(offset1=0;*(ChArray+offset1)!='\0';offset1++); cout<<"The length of the string is:"<<offset1<<endl; for(offset2=0;*(ptch+offset2)!='\0';offset2++); cout<<"The length of the string is:"<<offset2<<endl; for(k=0;*ptch++!='\0';k++); cout<<"The length of the string is:"<<k<<endl; } 方式1:数组名和下标 方式2:指针和下标 方式3: 数组名加偏移量的间接引用 方式4:指针加偏移量的间接引用 方式5:指针自加的间接引用 运行结果 都相同

例6.16 求整型数组的平均值,显示数组元素和平均值。 #include <iostream> using namespace std; void main() { int intArray[10]={8,11,23,34,45,56,65,78,86,97},*ptint; int i,num,sum; float average; ptint=intArray; sum=0; num=sizeof(intArray)/sizeof(*intArray); for(i=0;i<num;i++) sum=sum+*ptint++; average=(float)sum/num; cout<<"数组元素是:\n"; cout<<*ptint++<<" "; cout<<"\n平均值是:"<<average<<endl; } 指针初始化 求数组元素的数目 求平均值 指针再次初始化 输出数组元素和它们的平均值 数组元素是: 8 11 23 34 45 56 65 78 86 97 平均值是:50.3

6.5.2 通过指针访问二维数组 二维数组可以看成是一维数组的一维数组。二维数组名虽然也是地址(指针),但是却和一维数组名有不同的类型。 对一维数组A[5],数组名A的地址,就是数组第一个元素A[0]的地址。指针的类型是指向数组元素的指针。A+1就是元素A[1]的地址。

6.5.2 通过指针访问二维数组 对二维数组B[3][4],数组名B的地址,则是其中第一个一维数组B[0]的地址。指针的类型是指向一维数组的指针。B+1就是下一个一维数组B[1]的地址。如图6.3所示。

6.5.2 通过指针访问二维数组 在定义指向一维数组的指针时,还必须指出一维数组的大小。 声明指向一维数组的指针的格式如下: <类型名> (*指针变量名)[一维数组大小]; 例如,和图6.3中两个二维数组所对应的指向一维数组的指针定义如下: char (*ptchb)[4], (*ptchc)[2]; ptchb=B; ptchc=C;

6.5.2 通过指针访问二维数组 对于指向一维数组的指针,具有以下的特征: 二维数组名是指向一维数组的指针,而不是指向数组元素的指针。 指向一维数组指针加1 的结果,是指向下一个一维数组的指针。 指向一维数组的指针的间接引用的结果仍然是地址,即*ptchb仍然是地址。只是地址的类型变了。变为一维数组B[0]第一个元素B[0][0]的地址。 因为*ptchb是数组元素的地址,**ptchb就是数组元素的值。用指向一维数组指针访问二维数组第i行第j列元素的一般公式是*(*(指针名+i)+j)。

例6.17 比较指向一维数组的指针和指向数组元素的指针。 cout<<"不同类型的指针\n"; cout<<"ptshb的地址是: "<<ptshb<<endl; cout<<"*ptshb的地址是: "<<*ptshb<<endl; cout<<"*ptshb+1的地址是: "<<*ptshb+1<<endl; cout<<"B[0][1]的地址是: "<<&B[0][1]<<endl; //cout<<"ptchb和*ptchb相等吗?"<<(ptchb==*ptchb)<<endl; //有语法错误 cout<<"*ptshb+1和&B[0][1]相等吗?"; if(*ptshb+1==&B[0][1]) cout<<"Yes"<<endl; } B的第0行地址 B的第0行第0列 元素的地址 B的第0行第1列 元素的地址 不同类型的指针 ptshb的地址是: 0x0012FF68 *ptshb的地址是: 0x0012FF68 *ptshb+1的地址是: 0x0012FF6A B[0][1]的地址是: 0x0012FF6A *ptshb+1和&B[0][1]相等吗?Yes B的第0行第1列 元素的地址

求数组元素的数目,**dArray就是元素dArray[0][0] 例6.18 用单循环程序,求二维数组元素的平均值。 #include <iostream> using namespace std; void main() {int dArray[3][4]={32,42,12,25,56,76,46,53,76,89,96,82},(*pt)[4]; int sum, j; float average; sum=0; pt=dArray; j=sizeof (dArray)/sizeof( **dArray); for(int i=0;i<j;i++) sum=sum+*(*pt+i); average=(float)sum/j; cout<<"数据的平均值等于:"<<average<<endl; } 指向一维数组指针的初始化 求数组元素的数目,**dArray就是元素dArray[0][0] 数组求和 求平均值 输出平均值 运行结果: 数据的平均值等于57.0833

6.6 指针访问动态内存

6.6 指针访问动态内存 动态内存是在程序执行时才可以申请、使用和释放的内存。也就是存放动态数据的内存区域。存放动态数据的区域称为“堆”,动态内存也称为堆内存。 动态内存不能通过变量名来使用,而只能通过指针来使用。

6.6.1 动态内存的申请和释放 C++中通过运算符new申请动态内存,运算符delete释放动态内存。 运算的结果:如果申请成功,返回指定类型内存的地址;如果申请失败,返回NULL指针。 int *pi; pi=new int(10); 动态内存使用完毕后,要用delete运算来释放。delete运算符使用格式: delete <指针名>;

6.6.2 动态数组空间的申请和释放 申请动态一维数组时,要在new表达式中加上申请数组的大小: 释放动态数组空间都用相同的表达式: 注意:在动态申请数组空间时,不可以对数组进行初始化。 申请一个动态的整型数组: int *piarray; piarray=new int[10]; 也可以申请二维数组的空间: int (*pi_marray)[4]; pi_marray = new int[3][4]; 释放动态数组空间都用相同的表达式: delete []<指针名>;

6.6.3 内存泄漏和指针悬挂 内存泄漏是指动态申请的内存空间,没有正常释放,但是也不能继续使用的情况。如: char *ch1; ch1 = new char('A'); char *ch2 = new char; ch1=ch2; 原来为ch1所申请的存放字符A的空间就不可能再使用了,产生了内存泄漏。

6.6.3 内存泄漏和指针悬挂 让指针指向一个已经释放的空间,即所谓的指针悬挂(Dangling)。如: char *ch1, *ch2; ch1 = new char; ch2 = ch1; *ch2 = 'B'; delete ch1; 指针ch2就是指向了一个已经释放的地址空间,形成指针悬挂。如果还要用delete ch2;语句来释放ch2所指向的空间,就会出现运行错误。

6.7 引用概念

6.7 引用概念 引用(Reference)是C++中新引入的概念,也是C语言中不存在的数据类型。 引用是变量或者其他编程实体(如对象)的别名。因此,引用是不可以单独定义的。如图6.4(a)所示,变量A在内存中有自己的地址,而A的引用B实际上就是变量A,只是A的另外一个名字。

6.7.1 引用的声明和使用 引用是通过运算符&来定义的,定义的格式如下: <类型名> &引用名 = 变量名; 其中的变量名必须是已经定义的,并且和引用的类型必须相同。例如: int someInt; int &refInt = someInt; 必须注意:引用必须在声明的时候就完成初始化,不可以先声明引用,然后再用另一个语句对它初始化。

6.7.1 引用的声明和使用 引用有以下的特点: 引用不能独立存在,它只是其他变量的别名; 引用必须在声明的同时就初始化; 引用一旦定义,引用关系就不可以更改,即B若是A的引用,就不可能是其他变量的引用; 引用的类型就是相关的变量的类型,引用的使用和变量的使用相同。

#include <iostream> using namespace std; void main() 例6.22 引用的使用。观察以下程序的结果。 #include <iostream> using namespace std; void main() { int intA=10; int& refA=intA; cout<<"引用的值和相关变量值相同:refA="<<refA<<endl; refA=5; cout<<"引用的变化,则相关变量也变化:intA="<<intA<<endl; cout<<"引用的地址和相关变量地址相同:intA的地址="<<&intA<<endl; cout<<"引用的地址和相关变量地址相同:refA的地址="<<&refA<<endl; } 引用的值和相关变量值相同:refA=10 引用的变化,则相关变量也变化:intA=5 引用的地址和相关变量地址相同:intA的地址=0x0012FF7C 引用的地址和相关变量地址相同:refA的地址=0x0012FF7C

6.7.1 常引用 如果不希望通过引用来改变相关的变量的值,则可以定义常引用。常引用定义的格式: const <类型名> &引用名=变量名; 例如: int someInt = 10; const int &const_refA = someInt; 例如: const_refA = 50; 此时,const_refA就是常引用。不可以通过const_refA来改变someInt变量的值。 

#include <iostream> using namespace std; void main() 例6.23 指针的引用。 #include <iostream> using namespace std; void main() { int intA=10,intB=20; int *pti=&intA; int * &refi=pti; cout<<"指针的引用可以访问指针所指的变量:*refi="<<*refi<<endl; cout<<"指针变量原来的值:pti="<<pti<<endl; refi=&intB; cout<<"引用的变化,则相关指针也变化:pti="<<pti<<endl; cout<<"指针所指的变量值也发生变化:*pti="<<*pti<<endl; } 指针的引用可以访问指针所指的变量:*refi=10 指针变量原来的值:pti=0x0012FF7C 引用的变化,则相关指针也变化:pti=0x0012FF78 指针所指的变量值也发生变化:*pti=20

例6.24 用引用作为形式参数,通过函数调用,交换两个实参数。 #include <iostream> using namespace std; void swap_1(int &x, int &y) //引用作为形式参数 { int j; j=x; x=y; y=j; } void main() { int a=12345, b=54321; cout<< " 函数调用前:a= " <<a<< " b="<<b<<endl; swap_1(a, b); //变量作为实参数 cout<< " 函数调用后:a= " <<a<< " b="<<b<<endl; 函数调用前:a= 12345 b=54321 函数调用后:a= 54321 b=12345

6.7.2 通过引用传递函数参数 引用使用最多的场合是作为函数的形式参数。 引用作为函数的形式参数具有以下的特点: 引用作为形式参数时,实参数是相同类型的变量; 引用作为形式参数时,在函数中并不产生实参数的副本,形式参数的引用和实参数的变量实际上是同一个实体; 函数对引用的操作,也是对实参变量的操作,函数调用可以改变实参数的值。

6.7.2 通过引用传递函数参数 使用引用作为形式参数还需要注意: 如果实参数需要保护,可以使用“常引用”作为形式参数; 用引用作形参和用变量作形参是有区别的,但是,对于这两种情况,实参数可能相同。例如,函数swap(int a, int b)和swap(int &a, int&b)看起来是两个可以区分的重载函数。但是,都可以用整型变量x和y来调用:swap(x, y),因此,实际上是不可区分的,函数swap(int a, int b)和swap(int &a, int&b)不是可以区分的重载函数。

例6.25 引用作为函数返回值。 返回引用的值 返回引用的地址 程序执行后显示: 20 输出1:pb=20 30 输出2:pc=30 40 #include <iostream> using namespace std; int &fun(int &pf); void main() {int pa=10,pb; cout<<"输出1:pb="<<(pb=fun(pa))<<endl; int &pc=fun(pa); cout<<"输出2:pc="<<pc<<endl; cout<<"输出3:"<<++fun(pa)<<endl; pc=100; cout<<"pc="<<pc<<","<<"pa="<<pa<<endl; pb=200; cout<<"pb="<<pb<<","<<"pa="<<pa<<endl; } int &fun(int &pf) {pf=pf+10; cout<<pf<<endl; return pf; 返回引用的值 返回引用的地址 程序执行后显示: 20 输出1:pb=20 30 输出2:pc=30 40 输出3:41 pc=100,pa=100 pb=200,pa=100

6.7.3 用引用作为函数的返回值 返回引用有以下需要注意的地方: 返回引用需要在函数的返回值类型中加以说明,形式为: <类型名> &函数名(形式参数表) 返回引用的返回语句就是:return 变量名; 返回引用实际是返回地址。在使用上,或者直接使用这个地址;或者使用这个地址单元的数据。 返回的引用可以作为左值继续操作,而返回的变量值是不可以继续运算的。这是返回引用和返回变量值在使用上的主要区别。

总结 指针变量的特点是可变性,即一个指针变量内的地址是可变的。所以,通过一个指针变量,就可以访问一个数组。而引用的特点是不变性,一个变量的引用就只能和这个变量联系在一起。彼此随着对方的变化而变化。本章还介绍了函数调用的另一种方式:地址调用。具体又分为指针调用和引用调用。动态内存的使用也是本章的重点之一。

作业及实验: 第6章习题: 1,2,4,6,7,11

习题6.4 #include <iostream> #include<string> using namespace std ; void main() { char *s1="String01"; char *s2="String02"; char s3[]="String03"; char s4[]="String04"; //strcpy(s1,s2); cout<<s1<<endl; strcpy(s3,s4); cout<<s3<<endl; strcpy(s3,s2); //strcpy(s1,s4); } 习题6.4

习题6.6 #include <iostream> #include<string> using namespace std ; void main() { char *pch; pch=new char; //strcpy(pch,"Book"); cout<<pch<<endl; delete pch; }

习题6.11 #include<iostream> using namespace std; void Move(int *&a); void main() { int x[5]={10,20,30,40,50}, *px=x; cout<<"调用前的*px="<<*px<<endl; Move(px); cout<<"调用后的px"; if(px==x)cout<<"没有变化,*px还是"<<*px<<endl; else cout<<"也向前移动,*px变为"<<*px<<endl; } void Move(int *&a) { a=a+1; cout<<"函数中完成了指针移动:*a="<<*a<<endl;