谭浩强《C++程序设计》课件第6章.ppt

上传人:zhu****ei 文档编号:5399739 上传时间:2020-01-28 格式:PPT 页数:100 大小:507.50KB
返回 下载 相关 举报
谭浩强《C++程序设计》课件第6章.ppt_第1页
第1页 / 共100页
谭浩强《C++程序设计》课件第6章.ppt_第2页
第2页 / 共100页
谭浩强《C++程序设计》课件第6章.ppt_第3页
第3页 / 共100页
点击查看更多>>
资源描述
第6章指针 6 1指针的概念6 2变量与指针6 3数组与指针6 4字符串与指针6 5函数与指针6 6返回指针值的函数6 7指针数组和指向指针的指针6 8有关指针的数据类型和指针运算的小结 6 9引用 为了说清楚什么是指针 必须弄清楚数据在内存中是如何存储的 又是如何读取的 如果在程序中定义了一个变量 在编译时就给这个变量分配内存单元 系统根据程序中定义的变量类型 分配一定长度的空间 例如 C 编译系统一般为整型变量分配4个字节 为单精度浮点型变量分配4个字节 为字符型变量分配1个字节 内存区的每一个字节有一个编号 这图6 1就是 地址 6 1指针的概念 图6 1 请务必弄清楚一个内存单元的地址与内存单元的内容这两个概念的区别 在程序中一般是通过变量名来对内存单元进行存取操作的 其实程序经过编译以后已经将变量名转换为变量的地址 对变量值的存取都是通过地址进行的 这种按变量地址存取变量值的方式称为直接存取方式 或直接访问方式 还可以采用另一种称为间接存取 间接访问 的方式 可以在程序中定义这样一种特殊的变量 它是专门用来存放地址的 图6 2是直接访问和间接访问的示意图 为了将数值3送到变量中 可以有两种方法 1 直接将数3送到整型变量 所标识的单元中 见图6 2 a 2 将3送到指针变量i pointer所指向的单元 这就是变量i所标识的单元 中 见图6 2 b 图6 2所谓指向 就是通过地址来体现的 由于通过地址能找到所需的变量单元 因此可以说 地址指向该变量单元 因此将地址形象化地称为 指针 一个变量的地址称为该变量的指针 如果有一个变量是专门用来存放另一变量地址 即指针 的 则它称为指针变量 指针变量的值 即指针变量中存放的值 是地址 即指针 指针变量是一种特殊的变量 它和以前学过的其他类型的变量的不同之处是 用它来指向另一个变量 为了表示指针变量和它所指向的变量之间的联系 在C 中用 符号表示指向 例如 i pointer是一个指针变量 而 i pointer表示i pointer所指向的变量 见图6 3 下面两个语句作用相同 i 3 i pointer 3 图6 3 6 2变量与指针 C 规定所有变量在使用前必须先定义 即指定其类型 在编译时按变量类型分配存储空间 对指针变量必须将它定义为指针类型 先看一个具体例子 inti j 定义整型变量i jint pointer 1 pointer 2 定义指针变量 pointer 1 pointer 2第2行开头的int是指 所定义的指针变量是指向整型数据的指针变量 也就是说 指针变量pointer 1和pointer 2只能用来指向整型数据 例如i和j 而不能指向浮点型变量a和b 这个int就是指针变量的基类型 指针变量的基类型用来指定该指针变量可以指向的变量的类型 定义指针变量的一般形式为 6 2 定义指针变量 基类型 指针变量名 下面都是合法的定义 float pointer 3 pointer 3是指向单精度型数据的指针变量char pointer 4 pointer 4是指向字符型数据的指针变量请注意 指针变量名是pointer 3和pointer 4 而不是 pointer 3和 pointer 4 即 不是指针变量名的一部分 在定义变量时在变量名前加一个 表示该变量是指针变量 那么 怎样使一个指针变量指向另一个变量呢 只需要把被指向的变量的地址赋给指针变量即可 例如 pointer 1 将变量j的地址存放到指针变量pointer 2中 这样 pointer 1就指向了变量i pointer 2就指向了变量j 见图6 4 图6 4一般的C 编译系统为每一个指针变量分配4个字节的存储单元 用来存放变量的地址 在定义指针变量时要注意 1 不能用一个整数给一个指针变量赋初值 2 在定义指针变量时必须指定基类型 有两个与指针变量有关的运算符 1 取地址运算符 2 指针运算符 或称间接访问运算符 例如 a为变量a的地址 p为指针变量p所指向的存储单元 6 2 2引用指针变量 例6 1通过指针变量访问整型变量 includeusingnamespacestd intmain inta b 定义整型变量a bint pointer 1 pointer 2 定义指针变量 pointer 1 pointer 2a 100 b 10 对a b赋值pointer 1 运行结果为10010 a和b的值 10010 pointer 1和 pointer 2的值 请对照图6 5分析 图6 5 下面对 语句 请问 pointer 1的含义是什么 和 两个运算符的优先级别相同 但按自右至左方向结合 因此先进行 pointer 1的运算 它就是变量a 再执行 运算 因此 pointer 1与 a相同 即变量a的地址 如果有pointer pointer 1 它的作用是将 a a的地址 赋给pointer 2 如果pointer 2原来指向b 经过重新赋值后它已不再指向b了 而也指向了 见图6 6 图6 6 a 是原来的情况 图6 6 b 是执行上述赋值语句后的情况 图6 6 2 它们等价于变量a 即 a与a等价 见图6 7 图6 7例6 2输入a和b两个整数 按先大后小的顺序输出a和b 用指针变量处理 解此题的思路是 设两个指针变量p1和p2 使它们分别指向a和b 使p1指向a和b中的大者 p2指向小者 顺序输出 p1 p2就实现了按先大后小的顺序输出a和b 按此思路编写程序如下 includeusingnamespacestd intmain int p1 p2 p a b cin a b 输入两个整数p1 运行情况如下 4578 a 45b 78max 78min 45输入a的值45 b的值78 由于a b 将p1的值和p2的值交换 即将p1的指向与p2的指向交换 交换前的情况见图6 8 a 交换后的情况见图6 8 b 图6 8请注意 这个问题的算法是不交换整型变量的值 而是交换两个指针变量的值 函数的参数不仅可以是整型 浮点型 字符型等数据 还可以是指针类型 它的作用是将一个变量的地址传送给被调用函数的形参 例6 3题目同例6 2 即对输入的两个整数按大小顺序输出 这里用函数处理 而且用指针类型的数据作函数参数 程序如下 6 2 3指针作为函数参数 includeusingnamespacestd intmain voidswap int p1 int p2 函数声明int pointer 1 pointer 2 a b 定义指针变量pointer 1 pointer 2 整型变量a bcin a b pointer 1 运行情况如下 4578 max 78min 45请注意 不要将main函数中的swap函数调用写成if a b swap pointer 1 pointer 2 图6 9 请注意交换 p1和 p2的值是如何实现的 如果写成以下这样就有问题了 voidswap int p1 int p2 int temp temp p1 此语句有问题 p1 p2 p2 temp 本例采取的方法是交换a和b的值 而p1和p2的值不变 这恰和例6 2相反 可以看到 在执行swap函数后 主函数中的变量a和b的值改变了 这个改变不是通过将形参值传回实参来实现的 请读者考虑一下能否通过调用下面的函数实现a和b互换 voidswap intx inty inttemp temp x x y y temp 在main函数中用 swap a b 调用swap函数 会有什么结果呢 在函数调用时 a的值传送给x b的值传送给y 如图6 10 a 所示 执行完swap函数最后一个语句后 x和y的值是互换了 但main函数中的a和b并未互换 如图6 10 b 所示 也就是说由于虚实结合是采取单向的 值传递 方式 只能从实参向形参传数据 形参值的改变无法回传给实参 图6 10为了使在函数中改变了的变量值能被main函数所用 不能采取把要改变值的变量作为参数的办法 而应该用指针变量作为函数参数 在函数执行过程中使指针变量所指向的变量值发生变化 函数调用结束后 这些变量值的变化依然保留下来 这样就实现了 通过调用函数使变量的值发生变化 在主调函数中使用这些改变了的值 的目的 如果想通过函数调用得到n个要改变的值 可以采取下面的步骤 在主调函数中设n个变量 用n个指针变量指向它们 编写被调用函数 其形参为n个指针变量 这些形参指针变量应当与主调函数中的n个指针变量具有相同的基类型 在主调函数中将n个指针变量作实参 将它们的值 是地址值 传给所调用函数的n个形参指针变量 这样 形参指针变量也指向这n个变量 通过形参指针变量的指向 改变该n个变量的值 在主调函数中就可以使用这些改变了值的变量 请注意 不能企图通过改变形参指针变量的值而使实参指针变量的值改变 请分析下面程序 includeusingnamespacestd intmain voidswap int p1 int p2 int pointer 1 pointer 2 a b cin a b pointer 1 图6 11实参变量和形参变量之间的数据传递是单向的 值传递 方式 指针变量作函数参数也要遵循这一规则 调用函数时不会改变实参指针变量的值 但可以改变实参指针变量所指向变量的值 函数的调用可以 而且只可以 得到一个返回值 即函数值 而使用指针变量作函数参数 就可以通过指针变量改变主调函数中变量的值 相当于通过函数调用从被调用的函数中得到多个值 如果不用指针变量是难以做到这一点的 例6 4输入a b c3个整数 按由大到小的顺序输出 用上面介绍的方法 用3个指针变量指向3个整型变量 然后用swap函数来实现互换3个整型变量的值 程序如下 includeusingnamespacestd intmain voidexchange int int int 对exchange函数的声明inta b c p1 p2 p3 cin a b c 输入3个整数 p1 运行情况如下 12 5687 8712 56 一个变量有地址 一个数组包含若干元素 每个数组元素都在内存中占用存储单元 它们都有相应的地址 指针变量既然可以指向变量 当然也可以指向数组元素 把某一元素的地址放到一个指针变量中 所谓数组元素的指针就是数组元素的地址 inta 10 定义一个整型数组a 它有10个元素int p 定义一个基类型为整型的指针变量pp 将元素a 0 的地址赋给指针变量p 使p指向a 0 在C 中 数组名代表数组中第一个元素 即序号为0的元素 的地址 因此 下面两个语句等价 6 3数组与指针6 3 1指向数组元素的指针 p 对p当前所指向的数组元素赋予数值1如果指针变量p已指向数组中的一个元素 则p 1指向同一数组中的下一个元素 如果p的初值为 a 0 则 1 p i和a i就是a i 的地址 或者说 它们指向a数组的第i个元素 见图6 12 图6 12 2 p i 或 a i 是p i或a i所指向的数组元素 即a i 可以看出 实际上是变址运算符 对a i 的求解过程是 先按a i d计算数组元素的地址 然后找出此地址所指向的单元中的值 3 指向数组元素的指针变量也可以带下标 如p i 与 p i 等价 根据以上叙述 引用一个数组元素 可用以下方法 1 下标法 如a i 形式 2 指针法 如 a i 或 p i 其中a是数组名 p是指向数组元素的指针变量 如果已使p的值为a 则 p i 就是a i 可以通过指向数组元素的指针找到所需的元素 使用指针法能使目标程序质量高 例6 5输出数组中的全部元素 假设有一个整型数组a 有10个元素 要输出各元素的值有3种方法 1 下标法 includeusingnamespacestd intmain inta 10 inti for i 0 i a i 引用数组元素a i cout endl for i 0 i 10 i cout a i 引用数组元素a i cout endl return0 运行情况如下 9876543210 输入10个元素的值 9876543210 输出10个元素的值 2 指针法将上面程序第7行和第10行的 a i 改为 a i 运行情况与 1 相同 3 用指针变量指向数组元素 includeusingnamespacestd intmain inta 10 inti p a 指针变量p指向数组a的首元素a 0 for i 0 i p i 输入a 0 a 9 共10个元素cout endl for p a p a 10 p cout p p先后指向a 0 a 9 cout endl return0 运行情况与前相同 请仔细分析p值的变化和 p的值 对3种方法的比较 方法 1 和 2 的执行效率是相同的 第 3 种方法比方法 1 2 快 这种方法能提高执行效率 用下标法比较直观 能直接知道是第几个元素 用地址法或指针变量的方法都不太直观 难以很快地判断出当前处理的是哪一个元素 在用指针变量指向数组元素时要注意 指针变量p可以指向有效的数组元素 实际上也可以指向数组以后的内存单元 如果有inta 10 p a 指针变量p的初值为 要输出a 10 的值在使用指针变量指向数组元素时 应切实保证指向数组中有效的元素 指向数组元素的指针的运算比较灵活 务必小心谨慎 下面举几个例子 如果先使p指向数组a的首元素 即p a 则 1 p 或p 1 使p指向下一元素 即a 1 如果用 p 得到下一个元素a 1 的值 2 p 由于 和 同优先级 结合方向为自右而左 因此它等价于 p 作用是 先得到p指向的变量的值 即 p 然后再使p的值加1 例6 5 3 程序中最后一个for语句 for p a p a 10 p cout p 可以改写为for p a p a 10 cout p 3 p 与 p 作用不同 前者是先取 p值 然后使p加1 后者是先使p加1 再取 p 若p的初值为a 即 a 0 输出 p 得到a 0 的值 而输出 p 则得到a 1 的值 4 p 表示p所指向的元素值加 即 a 0 如果a 0 3 则 a 0 的值为4 注意 是元素值加1 而不是指针值加1 5 如果p当前指向a i 则 p 先对p进行 运算 得到a i 再使p减1 p指向a i 1 p 先使p自加1 再作 运算 得到a i 1 p 先使p自减1 再作 运算 得到a i 1 将 和 运算符用于指向数组元素的指针变量十分有效 可以使指针变量自动向前或向后移动 指向下一个或上一个数组元素 例如 想输出 数组100个元素 可以用以下语句 p a p a while p a 100 或while p a 100 cout p cout p p 在用 p 形式的运算时 很容易弄错 一定要十分小心 弄清楚先取p值还是先使p加1 在第5章5 4节中介绍过可以用数组名作函数的参数 前面已经多次强调 数组名代表数组首元素的地址 用数组名作函数的参数 传递的是数组首元素的地址 很容易推想 用指针变量作函数形参 同样可以接收从实参传递来的数组首元素的地址 此时 实参是数组名 下面将第5章5 4节中的例5 7程序改写 用指针变量作函数形参 例6 6将10个整数按由小到大的顺序排列 在例5 7程序的基础上 将形参改为指针变量 6 3 2用指针变量作函数参数接收数组地址 includeusingnamespacestd intmain voidselect sort int p intn 函数声明inta 10 i cout a i cout endl select sort a 10 函数调用 数组名作实参cout thesortedarray endl for i 0 i 10 i 输出10个已排好序的数cout a i cout endl return0 voidselect sort int p intn 用指针变量作形参 inti j k t for i 0 i n 1 i k i for j i 1 j n j if p j p k k j 用指针法访问数组元素t p k p k p i p i t 运行情况与例5 7相同 图6 13本例与例5 7在程序的表现形式上虽然有不同 但实际上 两个程序在编译以后是完全相同的 C 编译系统将形参数组名一律作为指针变量来处理 实际上在函数调用时并不存在一个占有存储空间的形参数组 只有指针变量 实参与形参的结合 有以下4种形式 实参形参数组名数组名 如例5 7 数组名指针变量 如例6 6 指针变量数组名指针变量指针变量在此基础上 还要说明一个问题 实参数组名a代表一个固定的地址 或者说是指针型常量 因此要改变a的值是不可能的 如a 语法错误 a是常量 不能改变 而形参数组名是指针变量 并不是一个固定的地址值 它的值是可以改变的 在函数调用开始时 它接收了实参数组首元素的地址 但在函数执行期间 它可以再被赋值 如f array intn cout array 输出array 0 的值array array 3 指针变量array的值改变了 指向array 3 cout arr endl 输出array 3 的值 用指针变量可以指向一维数组中的元素 也可以指向多维数组中的元素 1 多维数组元素的地址设有一个二维数组 它有3行4列 它的定义为inta 3 4 1 3 5 7 9 11 13 15 17 18 21 23 a是一个数组名 a数组包含3行 即3个元素 a 0 a 1 a 2 而每一元素又是一个一维数组 它包含4图6 14个元素 即4个列元素 例如 a 0 所代表的一维数组又包含4个元素 a 0 0 a 0 1 a 0 2 a 0 3 见图6 14 可以认为二维数组是 数组的数组 即数组a是由3个一维数组所组成的 6 3 3多维数组与指针 图6 14从二维数组的角度来看 a代表二维数组首元素的地址 现在的首元素不是一个整型变量 而是由4个整型元素所组成的一维数组 因此a代表的是首行的起始地址 即第0行的起始地址 a 0 a 1代表a 1 行的首地址 即 a 1 a 0 a 1 a 2 既然是一维数组名 而C 又规定了数组名代表数组首元素地址 因此a 0 代表一维数组a 0 中0列元素的地址 即 a 0 0 a 1 的值是 a 1 0 a 2 的值是 a 2 0 图6 150行1列元素的地址可以直接写为 a 0 1 也可以用指针法表示 a 0 为一维数组名 该一维数组中序号为1的元素显然可以用a 0 1来表示 见图6 16 欲得到a 0 1 的值 用地址法怎么表示呢 既然a 0 1是a 0 1 元素的地址 那么 a 0 1 就是a 0 1 元素的值 而a 0 又是和 a 0 无条件等价的 因此也可以用 a 0 1 表示a 0 1 元素的值 依此类推 a i j 或 a i j 是a i j 的值 图6 16 2 指向多维数组元素的指针变量 1 指向数组元素的指针变量例6 7输出二维数组各元素的值 这里采用的方法是用基类型为整型的指针变量先后指向各元素 逐个输出它们的值 includeusingnamespacestd intmain inta 3 4 1 3 5 7 9 11 13 15 17 19 21 23 int p p是基类型为整型的指针变量for p a 0 p a 0 12 p cout p cout endl return0 运行结果如下 1357911131517192123说明 p是指向整型数据的指针变量 在for语句中对p赋初值a 0 也可以写成 p 输出p当前所指的列元素的值 然后执行p 使p指向下一个列元素 2 指向由 个元素组成的一维数组的指针变量可以定义一个指针变量 它不是指向一个整型元素 而是指向一个包含 个元素的一维数组 这时 如果指针变量p先指向a 0 即p a 0 则p 1不是指向a 0 1 而是指向a 1 p的增值以一维数组的长度为单位 见图6 17 图6 17 例6 8输出二维数组任一行任一列元素的值 includeusingnamespacestd intmain inta 3 4 1 3 5 7 9 11 13 15 17 19 21 23 int p 4 i j cin i j p a cout p i j endl return0 运行情况如下 23 23 由于执行了 p a 使p指向a 0 因此p 2是二维数组a中序号为2的行的起始地址 由于p是指向一维数组的指针变量 因此p加1 就指向下一个一维数组 见图6 18 p 2 3是a数组2行3列元素地址 p 2 3 是a 2 3 的值 图6 18 3 用指向数组的指针作函数参数一维数组名可以作为函数参数传递 多维数组名也可作函数参数传递 例6 9输出二维数组各元素的值 题目与例6 7相同 但本题用一个函数实现输出 用多维数组名作函数参数 includeusingnamespacestd intmain voidoutput int p 4 函数声明inta 3 4 1 3 5 7 9 11 13 15 17 19 21 23 output a 多维数组名作函数参数return0 voidoutput int p 4 形参是指向一维数组的指针变量 inti j for i 0 i 3 i for j 0 j 4 j cout p i j cout endl 运行情况如下 1357911131517192123 在C 中可以用3种方法访问一个字符串 在第5章介绍了前两种方法 1 用字符数组存放一个字符串例6 10定义一个字符数组并初始化 然后输出其中的字符串 includeusingnamespacestd intmain charstr IloveCHINA cout str endl return0 6 4字符串与指针 运行时输出 IloveCHINA 2 用字符串变量存放字符串例6 11定义一个字符串变量并初始化 然后输出其中的字符串 include includeusingnamespacestd intmain stringstr IloveCHINA cout str endl return0 3 用字符指针指向一个字符串例6 12定义一个字符指针变量并初始化 然后输出它指向的字符串 includeusingnamespacestd intmain char str IloveCHINA cout str endl return0 对字符串中字符的存取 可以用下标方法 也可以用指针方法 例6 13将字符串str1复制为字符串str2 定义两个字符数组str1和str2 再设两个指针变量p1和p2 分别指向两个字符数组中的有关字符 通过改变指针变量的值使它们指向字符串中的不同的字符 以实现字符的复制 includeusingnamespacestd intmain charstr1 IloveCHINA str2 20 p1 p2 p1 str1 p2 str2 for p1 0 p1 p2 p2 p1 p2 0 p1 str1 p2 str2 cout str1is p1 endl cout str2is p2 endl return0 运行结果为str1is IloveCHINA str2is IloveCHINA 图6 19这个例子用来说明怎样使用字符指针 其实 对例6 13来说 用string变量来处理是十分简单的 stringstr1 IloveCHINA str2 定义string变量str2 str1 将str1复制到str2 指针变量也可以指向一个函数 一个函数在编译时被分配给一个入口地址 这个函数入口地址就称为函数的指针 可以用一个指针变量指向函数 然后通过该指针变量调用此函数 例6 14求 和 中的大者 先按一般方法写程序 includeusingnamespacestd intmain intmax intx inty 函数声明 6 5函数与指针6 5 1用函数指针变量调用函数 inta b m cin a b m max a b 调用函数max 求出最大值 赋给mcouty z x elsez y return z 可以用一个指针变量指向max函数 然后通过该指针变量调用此函数 定义指向max函数的指针变量的方法是 int p int int p所指向的函数的形参类型p是指向函数的指针变量指针变量p指向的函数的类型请将它和函数max的原型作比较intmax int int max函数原型可以看出 只是用 p 取代了max 其他都一样 现在将上面程序的主函数修改如下 includeusingnamespacestd intmain intmax intx inty 函数声明int p int int 定义指向函数的指针变量pinta b m p max 使p指向函数maxcin a b m p a b cout max m endl return0 请注意第7行的赋值语句 p max 此语句千万不要漏写 它的作用是将函数max的入口地址赋给指针变量p 这时 p才指向函数max 见图6 20 图6 20指向函数的指针变量的一般定义形式为函数类型 指针变量名 函数形参表 在C语言中 函数指针变量常见的用途之一是作为函数的参数 将函数名传给其他函数的形参 这样就可以在调用一个函数的过程中根据给定的不同实参调用不同的函数 例如 利用这种方法可以编写一个求定积分的通用函数 用它分别求5个函数的定积分 每次需要求定积分的函数是不一样的 可以编写一个求定积分的通用函数integral 它有3个形参 下限a 上限b 以及指向函数的指针变量fun 函数原型可写为doubleintegral doublea doubleb double fun double 6 5 2用指向函数的指针作函数参数 分别编写5个函数f1 f2 f3 f4 f5 用来求上面5个函数的值 然后先后调用integral函数5次 每次调用时把a b以及f1 f2 f3 f4 f5之一作为实参 即把上限 下限以及有关函数的入口地址传送给形参fun 在执行integral函数过程中求出各函数定积分的值 在面向对象的C 程序设计中 这种用法就比较少了 有兴趣的读者可参阅作者所著的 C程序设计 第二版 一书中的有关章节 一个函数可以带回一个整型值 字符值 实型值等 也可以带回指针型的数据 即地址 其概念与以前类似 只是带回的值的类型是指针类型而已 返回指针值的函数简称为指针函数 定义指针函数的一般形式为类型名 函数名 参数表列 例如int a intx inty 6 6返回指针值的函数 如果一个数组 其元素均为指针类型数据 该数组称为指针数组 也就是说 指针数组中的每一个元素相当于一个指针变量 它的值都是地址 一维指针数组的定义形式为类型名 数组名 数组长度 例如int p 4 可以用指针数组中各个元素分别指向若干个字符串 使字符串处理更加方便灵活 6 7指针数组和指向指针的指针6 7 1指针数组的概念 例6 15若干字符串按字母顺序 由小到大 输出 includeusingnamespacestd intmain voidsort char name intn 声明函数voidprint char name intn 声明函数char name BASIC FORTRAN C Pascal COBOL 定义指针数组intn 5 sort name n print name n return0 voidsort char name intn char temp inti j k for i 0 i n 1 i k i for j i 1 j0 k j if k i temp name i name i name k name k temp voidprint char name intn inti for i 0 i n i cout name i endl 运行结果为BASICCOBOLC FORTRANPascal 图6 21图6 22 print函数的作用是输出各字符串 name 0 name 4 分别是各字符串的首地址 print函数也可改写为以下形式 voidprint char name intn inti 0char p p name 0 while i n p name i cout p endl 其中 name i 表示先求 name i 的值 即name i 它是一个地址 将它赋给p 然后i加1 最后输出以p地址开始的字符串 在掌握了指针数组的概念的基础上 下面介绍指向指针数据的指针 简称为指向指针的指针 从图6 22可以看到 name是一个指针数组 它的每一个元素是一个指针型数据 其值为地址 分别指向不同的字符串 数组名name代表该指针数组首元素的地址 name i是name i 的地址 由于name i 的值是地址 即指针 因此name i就是指向指针型数据的指针 还可以设置一个指针变量p 它指向指针数组的元素 见图6 23 p就是指向指针型数据的指针变量 6 7 2指向指针的指针 图6 23怎样定义一个指向指针数据的指针变量呢 如下 char p 从附录B可以知道 运算符的结合性是从右到左 因此 char p 可写成char p 例6 16指向字符型数据的指针变量 includeusingnamespacestd intmain char p 定义指向字符指针数据的指针变量pchar name BASIC FORTRAN C Pascal COBOL p name 2 见图6 23中p的指向cout p endl 输出name 2 指向的字符串cout p endl 输出name 2 指向的字符串中的第一个字符 运行结果为C C指针数组的元素也可以不指向字符串 而指向整型数据或单精度型数据等 在本章开头已经提到了 间接访问 一个变量的方式 利用指针变量访问另一个变量就是 间接访问 如果在一个指针变量中存放一个目标变量的地址 这就是 单级间址 见图6 24 a 指向指针的指针用的是 二级间址 方法 见图6 24 b 从理论上说 间址方法可以延伸到更多的级 见图6 24 c 但实际上在程序中很少有超过二级间址的 图6 24 表6 1有关指针的数据类型 6 8有关指针的数据类型和指针运算的小结6 8 1有关指针的数据类型的小结 前面已用过一些指针运算 如p p i等 现在把全部的指针运算列出如下 1 指针变量加 减一个整数例如 p p p i p i p i p i等 C 规定 一个指针变量加 减一个整数是将该指针变量的原值 是一个地址 和它指向的变量所占用的内存单元字节数相加或相减 如p i代表这样的地址计算 p i d d为p所指向的变量单元所占用的字节数 这样才能保证p i指向p下面的第i个元素 2 指针变量赋值将一个变量地址赋给一个指针变量 如 6 8 2指针运算小结 p 实际上NULL代表整数0 也就是使p指向地址为0的单元 这样可以使指针不指向任何有效的单元 实际上系统已先定义了NULL defineNULL0在iostream头文件中就包括了以上的NULL定义 NULL是一个符号常量 应注意 p的值等于NULL和p未被赋值是两个不同的概念 任何指针变量或地址都可以与NULL作相等或不相等的比较 如if p NULL p p1 4 两个指针变量可以相减如果两个指针变量指向同一个数组的元素 则两个指针变量值之差是两个指针之间的元素个数 见图6 25 假如p1指向a 1 p2指向a 4 则p2 p1 a 4 a 1 4 1 3 但p1 p2并无实际意义 5 两个指针变量比较若两个指针指向同一个数组的元素 则可以进行比较 指向前面的元素的指针变量小于指向后面元素的指针变量 如图6 25中 p1 p2 或者说 表达式 p1 p2 的值为真 而 p2 p1 的值为假 注意 如果p1和p2不指向同一数组则比较无意义 图6 25 6 对指针变量的赋值应注意类型问题 在本章前几节中介绍了指针的基本概念和初步应用 应该说明 指针是C和C 中重要的概念 是C和C 的一个特色 使用指针的优点是 提高程序效率 在调用函数时 如果改变被调用函数中某些变量的值 这些值能为主调函数使用 即可以通过函数的调用 得到多个可改变的值 可以实现动态存储分配 但是同时应该看到 指针使用实在太灵活 对熟练的程序人员来说 可以利用它编写出颇有特色的 质量优良的程序 实现许多用其他高级语言难以实现的功能 但也十分容易出错 而且这种错误往往难以发现 对一个数据可以使用 引用 reference 这是C 对C的一个重要扩充 引用是一种新的变量类型 它的作用是为一个变量起一个别名 假如有一个变量a 想给它起一个别名b 可以这样写 inta 定义a是整型变量int 声明b是a的引用以上语句声明了b是a的引用 即b是a的别名 经过这样的声明后 a或b的作用相同 都代表同一变量 6 9引用6 9 1什么是变量的引用 注意 在上述声明中 是引用声明符 并不代表地址 不要理解为 把a的值赋给b的地址 声明变量b为引用类型 并不需要另外开辟内存单元来存放b的值 b和a占内存中的同一个存储单元 它们具有同一地址 声明b是a的引用 可以理解为 使变量b具有变量a的地址 见图6 26 如果a的值是20 则b的值也是20 图6 26 在声明一个引用类型变量时 必须同时使之初始化 即声明它代表哪一个变量 在声明变量b是变量a的引用后 在它们所在函数执行期间 该引用类型变量b始终与其代表的变量a相联系 不能再作为其他变量的引用 别名 下面的用法不对 inta1 a2 int 企图使b又变成a2的引用 别名 是不行的 例6 17引用和变量的关系 include includeusingnamespacestd intmain inta 10 int 6 9 2引用的简单使用 a的值开始为10 b是a的引用 它的值当然也应该是10 当a的值变为100 a a的值 时 b的值也随之变为100 在输出a和b的值后 b的值变为20 显然a的值也应为20 运行记录如下 100100 a和b的值都是100 2020 a和b的值都是20 有了变量名 为什么还需要一个别名呢 C 之所以增加引用类型 主要是把它作为函数参数 以扩充函数传递数据的功能 到目前为止 本书介绍过函数参数传递的两种情况 1 将变量名作为实参和形参 这时传给形参的是变量的值 传递是单向的 如果在执行函数期间形参的值发生变化 并不传回给实参 因为在调用函数时 形参和实参不是同一个存储单元 例6 18要求将变量i和j的值互换 下面的程序无法实现此要求 6 9 3引用作为函数参数 includeusingnamespacestd intmain voidswap int int 函数声明inti 3 j 5 swap i j 调用函数swapcout i j endl i和j的值未互换return0 voidswap inta intb 企图通过形参a和b的值互换 实现实参i和j的值互换 inttemp temp a 以下3行用来实现a和b的值互换a b b temp 运行时输出35i和j的值并未互换 见图6 27示意 为了解决这个问题 采用传递变量地址的方法 图6 27 2 传递变量的指针 形参是指针变量 实参是一个变量的地址 调用函数时 形参 指针变量 指向实参变量单元 程序见例6 19 例6 19使用指针变量作形参 实现两个变量的值互换 includeusingnamespacestd intmain voidswap int int inti 3 j 5 swap 形参与实参的结合见图6 28示意 图6 28这种虚实结合的方法仍然是 值传递 方式 只是实参的值是变量的地址而已 通过形参指针变量访问主函数中的变量 i和j 并改变它们的值 这样就能得到正确结果 但是在概念上却是兜了一个圈子 不那么直截了当 在Pascal语言中有 值形参 和 变量形参 即var形参 对应两种不同的传递方式 前者采用值传递方式 后者采用地址传递方式 在C语言中 只有 值形参 而无 变量形参 全部采用值传递方式 C 把引用型变量作为函数形参 就弥补了这个不足 C 提供了向函数传递数据的第 3 种方法 即传送变量的别名 例6 20利用 引用形参 实现两个变量的值互换 includeusingnamespacestd intmain voidswap int swap i j cout i i j j endl return0 voidswap int 输出结果为i 5j 3在swap函数的形参表列中声明a和b是整型变量的引用 图6 29实际上 在虚实结合时是把实参i的地址传到形参a 使形参a的地址取实参i的地址 从而使a和i共享同一单元 同样 将实参j的地址传到形参b 使形参b的地址取实参j的地址 从而使b和j共享同一单元 这就是地址传递方式 为便于理解 可以通俗地说 把变量i的名字传给引用变量a 使a成为i的别名 请思考 这种传递方式和使用指针变量作形参时有何不同 分析例6 20 对比例6 19 可以发现 使用引用类型就不必在swap函数中声明形参是指针变量 指针变量要另外开辟内存单元 其内容是地址 而引用变量不是一个独立的变量 不单独占内存单元 在例6 20中引用变量a和b的值的数据类型与实参相同 都是整型 在main函数中调用swap函数时 实参不必用变量的地址 在变量名的前面加 而直接用变量名 系统向形参传送的是实参的地址而不是实参的值 这种传递方式相当于Pascal语言中的 变量形参 显然 这种用法比使用指针变量简单 直观 方便 使用变量的引用 可以部分代替指针的操作 有些过去只能用指针来处理的问题 现在可以用引用来代替 从而降低了程序设计的难度 例6 21对3个变量按由小到大的顺序排序 includeusingnamespacestd intmain voidsort int 使i j if i k change i k 使ik change j k 使j k voidchange int 运行情况如下 Pleaseenter3integers 2312 345 sortedorderis 3451223 可以看到 这个程序很容易理解 不易出错 由于在调用sort函数时虚实结合使形参i j k成为实参a1 b1 c1的引用 因此通过调用函数sort a1 b1 c1 既实现了对i j k排序 也就同时实现了对a1 b1 c1排序 同样 执行change i j 函数 可以实现对实参i和j的互换 引用不仅可以用于变量 也可以用于对象 例如实参可以是一个对象名 在虚实结合时传递对象的起始地址 这会在以后介绍 当看到 a这样的形式时 怎样区别是声明引用变量还是取地址的操作呢 当 a的前面有类型符时 如int a 它必然是对引用的声明 如果前面无类型符 如cout a 则是取变量的地址
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!