第10章 让人迷惑的指针
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
10.2 指针变量的声明和使用
• 指针和变量,函数一样,也有类型,也可以被声 明、定义和使用。下面笔者将逐一对其进行讲解。
10.2.1 指针变量的类型
• 一个变量的地址称为该变量的“指针”。例如:“房间2202”是 李四“变量”的指针。如果有一个变量专门用来存放另一变量的 地址(即指针)的,则称其为“指针变量”。 如果要对指针变量进行定义,就必须对指针变量的数据类型进行 了解。指针变量的数据类型是由其所指向的变量的类型决定,而 不是指针本身数据值的类型决定。这与一般的变量的数据类型不 同。 因为任何指针本身数据值的类型都是unsigned long型的。由于指 针的类型是由其所指向的变量的类型决定的,而所指向的变量的 类型又各不相同,所以指针的类型也是各不相同的。这就好比, 前面说的宾馆中的房间号(指针),都是4个数字,但其所代表的 房间(指向的变量)大小就不一定是相同的。 指针不仅可以指向各种类型的变量,还可以指向数组、数据元素、 函数、文件,还可以指向指针。所以指针分为很多种,包括:指 向数组的指针、指向函数的指针、指向文件的指针和指向指针的 指针等。
10.3 指针变量的操作
• 既然指针也是一种数据类型,那么其也应该有对 应操作和运算方法,正如整数能做加、减、乘、 除一样。但是每一种操作或者运算都应该对这种 数据类型有意义。比如用乘法计算两个整数的乘 积,用加法计算两个整数的和。而如果去求一个 整数除以0的结果,这就没有意义了。对于指针类 型来说,C++中规定了其可以和整数做加法或者减 法运算,两个指针还可以做关系运算。
• • • • • • • •
10.2.4 特殊的值—NULL
• 在前面的变量相关的章节中已经讲过,如果变量 没有被初始化,就不能直接使用,对于指针变量 也是一样的。如果在声明指针变量后,没有对其 进行初始化或者暂时不使用,应该将其指向一个 特殊的值-NULL。 • 在C++中NULL表示的意思是“空或者什么都没有”, 即指针没有指向任何地址。这就好像一间房间已 经建好了,但还没有装修,没有办法住人,必须 标示出来一样。使用NULL来定义指针的方法如下: • int * p = NULL; • 注意:C++是大小写区别对待的,所以NULL和null 是不同的。在使用时必须是大写的NULL。
• • • • • • • •
10.2.3 获得地址并对指针变量进行初始化
• 一旦声明了变量,则放置该变量的地方就在内存中有了地址,可以用“&”取地址操 作符来获取变量的地址。对于一般的变量、数组中的元素等的地址值都可以用在变量 名前加上“&”来取得,其格式如下: &变量名; 通过这种方式就能够取得变量所在的地址值,例如: int a = 10; int b[5] = {1,2,3,4,5}; 取变量a的地址值用&a;取数组元素b[3]的地址值用&b[3]。有了地址值就可以赋值给 指针变量,对其进行初始化了。例如: int *pa = &a; //将a的地址赋给存放地址的变量(指针) int *pb = &b[3]; //将b[3]的地址赋给存放地址的变量(指 针) 这时,pa中保存的是变量a的地址值,pb中保存的是数组元素b[3]的地址。也可以说 pa指向了变量a,pb指向了数组元素b[3]。现在假定变量a的地址为8000,数组元素 b[3]的地址为9006,那么指针和变量的关系如图10.1所示。
•
•
•
10.2.2 指针变量的声明
• • • 在此之前,“*”是乘法符号,在这里用其来声明指针变量。其声明格式 如下: 数据类型 * 指针名; 其中,数据类型应该是指针所指向的数据相符合的数据类型。如int、 char、float等。“*”表示所声明的是一个指针变量,而不是普通变量。 如果这个“*”号被忽略的话,就变成声明变量了,笔者当年学习C++时, 就常常犯这个错误,所以对于初学者一定要多多注意。 指针的变量名应遵循C++标识符的命名规则。如果要声明多个指针变量时, 必须在每个指针变量名前加上“*”,如下所示: 数据类型 * 指针名1,指针名2,„; 下面列举几种常用的指针变量的声明: int * pn, *pi; //pn和pi是两个指向int型变量的指 针 float * pl; //pl是一个指向float型变量的指针 char * pc; //pc是一个指向char型变量的指针 int *(pf)(); //pf是一个指向函数的指针,该函数的返回值为 int型数值 int * *pp; //pp是一个指向指针的指针,即二维指针
第10章 让人迷惑的指针
• 在前一章中学习数组相关的知识的时候,提到了内存地址。本章 将继续深入学习内存地址,并向读者介绍在C++中可以访问和操作 内存地址的一种特殊类型变量—指针。指针不仅可以获得数据的 地址,而且还可以操纵地址。所以指针就成为了C++中最灵活,也 是最难学的部分。本章主要涉及到的知识点如下所述。 • 指针的概念:明白什么是指针,其有什么特殊的意义。 • 指针变量的声明、定义、初始化及使用:知道指针变量如何定义、 如何初始化、如何使用。 • 对指针变:知道数组和字符串指针的特点及使用。 • 常量指针与指针常量:明白指针常量与常量指针的区别及其如何 使用。 • 指针与函数:明白如何把指针作为参数交给函数处理及返回。 • C++中堆内存概念与使用:知道什么堆内存及其如何申请和释放。
10.3.2 指针变量的关系运算
• 在一定条件下,两个指针可以进行关系运算,例 如:两个变量指向同一个数组中的元素,如果两 个指针进行比较,在前面的元素的指针变量“小 于”指向后面元素的指针变量。当两个指针相等 时,说明这两个指针指向同一元素。关于指针变 量的关系运算不属于入门级的应用,所以本书将 不做相应介绍。
10.4.2 一维数组中元素的指针表示法
• • • • • • • 一维数组中元素的指针表示法。例如: int array[5]; 其中,array是一维数组名,其包含5个int型的元素。如果用下标的方法来表示 其中的元素,如下所示。 array[i] //其中,i=0,1,2,3,4 而如果用指针的方法来表示,如下所示。 *(array+i) //其中,i=0,1,2,3,4 因为在C++中数组名就是指向数组首地址的常量指针,所以在一维数组中,数组 中的第i个元素可以用上面的方式表示。现在对两种方式进行举例说明,如代码 10.6所示。【代码参考:光盘的源代码\C10\chap6.dsp】
10.2.5 指针的使用
• • • 在C++中规定“*”为指针的间接访问操作符。其作用是间接地访 问指针所指向的变量或者内存空间中的数据,其格式为: *指针变量名 这时“*指针变量名”就可以代表指针所指向的变量或者其他数据。 现在将介绍如何使用指针变量。其如代码10.1所示。【代码参考: 光盘的源代码\C10\chap1.dsp】
10.2.6 指向指针变量的指针
• 指向指针变量的指针听上去很不容易理解,其实可以这样 认为:指针变量也是变量,所以其也有地址值的,当然也 可以用指针指向这个地址。指向指针变量的指针的声明和 初始化方式如下: • int i = 0; //定义一个 变量i • int *p = &i; //定 义一个指针变量p,并赋初始值为i的地址 • int **pp=&p; //定义一个 指向指针变量的指针pp,赋初始值为p的地址 • 上面的int **pp可以这样理解,其数据类型是int*型而不是int 型的,所以pp是指向int型指针的指针变量。
10.5 常量指针与指针常量
• 除了前面讲解的各种常用指针外,C++中还包括常 量指针和指针常量,不过这两个特殊的指针是从 名字上来看是比较容易混淆的,所以特别放在这 一节中对两个概念进行详细的讲解和比较。
10.5.1 常量指针
• 因为指针的功能很强大,其可以任意修改指向的数据的值, 所以为了解决指针误修改或者破坏内存中的数据,就需要 定义常量指针。 • 常量指针顾名思义,即指向常量的指针被为常量指针。在 前面的章节讲过,为了保证数据在内存中不被修改,就需 要把其定义为常量。在这里,定义常量指针是为了保证指 针所指向的数据值在使用指针间接访问时不被改变,相当 于常量。就像如果你把电脑上任意文件的属性改变为“只 读”后,其就只能被读取,而不能被修改一样的。 • 定义常量指针的方法是在指针定义语句的数据类型前加 const,格式如下: • const 数据类型 * 指针变量名;
10.4.3 二维数组的指针表示法
• • • • • • • 二维数组的指针表示法。例如: int array[3][3]; 其中,array是二维数组名,其有9个int型的元素,如果用下标的方法来 表示其中的元素,如下所示。 array[i][j] //其中,i=0,1,2; j=0,1,2 而如果用指针的方法来表示,如下所示。 *(*(array+i)+j) //其中,i=0,1,2; j=0,1,2 可以把一个二维数看作是一个一维数组,它的元素又是一个一维数组。对 array[3][3]来说。可以认为其是具有3个元素一维数组,暂称为行数组, 而其中每个元素又是具有3个元素的一维数组,暂称为列数组。这样,可 以把array[3][3]看成为3个元素的一维行数组和3个元素的一维数组组成。 前面已经讲过了一维数组的指针表示方法。现在将二维数组的行和列的一 维数组都用指针来表示,便得到前面所述的形式。如果将二维数组的行数 组用下标来表示,列数组用指针来表示,可以得到如下形式: *(array[i]+j) 因为指针也是一种的数据类型,所以可以声明这样一个数组,其元素全部 是指针,这种数组被称为指针数组。指针数组的声明格式如下: 数据类型 * 数组名[数组大小]; 对指针数组的操作,和普通数组是一样的,所以这里就不再复述。
• • • •
10.4.4 字符串和指针
• • • • • • 在C++中有两种方式实现一个字符串,其如下所示: 1.用字符数组来实现字符串 用字符数组来实现字符串的方式如下: char str[] = “this is C++ program”; 2.用字符指针来实现字符串 用字符指针来实现字符串,即不定义字符串数组, 而定义一个字符指针。声明一个字符指针的方式 如下: • char * pStr; • 在声明字符指针后,可以直接对其赋值进行初始 化,即把字符指针指向字符串。其如下所示: • char * pStr = “this is c++ program”;
10.4 数组、字符串和指针
• C++中的指针不仅可以用来指向变量,而且还可以 用来指向数组或者字符串,指向数组的指针被称 为数组指针,指向字符串的指针被称为字符串指 针,下面笔者将分别对数组指针和字符串指针进 行详细讲解。
10.4.1 数组和指针
• 不知道读者还记得前面在讲解数组一章时,提到 过数组可以作为函数的参数来传递给函数。其本 质就是传递的数组的首地址(数组的首元素地址) 给函数处理。地址就是指针,所以从这点可以看 出来,其实数组名就是指向数组首地址的常量指 针(将在后面详细讲解)。换句话说,就是可以 用数组名来初始化一个对应数据类型指针。例如: • int b[3] = {1,2,3}; • int *pb = b;
10.3.1 指针变量的加减运算
• 指针做的加减法,同数值做的加减法是不相同的。因为指 针只能做整数的加减法运算。而不能做实数的加减法运算。 因为,内存中的存储空间都是整数个的,而不存在有半个 存储空间出现的情况。现在来看看对指针做加减法运算, 是什么结果呢?其如代码10.3所示。【代码参考:光盘的 源代码\C10\chap3.dsp】
10.1 变量的“向导”—指针
• 现在假设你和朋友张三出去旅游住在宾馆。知道张三的朋 友李四也住在同一家宾馆。你很想知道李四的年龄多大, 但又不知道他住在多少号房间,张三却知道。而张三住在 1208房间,如果你要想找到李四,就需要如下步骤才能找 到。 • (1)先去1208房间。 • (2)获得李四住的房间号:2202号。 • (3)去2202号房间。 • (4)找到李四,得到年龄35。 • 如果只对姓名和房间号来简化这个事情的话,如表10.1所 示。