当前位置:首页 >> 其它课程 >>

C语言程序设计教学做一体化模块8_图文

C语言程序设计 教学做一体化
主编 滕泓虬

中国水利水电出版社

模块八

指针

正确而灵活地使用指针,可以表示各 种数据结构,能很方便地使用数组和 字符串,从而可以使程序简洁、高效。

8.1 指针的概念

学习目标 1、理解指针和地址的关系; 2、掌握指针的概念和作用。

案例 8-1

定义一个变量,再定义一个指针指向该变量。

案例分析

? 1、定义如下: ? char ch=?a?; ? char *p=&ch; ? 2、语句char ch=?a?;表示定义了一个字符变量,变 量名为ch,变量的值为字符常量‘a?。定义后, 在编译时系统会为该字符变量分配一个内存单元, 假设该内存单元编号是2000。这里,单元编号 2000就是该内存单元的地址,就是变量ch的地址, 字符常量‘a?就是该内存单元的内容,也就是变 量ch的值。

p

ch ‘a?

2000

? 3、如果执行scanf(“%c”,&ch);语句,实际上就是 把键盘输入的字符值(ASCII码值)送到变量ch的 地址(为2000)对应的内存单元中。 ? 4、前面都是通过变量名来访问变量的内容,也就 是变量对应的内存单元的值,那在理解了内存单 元地址和内容的关系之后,可不可以定义一种变 量来保存内存单元的地址,从而达到访问内存单 元的内容的目的呢?

指针的定义

? 定义一种变量专门用来保存内存单元的地 址,地址称为指针,那么这种保存地址的 变量就被称为指针变量

知识链接

1、指针的概念 2、地址和内容的关系 提示: 1、要正确区分变量名、变量地址、变量内容三个概 念。变量名就是给变量取的名字,变量地址就是 系统给变量分配的内存单元的起始地址编号,变 量内容就是对应内存单元中存放的数据。 2、指针本质上就是所要指向数据类型的地址。 指针=地址

8.2 指针和指针变量

学习目标 1、掌握指针变量的定义和初始化; 2、掌握指针变量的引用

8.2.1 指针变量的定义

案例8-2 定义指针变量并解释其意义。

案例分析

1、定义:int *p;表示定义一个指向整型数据的指 针。类型说明符int表示指针指向整型变量,(*) 表示变量p是指针变量,它保存的是某个整型变量 的地址(指针),p是指针变量名。 2、又如: float *pointer; /*定义指针变量 pointer,指向浮点型变量*/ char *p_char; /*定义指针变量 p_char,指向字符型变量*/

知识链接

1、指针变量的定义 2、指针变量的一般格式

8.2.2 指针变量初始化

案例8-3 区分下列两种指针变量初始化方法。 1、int *p; p=&i; 2、int *p=&i;

案例分析

1、定义指针变量后进行初始化 定义指针变量后再进行初始化的一般格式是: 指针变量名= &所指向的变量名

p
&i

i

指针的定义

2、定义的同时进行初始化 ? 例如: ? int i, j; ? int *p=&i, *q=&j;

8.2.3 指针变量的引用

案例8-4 定义一个指向整型变量的指针,并进行初始 化和引用,最后输出变量的地址和指针变 量本身的值。

案例程序
main() { int a, *p; /*定义整型变量a和整型指针p*/ a=117; /*给变量a赋值*/ p=&a; /*把a的地址保存在p中,使p指向边变量a */ printf("a=%d, *p=%d\n",a,*p); /*输出a的值和p指针引用的值*/ *p=218; printf("a=%d,*p=%d\n", a, *p); /*再次输出a的值和p指针引用的值*/ printf("The address of a is:%ld\n",(long)&a); /*输出变量a的内存地 址*/ printf("The address of a is:%ld\n",(long)p); /*输出变量a的内存地址*/ }

运行程序,结果是: a=117, *p=117 a=218, *p=218 The address of a is:1693056984 The address of a is:1693056984

知识链接

用变量名访问和用指针变量访问的比较 char ch; char *p=&ch; printf(“%c”, ch); 或 printf(“%c”, *p);

提示

提示:注意指针的定义和引用的区别,定义 时的形式包含类型说明符,比如char *p; 这 里的“*”表示p是一个指针变量,而引用 时的形式不包含类型说明符,比如刚才 printf语句中的“*p”,这里的“*”实际上 是运算符,称为指针运算符,它的作用相 当于引用指针所指向的变量,或者说是引 用指针所指向的地址空间的内容。

案例8-5

从键盘输入两个整数,通过指针引用,把两 个数由大到小输出到屏幕上。

案例程序
main() {

int a,b; int *p1, *p2, *p; clrscr(); p1=&a; p2=&b;

/*定义两个整型变量*/ /*定义三个指向整型变量的指针*/

/*p1指向a,p2指向b*/

scanf("%d%d",p1,p2); /*输入a和b的值*/ printf("*p1=%d,*p2=%d\n",*p1,*p2); /*输出p1和p2所指向变量的值*/ if(a<b) { p=p1; p1=p2; p2=p; /*如果a<b,则交换指针变量p1和p2的值*/ } printf("a=%d,b=%d\n",a,b); /*输出变量a和b的值*/ printf("*p1=%d, *p2=%d\n",*p1,*p2); /*输出p1和p2所指向变量的值*/ }

运行程序,键盘输入:38 49 ↙ 输出结果是: *p1=38, *p2=49 a=38, b=49 *p1=49, *p2=38

案例分析

p1 &a p2 &b
交换前

a 38 b 49

p1 &b p2 &a
交换后

a 38 b 49

随堂练习

定义三个整型变量和三个指向整型数据的指 针,用指针指向三个整型变量,并按由大 到小输出三个变量的值。

8.2.4 指针变量作为函数的参数

案例8-6 用指针变量作为函数参数改变某个变量的值

案例程序
changevalue(int *pt) 的值*/ { *pt=555; } main() { int a=10; 10*/ int *p=&a; 的地址*/ changevalue(p); printf("a=%d\n",a); } 运行程序,结果是: a=555 /*函数changevalue()的功能是改变变量a /*改变指针变量所指向内存单元的值*/

/*定义整型变量a并赋初值为 /*定义整型指针变量p并赋初值为变量a /*调用函数用指针p作为参数*/ /*调用后输出a的值*/

案例分析

p &a pt

a 555 *p *pt

案例8-7

用变量名作为函数参数改变某个变量的值

案例程序
void changevalue(int b) 能是改变变量a的值*/ { printf("b=%d\n",b); b=555; } main() { int a=10; 为10*/ changevalue(a); printf("a=%d\n",a); } /*函数changevalue()的功 /*输出形参b的值*/ /*变量b的值改为555*/

/*定义整型变量a并赋初值
/*调用函数用变量a作为参数*/ /*调用后输出a的值*/

? 运行程序,结果是: ? b=10 ? a=10

案例分析

a

10

b

555

案例8-8

定义一个函数,实现两个整型变量值的交换

案例程序 void swap(int *pa,int *pb) /*函数swap(),用于交 换变量a和b的值*/ { int med; /*定义整型变量med, 用于做交换的中间存储*/ med=*pa; /*把变量a的值先保存在临时 辅助(中间存储)变量med中*/ *pa=*pb; /*交换pa和pb所指向变 量的值*/ *pb=med; }

main() { int a=11,b=22; /*定义两个整型变量a和b*/ int *p1,*p2; /*定义两个指针变量p1和p2*/ p1=&a; p2=&b; /*p1和p2初始化为变量a和b的地址*/ swap(p1,p2); /*调用swap,实现变量a和b的值 的互换,用指针作为实参*/ printf("a=%d, b=%d\n", a,b); /*输出a和b的值*/ printf("*p1=%d,*p2=%d\n",*p1,*p2); /*输出p1和 p2所指向变量的值*/ } ? 运行程序,结果是: ? a=22, b=11 ? *p1=22, *p2=11

案例分析

p1、pa &a p2、pb &b
交换前

a 11 b 22

p1、 pa &a p2、 pb &b
交换后

a 22 b 11

如果把swap()函数改成下面的代码,请考虑能否实 现a和b互换。 void swap(int *pa, int *pb) { int *temp; temp=pa; pa=pb; pb=temp; }

p1 &a p2 &b

a 11 b 22

pa
p1 a 11 b 22 pa &b pb &a

&a pb &b

&a p2 &b

随堂练习

? 1、编程从键盘输入两个数,用函数调用的 方式(用指针作为函数参数),实现两个 数由大到小排序。 ? 2、编程从键盘输入三个数,用函数嵌套调 用的方式,先调用三个数交换的函数,再 在三个数交换的函数中调用两个数交换的 函数(调用过程中都用指针作为函数参 数)。

知识链接

函数的参数传递,有两种方式,一种是传值 方式,另一种是传址方式。对于变量来说, 传值方式就是把变量的值作为参数传递, 比如用变量名作为参数就是这种方式;传 址方式就是把变量的地址作为参数传递, 比如用指针指向某一变量,然后用指针作 为参数,另外,用数组名作为参数也是传 址方式。

8.3 指针与数组
学习目标 掌握指向数组的指针以及指针和数组名的关系。 掌握指向数组元素的指针以及通过指针引用数组元素。 指针和数组之间存在特殊的关系。一个变量有一个地址,一 个数组包含若干个元素,每个数组元素都在内存中占有 相应的内存单元,都有相应的地址。因此指针变量可以 指向数组,也可以指向数组元素。所谓数组的指针就是 数组的起始地址,数组元素的指针就是数组元素的地址。 我们把指向数组的指针变量称为“指向数组的指针”, 把指向数组元素的指针变量称为“指向数组元素的指 针”。

8.3.1 指向数组的指针

案例8-9 定义一个数组,然后定义一个指针指向该数 组。

案例分析

1、一个数组是由连续的一块内存单元组成的。 数组名就是这块连续内存单元的首地址。 数组的元素按顺序存储在内存单元中,它 的第一个元素(如array[0])存储在首地址 对应的内存单元中,后面的数组元素(下标 大于0)存储在后续的内存单元中。例如定义 一个整型数组: int array[5];

1、一个数组是由连续的一块内存单元组成的。 数组名就是这块连续内存单元的首地址。 数组的元素按顺序存储在内存单元中,它 的第一个元素(如array[0])存储在首地址 对应的内存单元中,后面的数组元素(下标 大于0)存储在后续的内存单元中。例如定义 一个整型数组: int array[5];

假定array数组的起始地 址是1000,存放形式 如图
1000 1002 1004 1006 1008 array[0] array[1] array[2] array[3] array[4] array

提问:请考虑下面的关系是否成立? (1)array==1000 (2)&array[0]==1000 (3)array==&a[0] (4)&array[1]==1002

2、在清楚了数组在内存中的存储形式以后,下一步我们就 可以定义指针来指向数组了。在前面已经定义了数组 array[],再定义指针p指向数组array。 int array[5]; int *p; /*定义p为指向整型变量的 指针*/ 这里定义指针p为整型的原因就是因为数组array是int型,所 以p也应为指向int型的指针变量。为了使指针p指向数组 array,需要把数组的起始地址赋给指针变量p,以下两种 方法都是等价的。 (1)p=&array[0]; (2)p=array;

也可以在定义的同时赋初值给指针变量p: (1)int *p=&array[0]; /*求得第 一个元素array[0]的地址赋给p*/ (2)int *p=array; /*数组名 a相当于数组的首地址,赋给p*/

提示: (1)指针变量经过定义和赋初值以后,由于p的初 值被赋为数组的起始地址,所以指针变量p、数组 名array、&array[0]三者的值都是1000,如图8-15 所示。指针变量p、数组名array、&array[0]均指向 同一单元,它们是数组的首地址,也就是数组的 第一个元素array[0]的地址。 (2)注意数组名和数组元素的地址是常量,而指针 变量是变量。

知识链接

1、指针变量指向数组首地址的方式如下: 方式1:类型说明符 *指针变量=数组名(或&数组名 [0]); /*在定义指针的同时赋初值*/ 方式2:指针变量=数组名(或&数组名[0]); /*在定义了指针变量后*/ 2、指针变量指向数组某个元素的方式如下: 方式1:类型说明符 *指针变量=&数组名[下标]; /*在定义指针的同时赋初值*/ 方式2:指针变量=&数组名[下标]; /*在定义了指针变量后*/

8.3.2 通过指针引用数组元素

有两种方法可用于引用一个数组元素,即下 标法和指针法。 案例8-10 定义数组并给数组赋初值,用下标法引用数 组的元素并输出数值中各元素的值。

案例程序
main() { int array[5]={ 1,2,3,4,5}; /*定义循环变量i*/ int i; /*定义数组array并 初始化*/ for(i=0;i<5;i++) /*用循环方式输出数组各 元素的值*/ printf(“ array[%d]=%d”, i, array[i]);/* 用下标法引用数组的元 素*/ } 运行程序,结果是: array[0]=1 array[1]=2 array[2]=3 array[3]=4 array[4]=5

案例8-11

定义数组并给数组赋初值,用指针法引用数 组的元素并输出数值中各元素的值。

案例程序
main() { int i; /*定义循环变量i*/ int array[5]={1,2,3,4,5}; /*定义数组array并初始化*/ int *p=array; /*定义指针p并指向数组的起始地址*/ for(i=0;i<5;i++) /*用循环方式输出数组各元素的值*/ printf(" array[%d]=%d", i, *(p+i)); /* *(p+i)就是用指针引用数组元素*/ } 运行程序,结果同上一个案例一样。

案例分析
1、p指向了数组array的首地址后,p就指向了数组的第一个 元素array[0],那么p+1就表示指向数组的下一个元素即第二 个元素array[1,以此类推。 2、既然p+i表示指向数组中的第i+1个元素,即p+i表示第i+1 个元素的地址,则第i+1个元素可以表示为*(p+i)。如*p表 示第一个元素array[0],*(p+1)表示第二个元素array[1]。 3、程序中for循环循环五次,每次输出*(p+i)的值,分别是输 出array[0]、array[1]、array[2]、array[3]、array[4]的值。 4、当指针指向数组的首地址后,对下标为i的数组元素的引 用有下面四种方法: (1)*(指针变量+i) (2)*(数组名+i) (3)指针变量[i] (4)数组名[i]

提示: 1、数组array中的下标为i(即第i+1个)的元 素的地址有如下几个表示形式:p+i、 array+i、&p[i]。这里假定p指向数组的首地 址。 2、数组array中的下标为i(即第i+1个)的元 素有如下几个表示形式:p[i]、array[i]、 *(p+i)、*(array+i)。

知识链接

引用数组元素可以用下标法,即通过表示某一个数 组元素在数组中有序排列位置的下标去访问该数 组元素,如array[5];也可以用指针法,即通过指 向数组元素的指针去访问它。也就是说任何能由 数组下标完成的操作也都可用指针来实现,而且 在程序中使用指针不仅使代码更紧凑、灵活,而 且还可使目标代码占用内存少、运行速度快。 提问:占用内存少、运行速度快?

案例8-12

编程实现通过指针引用、数组名及下标引用 等方法引用数组元素

案例程序

main() { int arr[5],i,*p; /*定义数组arr,循环 变量i和指针p*/ p=arr; /*p指向数组的 首地址*/ for(i=0;i<5;i++) scanf("%d", p+i); /*从键盘输入数据赋 值给数组的各个元素*/ for(i=0;i<5;i++) printf("arr[%d]\t=%d\t",i, arr[i]); /*下标法*/

printf("\n"); for( ;p<arr+5; p++) printf("*p\t=%d\t",*p); /*指针法*/ printf("\n"); for(i=0;i<5;i++) printf("*(arr+%d)\t=%d\t",i, *(arr+i)); /*数组名变量表示法*/

printf("\n"); for(p=arr,i=0; i<5; i++) printf("*(p+%d)\t=%d\t", i, *(p+i)); /*指针变 量表示法*/ printf("\n"); for(i=0; i<5; i++) printf("p[%d]\t=%d\t", i, p[i]); /*指针下 标表示法*/ }

案例分析

2、语句scanf(“%d”, p+i); 也可以改成 scanf(“%d”, arr+i); 或scanf(“%d”, &arr[i]); 3、各个输出语句分别采用不同的引用方法, arr[i]是使用下标法,即数组名[i]方法;*p 是使每次循环指针变量做加1操作指向下一 个数组元素的方法;*(arr+i)是使用*(数组名 +i)的方法;*(p+i)是使用*(指针变量+i)的方 法; p[i]是使用指针变量[i]的方法。

随堂练习
1、利用指针变量引用数组各个元素并输出。 2、从键盘输入5门课程的成绩,利用函数求平均成绩(用指针作为函数 参数)。 3、编程将0到9存储到一个一维数组中并输出,代码如下: main() { int *p, i, a[10]; p=a; for(i=0; i<10; i++, p++) *p=i; for(i=0; i<10; i++, p++) printf(“a[%d]=%d\n”, i, *p); } 上机调试该程序,并找出程序的错误。

提示:指针p是变量,在本例中它的值是不断 改变的,在执行第二个for循环时,指针已 经指向了哪里呢?

8.3.3 数组名与指针变量作为函数参数

案例8-13 已知一个一维数组a[10],求其中的最大数和 最小数并输出。

案例程序
int max,min; /*定义变量max和min保存最大和最小数*/ void max_min(int array[],int); main() { int i,a[10]; printf("Please input 10 numbers:"); for(i=0;i<10;i++) scanf("%d",a+i); /*输入十个数*/ max_min(a,10); /*调用函数,数组名为实参*/ printf("\n max=%d, min=%d\n",max,min); }

void max_min(int array[],int n) /*形参array为数组名*/ { int *p; max=min=*array; /*假设第一个数为最大最小*/ for(p=array+1; p<array+n; p++) /*循环与其它数比较*/ if(*p>max) max=*p; /*改变最大最小变量的值*/ else if(*p<min)min=*p; } 运行程序,从键盘输入五个整数:6 7 8 9 10 1 2 3 4 5 ↙ 结果是: Please input 10 numbers: 6 7 8 9 10 1 2 3 4 5 max=10, min=1

案例分析
1、定义变量max和min用来保存最大数和最小数,而且为全 局变量,所以在各个函数中均可访问引用。 2、输入语句scanf(“%d”,a+i); 灵活的运用了数组名。 3、语句max_min(a,10); a作为数组名作为函数的参数, 将a数组的首地址传递给形参array。 4、函数max_min()中的语句max=min=*array; 中的array是形 参数组名,它接收实参数组a的首地址,*array相当于引用 对应地址单元的内容,也就是数组的第一个元素,该语句 等价于max=min=array[0]。 5、在执行函数max_min()中的for循环时,p的初值为array+1, 也就是p指向array[1](即a[1])。以后每次执行p++,使p 指向下一个元素。

知识链接

1、用数组名作为函数参数,在函数调用时, 是把实参数组的首地址传递给形参数组, 使得两个数组共同占用同一段内存空间, 这样形参数组中的元素值如果发生变化就 会使实参数组的元素值也同时发生变化。 数组名代表的是数组的首地址,也是第一 个元素的地址。在实际应用中,既可以用 数组名作为函数参数,也可以用指向数组 的指针变量作为函数参数,在函数调用时, 传递的都是数组的起始地址。

知识链接

2、归纳起来,如果有一个实参数组,想在函 数中对此数组进行访问,比如查询、修改 该数组元素的值等,一般就采用传址方式, 也就是传递数组的首地址,形参接收数组 的首地址后,也就和实参一样,共同占用 相同的内存单元,对形参数组的改变,也 就相应地改变了实参数组。这种传递数组 的首地址的方式有以下四种。

(1)形参和实参都用数组名。例如: main() { int a[5]; …… f(a); } f(int b[]) { …… }

(2)实参用数组名,形参用指针变量。例如: main() { int a[5]; …… f(a); } f(int *pf) { …… }

(3)实参、形参都用指针变量。例如: main() { int a[5], *p; p=a; …… f(p); } f(int *pf) { …… }

(4)实参为指针变量,形参为数组名。例如: main() { int a[5], *p; p=a; …… f(p); } f(int b[]) { …… }

随堂练习

定义一个有11个元素的数组,其中前10个元 素为10个整数,求该数组前n个元素的和。 要求: 1、n的值由键盘输入,输入值如果大于10则 提示重新输入。 2、采用上面介绍的四种参数传递方式,在函 数中求和,求得的结果保存在数组的最后 一个元素中。 3、在主函数中输出求得的和。

8.4 指针与字符串
学习目标 掌握指针指向字符串的方法; 掌握使用指针处理字符串。 指针可以指向数组,那么指针是否可以指向字符串呢? 实际上,指针是非常灵活的,它可以指向任何数据类型。如 果需要指针指向某数据类型,只需要该数据类型的变量 或常量的起始地址赋值给指针就可以了。字符串在C语 言中是用一维数组来表示的,也是数组的一种类型。只 不过该数组的每个元素是字符罢了,所以指针当然可以 指向字符串。既然指针可以指向字符串,那么就可以通 过指针来实现对字符串的操作。

8.4.1 指针指向字符串常量

案例8-14
用指针指向字符串常量并输出字符串。

案例程序
#include<stdio.h> void main() { char *ptr="You and Me"; /*指针指向字符串*/ printf("%s\n",ptr); /*输出字符串*/ } 运行程序,结果是输出字符串: You and Me

案例分析

1、程序没有定义字符数组,而是使用字符串常量, 同字符数组一样,字符串常量也是在内存中分配 一块连续空间来存放字符串常量。语句char *ptr="You and Me"; 在定义指针变量ptr的同时,把 字符串常量的首地址赋给ptr,也就是第一个字符 ‘Y?的地址赋给ptr,使ptr指向该字符串。使用输 出字符串格式符%s,对应输出ptr指向的字符串常 量。这条语句还可以写成: char *ptr; ptr="You and Me";

2、如printf("%s\n",ptr);语句所示,输出字符串可以 使用以下格式: printf(“%s”,指针变量); 输出格式控制符%s表示输出一个字符串,给出指向 字符串的指针变量名(这里是指向字符串的指针 变量ptr),则系统先输出它所输出它所指向的第 一个字符,然后自动使指针变量加1,使之指向下 一个字符,输出下一个字符,依此类推,直到遇 到字符串结束标志‘\0?,注意:字符串在内存中 都会由系统自动添加一个结束标识符‘\0?。

知识链接

将指针指向字符串常量有两种方法: 1、在定义指针变量的同时赋初值,格式为: char *指针变量="字符串常量"; 2、在定义指针变量之后,再单独给指针变量 赋初值的方法。格式为: char *指针变量; 指针变量="字符串常量";

注意:第二种方法不可以采用下列方法,例 如: char *p; *p="You and Me"; 想想为什么?

8.4.2指针指向字符数组

案例8-15
用户由键盘输入密码,判断密码长度是否等 于6,如果不等,则提示用户重新输入。使 用指向字符数组的指针变量。

案例程序
#include<stdio.h> main() { char a[10], *p; int n=0; p=a; while(1) { gets(a); /*输入一个字符串存入字符数组a中*/ printf("The string is:%s\n",p); /*输出从键盘输入的字符串*/

for( ;*p!='\0';p++) /*通过for循环计算输入字符个数*/ n++; if(n!=6) printf("Please input again:"); /*如果输入字符数不是6个,提示重新输入*/ else break; /*否则退出while循环,向下执行*/ } }

运行程序,从键盘输入字符串“China”,则 输出后,再提示重新输入,再输入字符串 “flower”。 运行结果是: China The string is: China Please input again: flower The string is: flower

案例分析

1、定义数组a后,语句p=a使指针变量p保存数组a的 首地址,即p指向a。 2、首先调用函数gets(a)从键盘输入字符串保存在字 符数组a中,然后再输出刚输入的字符串a 3、循环语句for( ;*p!='\0';p++) n++; 通过for循环计算 输入字符个数。每次统计一个字符后,指针p值自 加1,指向下一个字符,直到遇到结束符‘\0?。 4、语句if(n!=6) printf("Please input again:");表示如 果输入字符数不是6个,提示重新输入。

知识链接

字符串是按照字符数组存储的,所以对字符 串中字符的引用也可以用下标法和指针法。

知识链接

? 案例8-16 ? 输入一段字符,统计其中所包含的单词数 量。

案例程序
#include<stdio.h> #include<string.h> int count(char *); /*统计个数函数count声明*/ main() { char str[100]; /*定义数组str来保存输入的字符串*/ int n; /*整型变量n保存单词个数*/ printf("Please input a string:\n"); gets(str); /*输入字符串,保存在字符数组str中*/ n=count(str); /*调用函数count,统计单词个数*/ printf("The words in str are:%d\n",n); /*输出个数*/ }

int count(char *p) /*形参指针p也指向输入的字符串*/ { int num=0, flag; /*num存储单词数,flag为标志变量*/ flag=0; /*先把flag赋初始值为0*/ while(*p!='\0') /*字符不是结束符就继续循环*/ { if(*p==' ')flag=0; /*遇到空格,flag置0*/ else if(flag==0) /*当前字符不是空格,而且flag为0,说明遇到一 个新单词*/ { num++; /*单词个数加1*/ flag=1; /*标志flag置1,表示新单词已统计*/ } p++; /*指针p加1,指向下一个字符*/ } return(num); /*返回字符个数*/ }

运行程序,从键盘输入字符串“I would like to be a programmer!” 输出结果是: Please input a string: I would like to be a programmer! The words in str are: 7

提示:标志变量就是编程时用来对情况的判断的标
志,例如本例flag变量就表示值为0表示前一个字 符是空格,如果为1则表示前一个字符是非空格字 符或是还没开始统计。这样通过对标志变量的值 的判断,就可以很好的区分是否遇到了一个新的 单词,从而进行正确的统计。

随堂练习

定义两个字符数组,分别存储两个字符串, 用指针指向字符串,将其中一个字符串复 制到另外一个字符串。

知识链接

使用字符串指针变量与字符数组的区别 1、字符串指针变量本身是一个变量,用于存放字符串的首地址。也就
是说字符串指针变量只能指向字符串,而不能存放字符串。字符数组 则不同,它在内存中表示一段连续的存储空间,可以用来存放整个字 符串。

2、对字符串指针变量赋初值有两种方式:
(1)在定义变量的同时赋初值,例如: char *p="It?s a long road to go.“ (2)在定义后初始化,例如把上面语句改为: char *p; p="It?s a long road to go."

3、对字符数组的情况就不同了。对字符数组也同样可以在
定义的同时进行初始化,例如: char str[80]="Hard work can make up for a lack of intelligence." 但不能写成: char str[80]; str="Hard work can make up for a lack of intelligence." 如果采用先定义后初始化的方式,则只能采用对字符数组的 各元素逐个赋值的方式。

知识链接

4、字符数组一经定义后,编译时就会为它分配一段连续的
内存单元,所以它有确定的地址,字符数组名就代表该段 内存单元的起始地址。而定义指针变量时,给指针变量分 配内存单元,在其中存放一个地址值,但是该地址值是不 确定的,也就是指针变量的指向是不确定的,在把字符串 的首地址赋值给指针变量后,该指针才确定保存字符串的 首地址。

5、指针变量的值是可以改变的。例如:
char *p="Yesterday once more." p+=10; printf(“%s”,p); 指针p开始指向字符‘Y?,然后指针p的值加10,则指针p指 向“once”中的字符‘o?,最后输出结果应该是:once more. 而数组名代表数组的首地址,它本身的值是不可以改变的。 例如下面语句是错误的。 char str[]="Now and Then." str++; printf(“%s”,str);

8.5 指针函数与函数指针

学习目标 掌握指针型函数的用法。 掌握用函数指针调用函数以及用函数指针作 为函数参数。 指针函数就是函数返回值为指针型的函数, 而函数指针就是指向函数的指针。

8.5.1 指针函数

案例8-17 利用函数返回的指针引用对应的数据:输入 1~5之间的数字,输出对应的人名。

案例程序

char *spstars(int n) { static char stars[5][10]={"Jordan","James","Kobe","O'nei l","Wade"}; return(stars[n-1]); /*返回二维数组第n行的首地址*/ }

main() { int i; char *p; printf("Input your favorite player's number:"); scanf("%d",&i); /*输入对应的号码*/ p=spstars(i); /*函数返回的地址保存在指针变量p中*/ printf("No.%d is your favorite player:%s\n",i,p); /*输出序号 和对应的人名*/ }

运行程序,从键盘输入序号,比如1,则输出 结果是: Input your favorite player?s number: 1 No.1 is your favorite player: Jordan

案例分析

1、本案例定义了一个指针函数(指针型函数) spstars,它的返回值是地址。 2、语句static char stars[5][10]={“Jordan”,“James”,“Kobe”,“O?neil”,“W ade”};定义了一个静态二维字符数组stars . 3、返回语句return(stars[n-1]); 返回二维数组第n行的 首地址,在二维数组中stars中,stars[行下标]表示 该行的首地址,也就是一个名字字符串的首地址。 用n-1来表示下标的原因是数组下标是从0开始的, 比如序号为1的对应行下标应该是0,序号为n的行 下标应该是n-1。

提示
在函数spstars中,也可以定义一个局部指针变量,用该指针 变量保存对应名字字符串的首地址,在返回时,返回该指 针变量的值即可,指针型函数spstars代码修改如下: char *spstars(int n) { char *p; static char stars[5][10]={"Jordan","James","Kobe","O'neil","Wade"}; p=stars[n-1]; /*指针p保存对应行的首地址*/ return(p); /*返回指针p的值就是第n行的首地址*/ }

随堂练习

编写一个指针型函数,求出一个整数数组中 的最大数的地址并返回该地址,然后在 main函数中输出数组中的最大数的值和对 应的下标号。

知识链接

1、在C语言中,允许一个函数的返回值是一个指针 (即地址)。 2、指针函数的一般形式是: 类型说明符 *函数名(形参表) { 函数体 } 指针函数的定义格式和前面介绍的一般函数定义格 式基本相同,唯一的区别是在函数名前加一个“*” 用来表示函数的返回值是指针类型(即地址)。 例如:

float *funcp() { 函数体 } 这里类型说明符“float”和“*”表示函数返回 值是一个指针,该指针是指向浮点型(float) 的指针。

8.5.2 函数指针

在C语言中,和数组类似,函数也是占有一段 连续的内存空间,同样函数名就是该函数 所占内存空间的首地址。可以把函数的首 地址保存在一个指针变量中,使该指针变 量指向该函数,然后就可以通过该指针找 到并调用这个函数。指向函数的指针就是 函数指针。

案例8-18

案例8-18 用函数指针调用函数,求两个数中较大的数。

案例程序

#include<stdio.h> int max(int x, int y) { return(x>y?x:y); } void main() { int (*p)( ); /*定义指向函数的指针变量*/

int a,b,c; p=max; /*将max函数的入口地址赋给函数指针*/ scanf("%d,%d",&a,&b); /*输入两个数*/ c=(*p)(a,b); /*通过指针调用函数max*/ printf("a=%d,b=%d,max=%d\n",a,b,c);
} 运行程序,结果是: 输入:5, 9 ↙ 输出:a=5,b=9, max=9

案例分析

1、主函数中int (*p)( );定义指向函数的指针变 量p,它所指向的函数的返回值是int型。 2、赋值语句p=max;表示将函数的入口地址 赋给p。 3、调用函数用语句c=(*p)(a,b); 它等价于 c=max(a,b);

案例分析

1、主函数中int (*p)( );定义指向函数的指针变量p, 它所指向的函数的返回值是int型。 2、赋值语句p=max;表示将函数的入口地址赋给p。 3、调用函数用语句c=(*p)(a,b); 它等价于c=max(a,b);

随堂练习

定义一个一维数组,通过函数指针调用函数, 求数组的最大元素的值。

知识链接

1、用函数指针调用函数 (*指针变量名)(实参表) 2、函数指针变量定义的一般形式是: 类型说明符 (*指针变量名)(参数); int (*p)( ); 3、用函数指针调用函数的步骤如下:

① 先定义函数指针变量,如“int (*p)( ); ”, 定义p为函数指针。 ② 给函数指针变量赋值为被调用函数的入口 地址,形式为: 指针变量名=函数名; 如:p=max; ③ 经过步骤(1)、(2)后,就可以通过函 数指针调用函数,如:(*p)(a, b);

提示: 1、int *p( ); 表示指针型函数,即p是函数名, 该函数返回值是指针(地址)。 2、int (*p)( ); 表示函数指针,即p是指向函数 的指针,该函数返回值是一个整数(值)。

随堂练习

编写一个函数calculation( )做算术四则运算,每次调 用实现不同的功能,输入两个数,第一次调用实 现两个数的加法,第二次实现两个数的减法,第 三次实现两个数的乘法,第四次实现两个数的除 法。用函数指针作为参数,即调用函数 calculation( )时,用加减乘除四个函数的函数名作 为函数实参,在函数calculation( )中,定义指向函数 的指针作为形参来接收加减乘除函数的入口地址, 然后在函数calculation( )中用指向函数的指针调用 对应的加减乘除函数。

课后练习
选择题 1、已知:int *p,a;则语句“p=&a;”中的运算符“&”的含义是 ( )。 A)位与运算 B)逻辑与运算 C)取指针内容 D) 取变量地址 2、已知:double d;希望指针变量pd指向d,下面对指针变量 pd的正确定义是 ( )。 A)double pd; B)double &pd C)double *pd D) double *(pd) 3、若x为整型变量,p是指向整型数据的指针变量,则正确 的赋值表达式是( )。 A)p=&x B)p=x C)*p=&x D)*p=*x 4、已知:int a,*p=&a;则下列函数调用中错误的是( )。 A)scanf("%d",&a); B)scanf("%d",p) C)printf("%d",a); D)printf("%d",p);

5、函数的功能是交换变量x和y中的值,且通过正确调用返回交换的结果。能正确执行此功 能的函数是( )。 A)funa(int *x,int *y) { int *p; *p=*x; *x=*y; *y=*p; } B)funb(int x,int y) { int t; t=x; x=y; y=t; } C)func(int *x,int *y) { *x=*y; *y=*x; } D)fund(int *x,int *y) { int t; t=*x; *x=*y; *y=t; }

6、若有以下定义 int a[10], *p=a; 则*(p+5)表示( ) A) 元素a[5]的地址 B) 元素a[5]的值 C) 元素a[6]的地址 D) 元素a[6]的值 7、以下程序段运行结果是( )。 char *s= "abcde"; s=s+2; printf(“%s”, s); A) cde B) 字符‘c? C) 字符‘c?的地址 D)无确定的 输出结果

8、如有定义int (*p)( ); 指针p可以( ) A) 代表函数的返回值 B) 指向函数的入口地址 C) 表示函数的类型 D) 表示函数返回类型

程序分析 1、分析下面程序的运行结果 main() { int a[4]={1,3,5,7}; int i, *p=a; printf(“%d,%d\n”,*p, ++*p); }

2、分析下面程序的运行结果 #include<stdio.h> #include<string.h> main() { char s[80], *sp=“HELLO!”; sp=strcpy(s, sp); s[0]= ' h'; puts(sp); }

3、分析下面程序的运行结果 # include <stdio.h> main() { int a[]={1,2,3,4,5,6}; int *p; p=a; printf("%d",*p); printf("%d",*(++p)); printf("%d",*++p); printf("%d",*(p--)); p+=3; printf("%d,%d\n",*p,*(a+3)); }

4、分析下面程序的运行结果 #include<stdio.h> #define N 5 int max(int *pt) { int i,max=*pt; for(i=1; i<N; i++) { if(pt[i]>max) max=pt[i]; } return(max); }

/*循环比较其它数是否大于max值*/ /*如果大于则改变max为新值*/

void main() { int a[N],i,result; int (*p)(); /*定义指向函数的指针 变量*/ for(i=0;i<N;i++) scanf("%d",&a[i]); /*给数组赋初值*/ p=max; /*将max函数的入口地址赋给函 数指针*/ result=(*p)(a); /*通过指针调用函数max,数组 名a作为实参*/ printf("The max value of array a[%d] is:%d\n",N, result); }

动手编程 1、输入三个字符,按ASCII码大小顺序输出,要求 用指针实现。 2、用选择法对10个整数排序,用指针实现。 3、编写函数比较两个字符串,用指针作为函数参数, 如相等,则输出“Yes”,不等输出“No”。 4、编写一个函数,判断输入的字符串是否是回文。 回文是指顺读、逆读都一样的字符串,如 “abcba”。


相关文章:
C语言程序设计教学做一体化模块8_图文.ppt
C语言程序设计教学做一体化模块8 - C语言程序设计 教学做一体化 主编 滕泓虬
C语言程序设计教学做一体化-模块(3)_图文.ppt
C语言程序设计教学做一体化-模块(3) - 文档均来自网络,如有侵权请联系我删除文档... C语言程序设计教学做一体化-模块(3)_教学案例/设计_教学研究_教育专区。文档...
C语言程序设计教学做一体化 模块6_图文.ppt
C语言程序设计教学做一体化 模块6 - C语言程序设计 教学做一体化 TC ID
C语言程序设计教学做一体化-模块(4)_图文.ppt
C语言程序设计教学做一体化-模块(4) - 文档均来自网络,如有侵权请联系我删除文档... C语言程序设计教学做一体化-模块(4)_教学案例/设计_教学研究_教育专区。文档...
C语言程序设计教学做一体化-模块4._图文.ppt
C语言程序设计教学做一体化-模块4._职高对口_职业教育_教育专区。C C语言程序设计 教学做一体化主编 滕泓虬 中国水利水电出版社 C语言程序设计教学做一体化 模块...
C语言程序设计教学做一体化 模块4_图文.ppt
C语言程序设计教学做一体化 模块4_教学案例/设计_教学研究_教育专区。C语言程序设计 教学做一体化 C语言程序设计教学做一体化 模块四 选择结构程序设计 ? 本模块...
C语言程序设计教学做一体化模块2_图文.ppt
C语言程序设计教学做一体化模块2 - C语言程序设计 教学做一体化 主编 滕泓虬
C语言程序设计教学做一体化-模块3.ppt
C语言程序设计 语言程序设计 教学做一体化主编 滕泓虬 中国水利水电出版社 TC IDE 模块三 顺序结构程序设计 3.1 程序的三种基本结构 学习目标 (1)了解结构化程序...
C语言程序设计课程“教学做”一体化教学探索.doc
C语言程序设计课程“教学做一体化教学探索 - 龙源期刊网 http://www.qikan.com.cn C 语言程序设计课程“教学做一体化教学探 索 作者:梁永恩 万世明 翟敏...
C程序设计教学做一体化教程_第3章_运算符与表达式_图文.ppt
C程序设计教学做一体化教程_第3章_运算符与表达式 - C程序设计教学做一体化教程 耿祥义 张跃平 编著 清华大学出版社 2013-7-26 1 第3章 运算符与表达式 2013...
C程序设计教学做一体化教程_第5章_ 循环语句.讲义_图文.ppt
C程序设计教学做一体化教程_第5章_ 循环语句.讲义_其它_职业教育_教育专区。C C程序设计教学做一体化教程耿祥义 张跃平 编著 清华大学出版社 2018/11/13 1 第...
C程序设计教学做一体化教程_第9章_ 指针与数组_图文.ppt
C程序设计教学做一体化教程耿祥义 张跃平 编著 清华大学出版社 2014-9-1
C程序设计教学做一体化教程_第4章_分支与开关语句_图文.ppt
C程序设计教学做一体化教程_第4章_分支与开关语句 - C程序设计教学做一体化教程 耿祥义 张跃平 编著 清华大学出版社 2013-7-26 1 第4章 分支与开关语句 2013...
C语言程序设计教程第8章北京邮电大学出版社._图文.ppt
2018/9/14 C语言程序设计教程 第8章 指针 8 (2) 指针变量初始化
《C语言程序设计教程》._图文.ppt
高等教育出版社 《C语言程序设计教程》谭浩强 张基温等编著 主讲:傅丰 黄淮学院计算机科学系 Huanghuai University Department of Computer Science 第四章 模块化程序...
《C语言程序设计》课程教学设计方案_图文.doc
C 语言程序设计 课程教学设计方案 课程名称:C 语言...四、课程内容设计 (一)总体框架 模块(或章) 学习...算术运算符和算术表达式 8、 赋值运算符和赋值表达式...
基于C语言的逻辑程序设计._图文.ppt
算法设计 选择结构 情境5: 循环结构 情境9: 情境10: 情境6: 情境7: 情境8...采用广播教学及“教学做一体化的教学模式 基于C语言的逻辑程序设计 四、本...
502082《C语言程序设计》课程标准(已审核)分解.doc
掌握 C 语言的基本编程技能,掌握结构化程序设计方法...完成相关的功能模块设计、编码、调试和单元测 试工作...教学方法建议与说明(方法、手段、教学做一体等) 1....
《C语言程序设计》说课稿(超好)_图文.doc
课程教学大纲 课程教学大纲包括课程性质、课程地位、...按照程序设计的能力模块,系统的讲解 C 语言基本概念...“项目驱动、案例教学、理论实践一体化”的教学模式...
C语言程序设计说课探素_图文.ppt
JAVA 数据结构 C语言程序设计 课程的教学目标培养学生树立结构化程序设计的思想,...教学周次 第八、九周 第十周 第十一周 任务名称 “成绩录入模块”设计 “...