60
Chap 11 指指指指 11.1 指指指指指指指 11.2 指指指指指 11.3 指指指指指指指指指指指 1

Chap 11 指针进阶

  • Upload
    darren

  • View
    99

  • Download
    0

Embed Size (px)

DESCRIPTION

Chap 11 指针进阶. 11.1 布袋中的彩色球 11.2 解密藏头诗 11.3 学生信息管理的链表实现. 本章要点. 指针数组和指向指针的指针是如何被定义和使用的? 指针如何作为函数的返回值? 指向函数的指针的意义是什么? 什么是结构的递归定义,哪种应用需要这种定义方法? 对链表这种数据结构,如何进行动态内存分配操作? 如何建立单向链表并实现插入、删除以及查找操作?. 11.1 布袋中的彩色球. 11.1.1 程序解析 11.1.2 指针数组的概念 11.1.3 指向指针的指针 11.1.4 用指针数组处理多个字符串 - PowerPoint PPT Presentation

Citation preview

Page 1: Chap 11   指针进阶

Chap 11 指针进阶

11.1 布袋中的彩色球

11.2 解密藏头诗

11.3 学生信息管理的链表实现

1

Page 2: Chap 11   指针进阶

本章要点 指针数组和指向指针的指针是如何被定义和使用的? 指针如何作为函数的返回值? 指向函数的指针的意义是什么? 什么是结构的递归定义,哪种应用需要这种定义方

法? 对链表这种数据结构,如何进行动态内存分配操作? 如何建立单向链表并实现插入、删除以及查找操作?

2

Page 3: Chap 11   指针进阶

11.1 布袋中的彩色球

11.1.1 程序解析11.1.2 指针数组的概念11.1.3 指向指针的指针11.1.4 用指针数组处理多个字符串11.1.5 命令行参数

3

Page 4: Chap 11   指针进阶

11.1.1 程序解析例 11-1 已知一个不透明的布袋里装有红、

蓝、黄、绿、紫同样大小的圆球各一个,现从中一次抓出两个,问可能抓到的是什么颜色的球?

4

Page 5: Chap 11   指针进阶

程序解析-例 11-1 源程序#include<stdio.h>int main(void){ char *color[5] = {"red", "blue", "yellow", "green", "purple"}; /*

初始化 */ int count = 0, i, j; for(i = 0; i <= 4; i++) /* i 代表第一个球对应的颜色下标 */ for(j = i+1; j <= 4; j++) { /* j 代表第二个球对应的颜色下标 */ /* 两个球不能同色 */ count ++; printf("%6d", count); printf("%10s %10s\n", color[i], color[j]); } return 0;}

1 red blue 2 red yellow 3 red green 4 red purple 5 blue yellow 6 blue green 7 blue purple 8 yellow green 9 yellow purple 10 green purple

指针数组

char *color[5];color[0] = “red”; color[1] = “blue”; …

5

Page 6: Chap 11   指针进阶

11.1.2 指针数组的概念char *color[5];

类型名 * 数组名 [ 数组长度 ]数组元素是指针类型,用于存放内存地址

int a[5]; a 是一个数组,它有 5 个元素 每个元素的类型都是整型

char *color[5]; color 是一个数组,它有 5 个元素 每个元素的类型都是字符指针

6

Page 7: Chap 11   指针进阶

char *color[5] = {"red", "blue", "yellow", "green", "purple"}; color 是一个数组,它有 5 个元素 每个元素的类型都是字符指针 数组元素可以处理字符串

对指针数组元素的操作相当于对同类型指针变量的操作printf("%10s %10s\n", color[i], color[j]);

指针数组的概念

7

Page 8: Chap 11   指针进阶

#include <stdio.h> int main(void){ int i; char *color[5] = {"red", "blue", "yellow", "green", "purple"}, *tmp; for(i = 0; i < 5; i++) /* 输出字符串的地址和内容 */ printf("%x, %s\n", color[i], color[i]);

tmp = color[0]; /* 交换 color[0] 与 color[4]*/ color[0] = color[4]; color[4] = tmp; printf("color[0]:%s, color[4]:%s\n", color[0], color[4]); return 0;}

420064, red42005c, blue420054, yellow42004c, green420044, purple

例 11-2 使用指针数组输出 5 种颜色的英文名称

color[0]:purple, color[4]:red

8

Page 9: Chap 11   指针进阶

交换 color[0] 与 color[4] 的值

例 11-2 图示

9

Page 10: Chap 11   指针进阶

11.1.3 指向指针的指针-示例例 11-3 改写例 11-1 ,用指向指针的指针实现。 #include<stdio.h>int main(void){ char *color[5] = {"red", "blue", "yellow", "green", "purple"}; char **pc = color; int count = 0, i, j;

for(i = 0; i <= 4; i++) for(j = i+1; j <= 4; j++) { count++; printf( "%6d", count ); printf( "%10s %10s\n", color[i], color[j] ); } return 0;}

指向指针的指针

pc[i], pc[j] );或*(pc + i), *(pc + j)

10

Page 11: Chap 11   指针进阶

指向指针的指针-示例分析char *color[5] = {"red", "blue", "yellow", "green", "purple"};char **pc = color;printf( "%10s %10s\n", *(pc + i), *(pc + j) );

pc: color 或者 &color[0]

*pc: color[0]: "red"

*(pc+i) : color[i]

*color[0]: 'r'

**pc: *color[0]: 'r'

11

Page 12: Chap 11   指针进阶

指向指针的指针-定义指向指针的指针(二级指针) 类型名 ** 变量名

int a = 10;

int *pa = &a;

int **ppa = &pa;

&a

pa a10&p

ppa

*pa

*ppa **ppa

12

Page 13: Chap 11   指针进阶

&a

pa a

10&pa

ppa**ppa*pa

&b

pb b

20&pb

ppb**ppb*pb

int a = 10, b = 20, t;

int *pa = &a, *pb = &b, *pt;

int **ppa = &pa, **ppb = &pb, **ppt;

例 11-4

操 作 (1) : ppt = ppb; ppb = ppa; ppa = ppt;

13

Page 14: Chap 11   指针进阶

pa appa

&a 10&pb**ppb*pa

&b

pb b

20&pa

ppb**ppa*pb

int a = 10, b = 20, t;

int *pa = &a, *pb = &b, *pt;

int **ppa = &pa, **ppb = &pb, **ppt;

例 11-4

操 作 (1) : ppt = ppb; ppb = ppa; ppa = ppt; 操作 (2) : pt = pb; pb = pa; pa = pt; ?

14

Page 15: Chap 11   指针进阶

pa appa

&b 10&pb**ppa*pb

&a

pb b

20&pa

ppb**ppb*pa

int a = 10, b = 20, t;

int *pa = &a, *pb = &b, *pt;

int **ppa = &pa, **ppb = &pb, **ppt;

例 11-4

操 作 (1) : ppt = ppb; ppb = ppa; ppa = ppt; 操作 (2) : pt = pb; pb = pa; pa = pt;

操作 (3) : t = b; b = a; a = t; 15

Page 16: Chap 11   指针进阶

pa appa

&b 20&pb**ppa*pb

&a

pb b

10&pa

ppb**ppb*pa

int a = 10, b = 20, t;

int *pa = &a, *pb = &b, *pt;

int **ppa = &pa, **ppb = &pb, **ppt;

例 11-4

操 作 (1) : ppt = ppb; ppb = ppa; ppa = ppt; 操作 (2) : pt = pb; pb = pa; pa = pt;

操作 (3) : t = b; b = a; a = t; 16

Page 17: Chap 11   指针进阶

11.1.4 用指针数组处理多个字符串 处理多个字符串

二维字符数组char ccolor[ ][7] = {"red",

"blue", "yellow", "green", "purple"};

指针数组char *pcolor[ ] = {"red", "blue",

"yellow", "green", "purple"};

使用指针数组更节省内存空间

17

Page 18: Chap 11   指针进阶

1. 用指针数组处理多个字符串-排序例 11-5 将 5 个字符串从小到大排序后输出。#include <stdio.h>

int main(void)

{ int i;

int a[5] = {6, 5, 2, 8, 1};

void fsort( int a[ ], int n );

fsort( a, 5 );

for(i = 0; i < 5; i++)

printf("%d ", a[i]);

return 0;

}

#include <stdio.h>

int main(void)

{ int i;

char *pcolor[5]={ "red", "blue", "yellow", "green", "purple" };

void fsort(char *color[ ], int n);

fsort( pcolor, 5 );

for(i = 0; i < 5; i++)

printf("%s ", pcolor[i]);

return 0;

} 18

Page 19: Chap 11   指针进阶

例 11-5 字符串排序void fsort(int a[ ], int n){ int k, j; int temp; for(k = 1; k < n; k++) for(j = 0; j < n-k; j++) if(a[j] > a[j+1]){ temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; }}

void fsort(char *color[ ], int n)

{ int k, j;

char *temp;

for(k = 1; k < n; k++)

for(j = 0; j < n-k; j++)

if(strcmp(color[j],color[j+1])>0){

temp = color[j];

color[j] = color[j+1];

color[j+1] = temp;

}

}

19

Page 20: Chap 11   指针进阶

pcolor[0]

pcolor[1]

pcolor[2]

pcolor[3]

pcolorred\0

blue\0

yellow\0

green\0

purple\0pcolor[4]

排序前

pcolor[0]

pcolor[1]

pcolor[2]

pcolor[3]

pcolor

pcolor[4]

排序后

red\0

blue\0

yellow\0

green\0

purple\0 20

Page 21: Chap 11   指针进阶

2. 动态输入多个字符串-示例 用动态分配内存的方法处理多个字符串的输入示例

例 11-6 输入一些球的颜色,以 # 作为输入结束标志,再输出这些颜色。

其中颜色数小于 20 ,颜色的英文名称不超过 10 个字符。

21

Page 22: Chap 11   指针进阶

#include <stdio.h>#include<stdlib.h>#include<string.h>int main(void){ int i, n = 0; char *color[20], str[10]; printf(" 请输入颜色名称,每行一个, # 结束输入: \n"); scanf("%s", str); while(str[0] != '#') { color[n] = (char *) malloc ( sizeof (char) * ( strlen(str) + 1 ) ); strcpy(color[n], str);

n++; scanf("%s", str); } printf(" 你输入的颜色是: "); for(i = 0; i < n; i++) printf("%s ", color[i]); return 0;}

请输入颜色名称,每行一个, # 结束输入:redblue yellow#你输入的颜色是: red blue yellow

for( i=0; i<20; i++) color[i] = NULL;

for( i=0; i<n; i++) free( color[i] );

22

Page 23: Chap 11   指针进阶

3. 对指针数组的进一步讨论char *color[ ] = {"red", "blue", "yellow", "green", "purple"};

color :二级指针 (char **) ,等于 &color[0]

color+2 :指向 color[2] *(color+2) 和 color[2] 等价

color[0] :指向字符串 "red" 的首字符 rcolor[0]+2 :指向首字符 r 后的第 2 个字符 d

23

Page 24: Chap 11   指针进阶

对指针数组的进一步讨论(1) color[k]*(color+k) printf("%s", color[2]); printf("%s", *(color+2));

(2) *(color[k]+j) *(*(color+k)+j) color[k][j] printf("%c %c", *(color[2]), *(color[2]+2)); printf("%c %c", color[2][0], color[2][2]);

24

Page 25: Chap 11   指针进阶

11.1.5 命令行参数 C 语言源程序经编译和连接处理,生成可执行程序

后,才能运行。 在 DOS 环境的命令窗口中,输入可执行文件名,就

以命令方式运行该程序。 输入命令时,在可执行文件(命令)名的后面可以

跟一些参数,这些参数被称为命令行参数。 test world

命令名 命令行参数

25

Page 26: Chap 11   指针进阶

命令行参数命令名 参数 1 参数 2 … 参数 n

命令名和各个参数之间用空格分隔,也可以没有参数 使用命令行的程序不能在编译器中执行,需要将源程序经

编译、链接为相应的命令文件(一般以 .exe 为后缀),然后回到命令行状态,再在该状态下直接输入命令文件名。

26

Page 27: Chap 11   指针进阶

带参数的 main() 函数

第 1 个参数 argc 接收命令行参数 ( 包括命令名 ) 的个数 第 2 个参数 argv 接收以字符串常量形式存放的命令行参

数 ( 命令名本身也作为一个参数 )

#include <stdio.h> /* test.c */int main(int argc, char *argv[ ]){ printf("Hello "); printf("%s", argv[1]); return 0;}

test world!Hello world!

argc: 2*argv[ ]: {"test", "world!"}

27

Page 28: Chap 11   指针进阶

例 10-7 输出命令行参数例 11-7 编写 C 程序 echo ,它的功能是将所有命令行

参数在同一行上输出。 #include <stdio.h>

int main(int argc, char *argv[ ])

{ int k;

for(k = 1; k < argc; k++) /* 从第 1 个命令行参数开始 */

printf("%s ", argv[k]); /* 打印命令行参数 */

printf("\n");

return 0;

}

在命令行状态下输入:echo How are you?How are you?

28

Page 29: Chap 11   指针进阶

11.2 解密藏头诗

11.2.1 程序解析11.2.2 指针作为函数的返回值11.2.3 指向函数的指针

29

Page 30: Chap 11   指针进阶

11.2.1 程序解析-解密藏头诗

例 11-8 输入一首藏头诗(假设只有 4句),输出其真实含义。藏头诗:将这首诗每一句的第一个字连起来,所组

成的内容就是该诗的真正含义。

30

Page 31: Chap 11   指针进阶

11.2.1 程序解析#include <stdio.h>char *change(char s[ ][20], char

t[ ]);int main(void){ int i; char s[4][20], t[10], *p; printf(“ 请输入藏头诗: \n”); for(i = 0; i < 4; i++) scanf("%s", s[i]); p = change(s, t); printf("%s\n", p); return 0;}

请输入藏头诗:一叶轻舟向东流,帆梢轻握杨柳手,风纤碧波微起舞,顺水任从雅客悠。一帆风顺

char * change(char s[ ][20], char t[ ])

{

int i;

for(i= 0; i < 4; i++) {

t[2*i] = s[i][0];

t[2*i+1] = s[i][1];

}

t[2*i] = '\0';

return t;

}

函数的返回值是字符指针

printf("%s\n", change(s, t) );或change(s, t);printf("%s\n", t);

31

Page 32: Chap 11   指针进阶

11.2.2 指针作为函数的返回值 函数返回值的类型

整型、字符型、浮点型、结构类型指针(返回一个地址)

函数的定义、调用方法与其他函数一样

32

Page 33: Chap 11   指针进阶

指针作为函数的返回值-例 11-9输入一个字符串和一个字符,如果该字符在字符串中,

就从该字符首次出现的位置开始输出字符串中的字符。

要求定义函数 match(s, ch) ,在字符串 s 中查找字符ch ,如果找到,返回第一次找到的该字符在字符串中的位置(地址);否则,返回空指针 NULL 。

例如,输入字符 r 和字符串 program 后,输出rogram 。

33

Page 34: Chap 11   指针进阶

例 11-9 源程序

#include <stdio.h> char *match(char *s, char ch){ while(*s != '\0') if(*s == ch) return(s); /* 若找到字符 ch ,返回相应的地址 */ else s++; return(NULL); /* 没有找到 ch ,返回空指针 */}int main(void ){ char ch, str[80], *p = NULL; printf(“Please Input the string:\n”); scanf("%s", str); getchar( ); ch = getchar( ); if( ( p = match(str, ch) ) != NULL ) printf("%s\n", p); else printf("Not Found\n"); return 0;}

Please Input the string:University vversity

字符指针 p接收match返回的地址,从 p指向的存储单元开始,连续输出其中的内容,直至 '\0'为止。

Please Input the string:school aNot Found

34

Page 35: Chap 11   指针进阶

指针作为函数的返回值的进一步讨论 函数返回值的类型

整型、字符型、浮点型、结构类型指针(返回一个地址)

函数的定义、调用方法与其他函数一样 进一步讨论

定义函数时,可以: 动态分配内存 操作这些新分配的单元 返回新分配单元的地址

修改例 11-8,采用动态分配内存的方法

35

Page 36: Chap 11   指针进阶

例 11.8 修改-动态分配内存#include <stdio.h>

#include <stdlib.h>char *change_d(char s[ ][20]);int main(void){ int i; char s[4][20], *p = NULL;

printf(" 请输入藏头诗: \n"); for(i = 0; i < 4; i++) scanf("%s", s[i]); p = change_d(s); printf("%s\n", p); free(p); return 0;}

char * change_d(char s[ ][20]){ int i; char *head, *p;

p = (char *) calloc(8, sizeof(char)); head = p; for(i= 0; i < 4; i++) { *(p++) = s[i][0]; *(p++) = s[i][1]; } *p = '\0';

return head;}

36

Page 37: Chap 11   指针进阶

11.2.3 指向函数的指针 每个函数都占用一段内存单元,

有一个入口地址(起始地址) 函数名:函数的入口地址 函数指针:一个指针变量,接收

函数的入口地址,让它指向函数通过函数指针调用函数做为函数的参数

指令 1

指令 2

指令 3

指令 n

入口地址

37

Page 38: Chap 11   指针进阶

函数指针的定义和赋值 定义类型名 (* 变量名 )( );

所指向函数的返回值的类型 int (*funptr)( ); 定义一个函数指针 funpt funpt 指向一个返回值类型为 int 的函数

赋值funptr = fun; 函数 fun 的入口地址赋给 funptr funptr 指向函数 fun

int fun(x ,y){ return x > y ? x : y;}

38

Page 39: Chap 11   指针进阶

通过函数指针调用函数int (*funptr)( );funptr = fun;调用函数

函数名z = fun(3, 5);函数指针(*funptr)(3, 5);

(* 函数指针名 )( 参数表 )

int fun(x ,y){ return x > y ? x : y;}

39

Page 40: Chap 11   指针进阶

函数指针做为函数的参数 实参:函数名或已赋值的函数指针 形参:函数指针,指向实参所代表函数的入口地址例 11-10 编写一个函数 calc(f, a, b) ,用梯形公式求函数 f(x)

在 [a, b]上的数值积分。

然后调用 calc(f, a, b)计算下列数值积分。

分析: 函数定义时,形参:函数指针 f 、积分区间上下限参数

a , b 函数调用时,实参:被积函数的名称(或函数指针)和积分区间的上下限

40

Page 41: Chap 11   指针进阶

例 11-10 源程序double f1 ( double x )

{ return (x*x); }double f2 ( double x ){ return (sin(x)/x); }double calc ( double (*f)(double), double a, double b ) { double z; z = (b-a)/2 * ( (*f)(a) + (*f)(b) ); /* 调用 f 指向的函数 */ return ( z );}int main ( void ){ double result; result = calc(f1, 0.0, 1.0); /* 函数名 f1 作为函数 calc 的实参 */ printf("1: resule=%.4f\n", result); funp = f2; result = calc(funp, 1.0, 2.0); /* 函数指针 funp 作为函数 calc 的实参

*/ printf("2: resule=%.4f\n", result); return 0;}

1: resule=0.50002: resule=0.6481

41

Page 42: Chap 11   指针进阶

11.3 学生信息管理的链表实现

11.3.1 程序解析

11.3.2 链表的概念

11.3.3 单向链表的常用操作

42

Page 43: Chap 11   指针进阶

11.3.1 程序解析

例 11-11 建立一个学生成绩信息(包括学号、姓名、成绩)的单向链表,学生记录按学号由小到大顺序排列,要求实现对成绩信息的插入、修改、删除和遍历操作。

main

InsertDoc DeleteDoc Print_Stu_Doc Create_Stu_Doc

InsertDoc

head 9905 Qian 80 NULL9901 Wang 80 9902 Li 90

43

Page 44: Chap 11   指针进阶

例 11-11 数据定义与函数声明 struct stud_node{ /* 链表结点类型 */ int num; char name[20]; int score; struct stud_node *next;};struct stud_node * Create_Stu_Doc(); /* 新建链表 */

struct stud_node * InsertDoc(struct stud_node * head, struct stud_node *stud); /* 插入 */

struct stud_node * DeleteDoc(struct stud_node * head, int num); /* 删除 */

void Print_Stu_Doc(struct stud_node * head); /* 遍历 */44

Page 45: Chap 11   指针进阶

11.3.2 链表的概念

一种动态存储分布的数据结构若干个同一结构类型的“结点”依次串接而成 单向链表、双向链表

头指针 结点 尾结点

head 9903 Qian 80 NULL9901 Wang 80 9902 Li 90

45

Page 46: Chap 11   指针进阶

链表的概念-结点定义

struct stud_node{ int num; char name[20]; int score;

struct stud_node *next;};

结构的递归定义

head 9905 Qian 80 NULL9901 Wang 80 9902 Li 90

46

Page 47: Chap 11   指针进阶

链表的概念-与数组比较 数组

事先定义固定长度的数组在数组元素个数不确定时,可能会发生浪费内存

空间的情况

链表动态存储分配的数据结构根据需要动态开辟内存空间,比较方便地插入新

元素(结点)使用链表可以节省内存,提高操作效率

47

Page 48: Chap 11   指针进阶

动态存储分配函数 malloc()void *malloc(unsigned size)

在内存的动态存储区中分配一连续空间,其长度为size

若申请成功,则返回一个指向所分配内存空间的起始地址的指针

若申请不成功,则返回 NULL (值为 0 )返回值类型: (void *)

通用指针的一个重要用途 将 malloc 的返回值转换到特定指针类型,赋给一个指

针 48

Page 49: Chap 11   指针进阶

malloc() 示例int *ip = (int *) malloc( sizeof(int) )

struct student * p;

p = (struct student *) malloc(sizeof(struct student));

调用 malloc 时,用 sizeof 计算存储块大小 虽然存储块是动态分配的,但它的大小在分配后也是确定的,不要越界使用。

p

49

Page 50: Chap 11   指针进阶

动态存储释放函数 free 当某个动态分配的存储块不再用时,要及时

将它释放void free(void *ptr) 释放由动态存储分配函数申请到的整块内存空间,

ptr 为指向要释放空间的首地址。

free(ip);free(p); p

50

Page 51: Chap 11   指针进阶

11.3.3 单向链表的常用操作

1. 链表的建立2. 链表的遍历3. 插入结点4. 删除结点

51

Page 52: Chap 11   指针进阶

1. 链表的建立struct stud_node *head, *tail, *p;head = tail = NULL;size = sizeof(struct stud_node);p = (struct stud_node *) malloc(size);

head

tail

tail

pnum name score next

p

52

Page 53: Chap 11   指针进阶

1. 链表的建立

53

Page 54: Chap 11   指针进阶

程序段 head = tail = NULL; scanf("%d%s%d", &num,name, &score); while(num != 0){ p = (struct stud_node *) malloc(size);

p->num = num; strcpy(p->name, name); p->score = score; p->next = NULL;

if(head == NULL) head = p; else tail->next = p; tail = p; scanf("%d%s%d", &num, name, &score); }

54

Page 55: Chap 11   指针进阶

2. 链表的遍历

ptr->num

ptr->score

ptr ptr

for(ptr = head; ptr != NULL; ptr = ptr -> next)

printf("%ld, %d", ptr -> num, ptr -> score);

ptr=ptr->next

head 9905 Qian 80 NULL9901 Wang 80 9902 Li 90

55

Page 56: Chap 11   指针进阶

链表的遍历-函数void Print_Stu_Doc(struct stud_node * head) { struct stud_node * ptr; if(head == NULL){ printf("\nNo Records\n"); return; } printf("\nThe Students' Records Are: \n"); printf(" Num Name Score\n"); for(ptr = head; ptr!=NULL; ptr = ptr->next) printf("%8d %20s %6d \n", ptr->num, ptr->name, ptr->score);

}

56

Page 57: Chap 11   指针进阶

s->next = ptr->next

ptr->next = s

先连后断

head

ptr

s

3. 插入结点

57

Page 58: Chap 11   指针进阶

ptr->next = s

s->next = ptr->next

行吗?

head

ptr

s

3. 插入结点

58

Page 59: Chap 11   指针进阶

ptr->next = s

s->next = ptr->next

行吗?

head

ptr

s

3. 插入结点

老大不高兴后果很严重

59

Page 60: Chap 11   指针进阶

ptr2=ptr1->next

ptr1->next=ptr2->next

head

ptr1 ptr2

free(ptr2)

先接后删

4. 删除结点

ptr2

head

ptr1

60