Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


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

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

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

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

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

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

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

7 指针实例 如执行: *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; 因为我们永远不知道变量存储的 真实地址,而且程序每次运行变量地址可能都不同。

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

9 指针变量的使用 设有定义 执行语句: 执行语句: 执行语句: 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

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

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

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

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

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

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

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

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

18 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; }

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

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

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

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

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

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

25 动态内存分配与回收 //动态字符串的使用 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

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

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

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

29 #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; }

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

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

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

33 可改为: 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);

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

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

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

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

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

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

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

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

42 统计字符串中单词数的函数 #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;

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

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

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

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

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

48 函数原型 函数原型可设计为: 函数的调用 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) 函数的参数有两类:输入参数和输出参数。一般,输入参数用值传递,输出参数用指针传递。在参数表中,输入参数放在前面,输出参数放在后面。

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

50 完整的函数 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; }

51 函数的调用 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;

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

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

54 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 即在main中,a是数组,占用了40个字节。而在函数f中,arr是一个指针

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

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

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

58 伪代码 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; 取两个最大值中的较大者作为最大值; 取两个最小值中的较小值作为最小值 }

59 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; }

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

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

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

63 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;

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

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

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

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

68 引用参数 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) 注意:实参必须是变量,而不能是一个表达式

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

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

71 返回引用的函数的主要用途 将函数用于赋值运算符的左边,即作为左值。 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]的一个引用

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

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

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

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

76 //该函数用二分查找在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; //没有找到

77 函数的应用 #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; }

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

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

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

81 把参数传递给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

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

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

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

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

86 计算程序 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; }

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

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

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

90 多级指针的应用 可以用指向指针的指针访问指针数组的元素。如 #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

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

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

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

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

95 用指向数组的指针输出二维数组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行第一个元素的地址,它的类型是整型指针

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

97 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;

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

99 函数的指针和指向函数的指针变量 函数的指针:指向函数代码的起始地址 定义:返回类型 (*指针变量名)( ) ; 使用 : 赋值
定义:返回类型 (*指针变量名)( ) ; 使用 : 赋值 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)

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

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

102 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"; }

103 利用指向函数的指针 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](); }

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

105 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); }

106 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; }

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

108 通用快速排序的应用 如果要排序一组整型数,则需要定义一个比较函数,如下所示: 如果整型数组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);

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


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

Similar presentations


Ads by Google