第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针

Slides:



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

第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
第 6 章 第 6 章 指 针 指 针 1.
第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(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
在PHP和MYSQL中实现完美的中文显示
Using C++ The Weird Way Something about c++11 & OOP tricks
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
4.3函数 4.3.1函数的概念及定义 1、函数的概念: 可以被其它程序调用具有 特定功能的一段相对独立的 程序(模块),称函数。
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
指 针 为什么要使用指针 指针变量 指针与数组 返回指针值的函数 动态内存分配 通过指针引用字符串 指向函数的指针 小 结 习 题.
資料大樓 --談指標與陣列 綠園.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第 十 章 指 针.
Object-Oriented Programming in C++ 第一章 C++的初步知识
走进编程 程序的顺序结构(二).
辅导课程六.
计算机网络讲义 第5章 批量数据处理—数组 一维数组 排序和查找 二维数组 字符串.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第3讲 C++程序控制结构 3.1 顺序结构 3.2 分支结构 3.3 循环结构 3.4 转向控制 3.5 综合案例分析.
C语言程序设计基础 第8章 指针 刘新国.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第8章 指针.
程序的三种基本结构 if条件分支语句 switch多路开关语句 循环语句 循环嵌套 break,continue和goto语句
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系.
第一章 函数与极限.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
简单介绍 用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 返回指针值的函数
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
第4章 Excel电子表格制作软件 4.4 函数(一).
第九节 赋值运算符和赋值表达式.
3.16 枚举算法及其程序实现 ——数组的作用.
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本节内容 结构体.
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C语言程序设计 第8章 指针.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
基本知识 数据类型、变量、常量、运算符.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
第八章 指 针 北京邮电大学出版社.
顺序结构程序设计 ——关于“字符串”和数值.
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
Presentation transcript:

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

指针介绍 指针 本章将介绍C++语言的一个重要的特性:指针,为了成为一个优秀的C++语言程序员,你必须掌握指针并熟练地使用它们。 是内存的地址并可作为数据 是一个灵活和危险的机制 允许共享处理数据 允许内存动态分配(只要需要,而非预先定义)

指针的概念 指针就是把地址作为数据处理 指针变量:存储地址的变量 变量的指针:当一个变量存储另一个变量的地址时,那我们说它就是那个变量的指针 使用指针的目的:提供间接访问

指针的概念 续 如在某一程序中定义了 int x = 2; 指针的概念 续 如在某一程序中定义了 int x = 2; 如系统给x分配的空间是1000号单元,则指向x的指针是另一个变量p,p中存放的数据为1000 1000号单元的内容有两种访问方式: 访问变量x(直接访问) 访问变量p指向的单元的内容(间接访问) 1000 2 x p

定义指针变量 定义指针变量要告诉编译器该变量中存放的是一个地址。 指针变量的主要用途是提供间接访问,因此也需要知道指针指向的单元的数据类型 指针变量的定义 类型标识符 *指针变量; 如:int *intp; double *doublep; int *p, x, *q;

指针变量的操作 如何让指针指向某一变量?因为我们不知道系统分配给变量的真正地址是什么。 如何通过指针变量处理和改变它所指向的单元的值? 用地址运算符 “&” 解决。如表达式 “&x” 返回的是变量 x 的地址。如:intp = &x; & 运算符后面不能跟常量或表达式。如 &2 是没有意义的,&(m * n + p )。也是没有意义的 如何通过指针变量处理和改变它所指向的单元的值? 用引用运算符 “*” 解决。如 *intp 表示的是 intp 指向的这个单元的内容。如:*intp = 5 等价于 x = 5 在对 intp 使用引用运算之前,必须先对 intp 赋值

指针实例 如执行: *intp=Y+4; 1000 intp 4 Y 3 X 如有: int X, *intp, Y; X=3; Y=4; 1004 4 Y 3 X 如有: int X, *intp, Y; X=3; Y=4; intp=&X; 1000 intp 1004 4 Y 8 X 注意:不能用 intp=100; 因为我们永远不知道变量存储的 真实地址,而且程序每次运行变量地址可能都不同。

指针使用 指针变量可以指向不同的变量。如上例中intp指向x,我们可以通过对intp的重新赋值改变指针的指向。如果想让intp指向y,只要执行intp=&y就可以了。这时,intp与x无任何关系。 同类的指针变量之间可相互赋值,表示二个指针指向同一内存空间。 空指针 指针没有指向任何空间 空指针用常量NULL表示,NULL的值一般赋为0 不能引用空指针指向的值

指针变量的使用 设有定义 执行语句: 执行语句: 执行语句: int x, y; x=23; p1=&x; *p1=34; int *p1,*p2; 执行语句: x=23; y=234; 执行语句: p1=&x; p2=&y; 执行语句: *p1=34; p2=p1; 1000 x 1004 y 1008 p1 1012 p2 1000 23 x 1004 234 y 1008 p1 1012 p2 1000 23 x 1004 234 y 1008 p1 1012 p2 1000 34 x 1004 234 y 1008 p1 1012 p2

指针实例 有以下结构 A p1 a B p2 b 比较执行 p1=p2和 *p1= *p2后的不同结果。 解: A p1 a B p2 b

指针的初始化 指针在使用前必须初始化。 和别的变量一样,定义指针不初始化是一个比较普通的错误。 没有初始化的指针可能指向任意地址,对这些指针作操作可能会导致程序错误。 NULL是一个特殊指针值,称为空指针。它的值为0。它可被用来初始化一个指针,表示不指向任何地址。 思考:int *p; *p = 5; 有什么问题?

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

指针运算和数组 指向数组元素的指针 数组元素是一个独立的变量,因此可以有指针指向它。如:p = &a[1], p = &a[i] 数组元素的地址是通过数组首地址计算的。如数组的首地址是 1000,则第i 个元素的地址是1000 + i * 每个数组元素所占的空间长度

指针与数组 在C++中,指针和数组关系密切,几乎可以互换使用 数组名可以看成是常量指针,对一维数组来说,数组名是数组的起始地址,也就是第0个元素的地址 如执行了p=array,则p与array是等价的,对该指针可以进行任何有关数组下标的操作

例如:有定义 int a[10], *p 并且执行了 p=a, 那么可用下列语句访问数组a的元素 for ( i=0; i<10; ++i ) cout << p[i];

指针运算 指针保存的是一个地址,地址是一个整型数,因此可以进行各种算术运算,但仅有加减运算是有意义的。指针运算与数组有密切的关系 指针+1表示数组中指针指向元素的下一元素地址; 指针-1表示数组中指针指向元素的上一元素地址; 合法的指针操作:p + k, p - k, p1 - p2

数组元素的指针表示 当把数组名,如 intarray,赋给了一个同类指针intp 后,intarray 的元素可以通过intp访问。第i个元素的地址可表示为 intp + i,第i个元素的值可表示为 *(intp + i)。 通过指针访问数组时,下标有效范围由程序员自己检查。 如输出数组 a 的十个元素

for ( p=a; p<a+10; ++p ) cout << *p ; 方法4: 方法2: for ( i=0; i<10; ++i ) cout << *(a+i); 方法1: for ( i=0; i<10; ++i ) cout << a[i]; 方法3: for ( p=a; p<a+10; ++p ) cout << *p ; 方法4: for ( p=a, i=0; i<10; ++i ) cout << *(p+i); 方法5: for ( p=a, i=0; i<10; ++i ) cout << p[i] ; 下列程序段 有无问题? for ( i=0; i<10; ++i ) { cout << *a ; ++a; }

指针和数组的区别 当执行了 intp = array 后 虽然通过指针可以访问数组,但两者本质是不同的。 在定义数组时为数组的各个元素分配了全部的存储区,而在定义指针时,仅仅分配四个字节的存储区存放指针地址。只有把一个数组名付给了对应的指针后,指针才能当作数组使用 如有:int array[5], *intp; array intp 当执行了 intp = array 后

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

动态分配 在C++语言中,每个程序需要用到几个变量,在写程序前就应该知道。每个数组有几个元素也必须在写程序时就决定。 有时我们并不知道我们需要多大的数组元素直到程序开始运行。因此希望能在程序中根据某一个当前运行值来决定数组的大小。如设计一个打印魔阵的程序,我们希望先输入魔阵的阶数,然后根据阶数定义一个矩阵

动态分配方法 这些问题的解决方案就是内存的动态分配。我们定义一个指针,并让它指向一个合适的内存。如: int *scores;

动态内存分配与回收 C++中由new 和 delete两个运算符替代 - 运算符new用于进行内存分配: 申请动态变量:p = new type; 申请动态数组:p = new type[size]; 申请动态变量并初始化:p = new type(初值); - 运算符delete 释放 new分配的内存: 释放动态变量:delete p; 释放动态数组:delete [] p;

动态内存分配与回收 //为简单变量动态分配内存,并作初始化 int main() { int *p; p = new int(99); //动态分配内存,并将99作为初始化值赋给它 cout<< *p; delete p; return 0; }

动态内存分配与回收 //动态字符串的使用 int main() {int *p; 输出结果: char *q; 5 abcde p = new int(5); q = new char[10]; strcpy(q, "abcde"); cout << *p << endl; cout << q << endl; delete p; delete q; return 0; } 输出结果: 5 abcde

动态分配的检查 new操作的结果是申请到的空间的地址 当系统空间用完时,new操作可能失败 new操作失败时,返回空指针

动态内存分配与回收 //动态分配检查 int main() {int *p; p = new int; if(!p) { cout << "allocation failure\n"; return 1;} *p = 20; cout << *p; delete p; return 0; }

assert宏 assert()宏在标准头文件cassert中

#include <iostream> #include <cassert> //包含assert宏的头文件 using namespace std; int main() { int *p; p = new int; assert (p != 0); //p等于0,则退出程序 *p=20; cout << *p; delete p; return 0; }

内存分配的进一步介绍 静态分配:对全局变量和静态变量,编译器为它们分配空间,这些空间在整个程序运行期间都存在 自动分配:函数内的局部变量空间是分配在系统的栈工作区。当函数被调用时,空间被分配;当函数执行结束后,空间被释放 动态分配:在程序执行过程中需要新的存储空间时,可用动态分配的方法向系统申请新的空间,当不再使用时用显式的方法还给系统。这部分空间是从被称为堆的内存区域分配。 OS Program Heap 动态分配 Stack 自动分配 Globe variables 静态分配

内存泄漏 动态变量是通过指针间接访问的。如果该指针被修改,这个区域就被丢失了。堆管理器认为你在继续使用它们,但你不知道它们在哪里,这称为内存泄露。 为了避免出现孤立的区域,应该明白地告诉堆管理器这些区域不再使用。可以采用delete操作,它释放由new申请的内存。 当释放了内存区域,堆管理器重新收回这些区域,而指针仍然指向堆区域,但不能再使用指针指向的这些区域。 要确保在程序中同一个区域释放一次。 释放内存对一些程序不重要,但对有些程序很重要。如果你的程序要运行很长时间,而且存在内存泄漏,这样程序会耗尽所有内存,直至崩溃。

动态空间分配示例 输入一批数据,计算它们的和。数据个数在设计程序时尚无法确定。 存储一批数据应该用数组,但C++语言的数组大小必须是固定的。该问题有两个解决方案: 开设一个足够大的数组,每次运行时只使用一部分。缺点:浪费空间 用动态内存分配根据输入的数据量申请一个动态数组

可改为: p = new int [n]; assert( p != NULL); #include <iostream> using namespace std; int main() { int *p, i, n, sum=0; cout << "An array will be created dynamically.\n\n" ; cout << "Input an array size n followed by n integers:"; cin >> n; if  (!(p = new int [n])) exit(1); for (i=0; i<n; ++i) cin >> p[i]; for(i=0; i<n; ++i) sum += p[i]; delete [] p;  cout << "Number of elements:" << n << endl; cout << "Sum of the elements:" << sum << endl; return 0; } 可改为: p = new int [n]; assert( p != NULL);

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

字符串再讨论 字符串的另一种表示是定义一个指向字符的指针。然后直接将一个字符串常量或字符串变量赋给它 如 char *String, ss[ ] =“abcdef”; String = “abcde”; String = ss;

String = “abcde”;的执行结果 字符串常量存储在一个称为数据段的内存区域里 将存储字符串”abcde”的内存的首地址赋给指针变量String。 String “abcde” Program OS 数据段 或代码区 栈 堆

String = ss的执行过程 OS Program 将字符数组ss的起始地址存入String String “abcdef\0” 数据段 栈 堆 将字符数组ss的起始地址存入String

String = new char[5]; strcpy(String, “aaa”) 动态变量存储在堆工作区 将存储字符串”aaa”的内存的首地址赋给指针变量String。 String Program OS 数据段 栈 堆 “aaa\0”

用指针表示的字符串的操作 可以直接作为字符串操作函数的参数。但必须注意,如果该指针指向的是一个字符串常量时,则使用是受限的。如不能作为strcpy的第一个参数 由于在C++中,数组名被解释成指向数组首地址的指针。因此,字符串是用一个指针变量表示,我们可以把此指针变量解释成数组的首地址,通过下标访问字符串中的字符。如string[3]的值是d。

用指针处理串 目的:编写一个记录串中单词的个数的函数。 关键技术:要传递一个字符串给函数

字符串作为函数的参数 字符串作为函数的参数和数组名作为参数传递一样,可以有两种方法 两种传递方式的本质是一样的,都是传递了字符串的首地址 作为字符数组传递 作为指向字符的指针传递 两种传递方式的本质是一样的,都是传递了字符串的首地址 字符串作为字符数组传递时不需要指定长度。因为字符串操作的结束是依据‘\0’

统计字符串中单词数的函数 #include <ctype> Using namespace std; int word_cnt(const char *s) { int cnt = 0; while (*s != '\0') { while (isspace(*s)) ++s; //跳过空白字符 if (*s != '\0') { ++cnt; //找到一个单词 while (!isspace(*s) && *s != '\0') ++s; //跳过单词 } } return cnt;

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

指针作为函数参数和返回值 指针作为函数参数 数组名作为函数参数 返回指针的函数 引用和引用传递 返回引用的函数

指针作为函数参数 例:编一函数,交换二个参数值。 新手可能会编出如下的函数: void swap(int a, int b) { int c; c=a; a=b; b=c; } 希望通过调用swap(x, y)交换变量x和y的值 因为C++采用的是值传递机制,函数中a、b值的交换不会影响实际参数x和y的值

正确的方法 void swap(int *a, int *b) { int c; c=*a; *a= *b; *b=c; } 3 4 x y 交换x和y的值,可以调用swap(&x, &y) 用指针作为参数可以在函数中修改主调程序的变量值,即实现变量传递。必须小心使用!!!

能解一元二次方程的函数 目前为止我们了解到的函数只能有一个返回值,由return 语句返回。 一个一元二次方程有二个解,如何让此函数返回二个解。答案是采用指针作为函数的参数。 由调用程序准备好存放两个根的变量,将变量地址传给函数。在函数中将两个根的值分别放入这两个地址

函数原型 函数原型可设计为: 函数的调用 void SolveQuadratic(double a, double b, double c, double *px1, double *px2) 函数的调用 SolveQuadratic(1.3, 4.5, 2.1, &x1, &x2) SolveQuadratic(a, b, c, &x1, &x2) 函数的参数有两类:输入参数和输出参数。一般,输入参数用值传递,输出参数用指针传递。在参数表中,输入参数放在前面,输出参数放在后面。

原型的改进 并不是每个一元二次方程都有两个不同根,有的可能有两个等根,有的可能没有根。函数的调用者如何知道x1和x2中包含的是否是有效的解? 解决方案:让函数返回一个整型数。该整型数表示解的情况

完整的函数 int SolveQuadratic(double a,double b,double c, double *px1,double *px2) { double disc, sqrtDisc; if(a == 0) return 3; //不是一元二次方程 disc = b * b - 4 * a * c; if( disc < 0 ) return 2; //无根 if ( disc == 0 ) { *px1 = -b /(2 * a); return 1;} //等根 //两个不等根 sqrtDisc = sqrt(disc); *px1 = (-b + sqrtDisc) / (2 * a); *px2 = (-b - sqrtDisc) / (2 * a); return 0; }

函数的调用 int main() { double a,b,c,x1,x2; int result; cout << "请输入a,b,c: "; cin >> a >> b >> c; result = SolveQuadratic(a, b, c, &x1, &x2); switch (result) { case 0: cout << "方程有两个不同的根:x1 = " << x1 << " x2 = " << x2; break; case 1: cout << "方程有两个等根:" << x1; break; case 2: cout << "方程无根"; break; case 3: cout << "不是一元二次方程"; } return 0;

指针作为函数参数和返回值 指针作为函数参数 数组名作为函数参数 返回指针的函数 引用和引用传递 返回引用的函数

数组传递的进一步讨论 数组传递的本质是地址传递,因此形参和实参可以使用数组名,也可以使用指针。 数组传递是函数原型可写为: type fun(type a[], int size); 也可写为 type fun(type *p, int size); 但在函数内部,a和p都能当作数组使用 调用时,对这两种形式都可用数组名或指针作为实参 建议:如果传递的是数组,用第一种形式;如果传递的是普通的指针,用第二种形式

C++将数组名作为参数传递处理成指针的传递 #include <iostream> using namespace std; void f(int arr[], int k) {cout << sizeof(arr) << " " << sizeof(k) << endl; } void main() { int a[10]={1,2,3,4,5,6,7,8,9,0}; cout << sizeof(a) << endl; f(a,10); 输出: 40 4 即在main中,a是数组,占用了40个字节。而在函数f中,arr是一个指针

数组传递的灵活性 void sort(int p[ ] , int n) {...} main() {int a[100]; ... sort(a, 100); //排序整个数组 sort(a, 50); //排序数组的前50个元素 sort(a+50, 50); //排序数组的后50个元素 }

实例 设计一函数用分治法在一个整数数组中找出最大和最小值 具体方法是: 如果数组只有一个元素,则最大最小都是他。 如果数组中只有两个元素,则大的一个就是最大数,小的就是最小数。这种情况不需要递归。 否则,将数组分成两半,递归找出前一半的最大最小值和后一半的最大最小值。取两个最大值中的较大者作为最大值,两个最小值中的较小值作为最小值。

设计考虑 函数的参数是要查找的数组,传递一个数组要两个参数:数组名和数组规模 函数返回的是数组中的最大值和最小值,将它们作为指针传递的参数 查找数组的前一半就是递归调用本函数,传给他的参数是当前的数组名,数组的规模是原来的一半 查找数组的后一半也是递归调用本函数,传给它的参数是数组后一半的起始地址,规模也是原来的一半

伪代码 void minmax ( int a[ ] , int n , int *min_ptr , int *max_ptr) { switch (n) { case 1: 最大最小都是a[0]; case 2: 大的得放入*max_ptr,小的放入*min_ptr; Default: 对数组a的前一半和后一般分别调用minmax; 取两个最大值中的较大者作为最大值; 取两个最小值中的较小值作为最小值 }

void minmax ( int a[] , int n , int *min_ptr , int *max_ptr) { int min1 , max1 , min2 , max2; switch(n) { case 1: *min_ptr = *max_ptr = a[0]; return; case 2: if (a[0] < a[1] ) { *min_ptr = a[0]; *max_ptr= a[1]; } else { *min_ptr = a[1]; *max_ptr= a[0];} return; default: minmax( a, n/2, &min1, &max1 ); minmax( a + n/2, n - n / 2, &min2, &max2 ); if (min1 < min2) *min_ptr = min1; else *min_ptr = min2; if (max1 < max2) *max_ptr = max2; else *max_ptr = max1; }

指针作为函数参数和返回值 指针作为函数参数 数组名作为函数参数 返回指针的函数 引用和引用传递 返回引用的函数

返回指针的函数 函数的返回值可以是一个指针 返回指针的函数原型: 类型 *函数名(形式参数表); 当函数的返回值是指针时,返回地址对应的变量不能是局部变量。

实例 设计一个函数从一个字符串中取出一个子串。 原型设计: 返回值指针指向的空间必须在返回后还存在。这可以用动态字符数组 从哪一个字符串中取子串、起点和终点 返回值:字符串可以用一个指向字符的指针表示,所以函数的执行结果是一个字符串,表示一个字符串可以用一个指向字符的指针 返回值指针指向的空间必须在返回后还存在。这可以用动态字符数组

char *subString(char *s, int start, int end) { int len = strlen(s); if (start < 0 || start >= len || end < 0 || end >= len || start > end) { cout << "起始或终止位置错" << endl; return NULL; } char *sub = new char[end - start + 2]; strncpy(sub, s + start, end - start +1); sub[end - start +1] = '\0'; return sub;

指针作为函数参数和返回值 指针作为函数参数 数组名作为函数参数 返回指针的函数 引用和引用传递 返回引用的函数

引用传递 引用传递是地址传递的另一种更简单明了的实现方法 引用的概念 函数中的引用

C++中的引用 引用的定义:给一个变量取一个别名,使一个内存单元可以通过不同的变量名来访问。 例:int i; int &j=i; j是i的别名,i与j是同一个内存单元。 C++引入引用的主要目的是将引用作为函数的参数。

引用传递 引用传递是地址传递的另一种更简单明了的实现方法 引用的概念 函数中的引用

引用参数 C++引入引用的主要目的是将引用作为函数的参数。 指针参数 引用参数 void swap(int *m, int *n) { int temp; temp=*m; *m=*n; *n=temp; } 调用:swap(&x, &y) 引用参数 void swap(int &m, int &n) {int temp; temp=m; m=n; n=temp; } 调用:swap( x, y) 注意:实参必须是变量,而不能是一个表达式

调用swap(x,y)时,相当于发生了变量定义 int &m = x int &n = y 即,形式参数m和实际参数x共享一块空间,形式参数n和实际参数y共享一块空间。在swap函数中交换了m和n的值,就相当于交换了x和y的值。

指针作为函数参数和返回值 指针作为函数参数 数组名作为函数参数 返回指针的函数 引用和引用传递 返回引用的函数

返回引用的函数的主要用途 将函数用于赋值运算符的左边,即作为左值。 int a[] = {1, 3, 5, 7, 9}; int &index(int); //声明返回引用的函数 void main() { index(2) = 25; //将a[2]重新赋值为25 cout << index(2);} int &index(int j) { return a[j]; } //函数是a[j]的一个引用

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

指针数组与多级指针 指针数组 Main函数的参数 多级指针

指针数组 地址本身也是数据,他们也可以像其他数据一样组织成一个数组 一个数组,如果他的元素均为指针,则称为指针数组 一维指针数组的定义形式: 类型名 *数组名[数组长度]; 例如,char *String[10]; 定义了一个名为String的指针数组,该数组有10个元素,数组的每个成员是一个指向字符的指针

指针数组的应用 字符串可以用一个指向字符的指针表示,一组字符串可以用一个指向字符的指针数组来表示 例:写一个函数用二分法查找某一个城市在城市表中是否出现。用递归实现 关键问题: 城市表的存储:用指向字符的指针数组 查找时的比较:用字符串比较函数

//该函数用二分查找在cityTable中查找cityName是否出现 //lh和rh表示查找范围,返回出现的位置 Int binarySearch(char *cityTable[], int lh, int rh, char *cityName) {int mid, result; if (lh <= rh) { mid =(lh+rh)/2; result= strcmp(cityTable[mid], cityName); if (result == 0) return mid; //找到 else if (result > 0) return binarySearch(cityTable, lh, mid-1, cityName); else return binarySearch(cityTable, mid+1, rh,cityName); } return -1; //没有找到

函数的应用 #include <iostream> using namespace std; int binarySearch(char *cityTable[], int lh, int rh, char *cityName); int main() {char *string[10] = {"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii","jjj"}; char tmp[10];; while (cin >> tmp) cout << binarySearch(string, 0, 9, tmp) << endl; return 0; }

“aaa” “bbb” “ccc” “ddd” “eee” “fff” “ggg” “hhh” “iii” “jjj”

指针数组与多级指针 指针数组 Main函数的参数 多级指针

main函数的形参 如需要实现:copy a b之类的功能,可以用带有参数的main函数来实现 main函数有二个形式参数: int argc, char *argv[ ] argc – 参数的数目(包括命令名本身) argv[ ]—指向每个参数的指针,是一个指向字符串的指针数组

把参数传递给main() 假设生成的执行文件myprogram.exe #include <stdio.h> int main(int argc, char *argv[]) { int i; cout << "argc=“ << argc << endl; for(i=0; i<argc; ++i) cout << "argv[“ << i << “]=“ << argv[i] << endl; return 0; } 假设生成的执行文件myprogram.exe

把参数传递给main() 续 假设生成的执行文件myprogram.exe 在命令行输入:myprogram 输出结果:argc=1 argv[0]=myprogram 在命令行输入:myprogram try this 输出结果:argc=3 argv[1]=try argv[2]=this

main函数参数实例 编写一个求任意n个正整数的平均数的程序 如果该程序对应的可执行文件名为aveg,则可以在命令行中输入 表示求10、30、50、20和40的平均值,对应的输出为30。

设计考虑 将这些数据作为命令行的参数 由于每个数据都要转换,而且这个工作很独立,所以将它设计成一个函数 从argc得到数据的个数 从argv得到每一个数值,但注意数值是以字符串表示,要进行计算,必须把它转换成真正的数值 由于每个数据都要转换,而且这个工作很独立,所以将它设计成一个函数

字符串形式的数字转换到真正的数值 int ConvertStringToInt(char *s) { int num = 0; while(*s) { num = num * 10 + *s - '0'; ++s; } return num;

计算程序 int main(int argc, char *argv[]) { int sum = 0; for (int i = 1; i < argc; ++i) sum += ConvertStringToInt(argv[i]); cout << sum / (argc - 1) << endl; return 0; }

指针数组与多级指针 指针数组 Main函数的参数 多级指针

多级指针 指针指向的内容还是一个指针,称为多级指针 如有定义:char *string[10]; string是一个数组,数组元素可以通过指针来访问。如果p是指向数组string的某一个元素,那么p指向的内容是一个指向字符的指针,因此p就是一个多级指针。string也是一个多级指针,不过是一个常指针

多级指针的定义 两级指针:类型名 **变量名; 三级指针:类型名 ***变量名; 如:int **q; 表示q指向的内容是一个指向整型的指针。可以这样使用:int x=15, *p=&x; q = &p; 同样:char **s; 表示s指向的内容是一个指向字符的指针 q p 15 s “abcde”

多级指针的应用 可以用指向指针的指针访问指针数组的元素。如 #include <iostream> using namespace std; int main() { char *city[] = {"aaa", "bbb", "ccc", "ddd", "eee"}; char **p; for (p=city; p<city+5; ++p) cout << *p << endl; return 0; } 输出结果: aaa bbb ccc ddd eee

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

二维数组与指针 int a[3][4];等价于定义了3个变量 a a[1] a[2] a[0] 1 2 3 4 5 6 7 8 9 10 11 12

指向一维数组的指针 a[ ]是一个指针数组,它的每个元素是一个整型指针,指向每一行的第一个元素。a是一个指向一维数组的指针,指向a[ ]的第一个元素。对a加1,事实上是跳到下一行 指向一维数组的指针可以这样定义: 类型名 (*指针变量名)[一维数组的元素个数]; 注意:圆括号不能省略,如果省略了圆括号就变成了指针数组

等价于a[i][j]的表达式 a[i][j] *(a[i] + j) int a[3][5]; (*(a + i))[j]

用指向数组的指针输出二维数组a int (*p)[4], *q; for (p = a; p < a + 3; ++p) { //每一行 for (q = *p; q < *p+4; ++q) //每一列 cout << *q << '\t'; cout << endl; } 注意:如果输出a和a[0],这两个值是相同的。但是,这两个值的含义是不同的,前者是第0行的首地址,它的类型是指向由四个元素组成的一位数组的首地址,后者是第0行第一个元素的地址,它的类型是整型指针

动态的二维数组 方法一:用一维动态数组 方法二:用指向指针的指针,可以用a[i][j]访问 访问i行j列的元素转换成访问一维数组的第4*i+j个元素 方法二:用指向指针的指针,可以用a[i][j]访问 用指向指针的指针指向一个一维的指针数组 指针数组中的每个元素指向矩阵的每一行的第一个元素

int main() { int **a, i, j, k = 0; a = new int *[3]; for (i = 0; i < 3; ++i) a[i] = new int[4]; for (i = 0; i < 3; ++i) for (j = 0; j < 4; ++j) a[i][j] = k++; for (i = 0; i < 3; ++i) { cout << endl; for (j = 0; j < 4; ++j) cout << a[i][j] << '\t'; } for (i = 0; i < 3; ++i) delete [] a[i]; delete [] a; return 0;

第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 第7章 间接访问—指针 指针的概念 指针运算与数组 动态内存分配 字符串再讨论 指针作为函数参数和返回值 指针数组与多级指针 指向多维数组的指针 指向函数的指针

函数的指针和指向函数的指针变量 函数的指针:指向函数代码的起始地址 定义:返回类型 (*指针变量名)( ) ; 使用 : 赋值 定义:返回类型 (*指针变量名)( ) ; 使用 : 赋值 eg. int isdigit(int n, int k); {...} int (*p)(int, int ); p=isdigit; 引用:a=isdigit(n,k); a=(*p)(n,k); 或 a=p(n,k)

函数的指针的用途 菜单选择的实现 作为函数的参数

函数指针的应用 用函数指针的数组实现菜单选择 例如,在一个工资管理系统中有如下功能: 1。添加员工; 2。删除员工; 3。修改员工信息; 4。打印工资单; 5。打印汇总表; 6。退出。 在设计中,一般把每个功能设计成一个函数。如添加员工的函数为add,删除员工的函数为delete,修改员工信息的函数为modify,打印工资单的函数为printSalary,打印汇总表函数为printReport。主程序是一个循环,显示所有功能和它的编号,请用户输入编号,根据编号调用相应的函数。

int main() { int select; while(1) { cout << "1--add \n"; cout << "2--delete\n"; cout << "3--modify\n"; cout << "4--print salary\n"; cout << "5--print report\n"; cout << "0--quit\n"; cin >> select; switch(select) { case 0: return 0; case 1: add(); break; case 2: erase(); break; case 3: modify(); break; case 4: printSalary(); break; case 5: printReport(); break; default: cout << "input error\n"; }

利用指向函数的指针 int main() { int select; void (*func[6])() = {NULL, add, erase, modify, printSalary, printReport}; while(1) { cout << "1--add \n"; cout << "2--delete\n"; cout << "3--modify\n"; cout << "4--print salary\n"; cout << "5--print report\n"; cout << "0--quit\n"; cin >> select; if (select == 0) return 0; if (select > 5) cout << "input error\n"; else func[select](); }

函数指针的应用 把函数指针作为函数的参数 例:设计一个通用的快速排序函数,可以排序任何类型的数据 关键问题 如何表示要排序的数据:将快速排序设计成一个函数模板,将待排序的数据类型设计成模板参数 不同类型的数据有不同的比较方式:向排序函数传递一个比较函数来解决。

template <class T> void quicksort(T data[], int low, int high, int (*comp)(T, T)) { int mid; if (low >= high) return; mid = divide(data, low, high, comp); quicksort( data, low, mid-1, comp); quicksort( data, mid+1, high, comp); }

template <class T> int divide(T data[], int low, int high, int (*comp)(T, T)) { T k = data[low]; do { while (low < high && comp(data[high], k)>0) --high; if (low < high) { data[low] = data[high]; ++low;} while (low < high && comp(data[low], k) < 0) ++low; if (low < high) { data[high] = data[low]; --high;} } while (low != high); data[low] = k; return low; }

通用快速排序的应用 如果需要排序一组字符串,待排序的一组字符串保存在数组a中。a的定义如下: char *a[]={"aaa", "nnn", "rrr", "fff", "sss", "ggg", "ddd"}; 调用 quicksort(a, 0, 6, strcmp);

通用快速排序的应用 如果要排序一组整型数,则需要定义一个比较函数,如下所示: 如果整型数组a定义如下: 调用 int intcmp(int a, int b) { if (a == b) return 0; if (a < b) return -1; else return 1; } 如果整型数组a定义如下: int a[] = {7,9,4,3,8,1,2,5,6,0}; 调用 quicksort(b, 0, 9, intcmp);

总结 本章介绍了指针的概念 ,指针变量的定义、运算 采用指针,可以使数组有多种访问方式 将指针作为形式参数可以使一个函数与其调用函数共享数据 动态分配内存