Presentation is loading. Please wait.

Presentation is loading. Please wait.

C语言程序设计教程 单位:德州学院计算机系.

Similar presentations


Presentation on theme: "C语言程序设计教程 单位:德州学院计算机系."— Presentation transcript:

1 C语言程序设计教程 单位:德州学院计算机系

2 第8章 指 针 1 指针与指针变量 2 指针与数组 3 指针与函数 4 典型例题

3 8.1指针与指针变量 8.1.1指针的基本概念 内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。 在C语言中,存取变量值的方法有直接和间接存取两种方式。 1.在程序中直接使用变量名对内存单元进行存取的方式称为“直接存取方式 ”。

4 则给整型变量a、b和c各分配4个字节的内存单元,其分配情况如图8.1所示。
设在函数内部有如下语句: int a=10,b=20; float c=30; 则给整型变量a、b和c各分配4个字节的内存单元,其分配情况如图8.1所示。 如图1所示,变量a所占内存单元的地址是3008,直接使用变量名a便可对这段内存单元进行存取。 图8.1 内存单元空间分配示意图

5 在C语言中,指针是一种特殊的变量,它是存放地址的。 pa与a的逻辑关系如图8.2所示。
2.另一种存取变量值的方式称为“间接存取方式”。 即,将变量a的地址存放在另一个变量pa中。 地址一般被形象地称为指针。专门用来存放指针的变量称为指针变量。 在C语言中,指针是一种特殊的变量,它是存放地址的。 pa与a的逻辑关系如图8.2所示。 图8.2 pa与a的逻辑关系示意图

6 8.1.2指针变量的定义与初始化 一个变量的地址称为该变量的“指针”。 例如,地址3008是变量a的指针。如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的pa就是一个指针变量。

7 1.指针变量的定义 float *pointer_3; 例如: char *pointer_4; 一般形式为:
「存储类型」 数据类型 *变量名; 说明: (1)存储类型为可选项,含义与第7章中的存储类型相 同,是指指针变量本身的存储类型。 (2)“*”是指针说明符,表明其后的变量名为指针变量。 (3)数据类型是指该指针变量所指向变量的数据类型,称为指针变量的基类型。在VC 中,所有指针本身的类型均默认为unsigned long int型。 float *pointer_3; char *pointer_4; 例如:

8 在定义指针变量时要注意两点: 指针变量前面的“*”,表示该变量的类型为指针型变量。 例: float *pointer_1; 指针变量名是pointer_1 , 而不是* pointer_1 。

9 (2) 在定义指针变量时必须指定变量的数据类
型。一旦指针变量的类型被确定后,它只能 指向同一个类型的变量。 即:整型变量的地址只能放到定义为整型变 量的指针变量中。下面的赋值是错误的∶ float a; int * pointer_1; pointer_1=&a;

10 2.相关运算符 两个与指针变量密切相关的运算符:“&”和“*”。 (1)“&”是取地址运算符,其功能是取变量的地址。 例如: pa=&a;
其功能是将变量a的地址赋给指针变量pa,即建立起了指针变量pa与变量a之间的逻辑关系,如图8.3所示。 图8.3 指针运算示意图 (2)“*”是指针运算符,也称为间接运算符,表示指针变量所指向的内存单元。例如: *pa=3; 其功能是将3存入到pa所指向的内存单元(即a)中与语句“a=3;”的功能相同。

11 3.指针变量的初始化 int a; int *pa=&a; int *pb=pa;
在定义指针变量时,可以同时对它赋初值,称为指针变量的初始化。 例如: int a; int *pa=&a; int *pb=pa;

12 8.1.3 指针变量的基本运算 可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向该变量。 例如:
1.指针变量的赋值 可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向该变量。 例如: int a=5,*pa,*pb; pa=&a; pb=pa; 指针变量指向示意图 三条语句执行完毕后,各变量之间的逻辑关系如图8.5 所示。 特别注意,不同数据类型的指针变量之间不能赋值 。

13 【例8.1】指针赋值程序示例。 #include <stdio.h> void main() { int a,b;
int *pa, *pb; //定义了两个指针变量pa和pb a=20;b=30; pa=&a; //使指针变量pa指向变量a pb=&b; //使指针变量pb指向变量b printf("a+b=%d\n",a+b); printf(" *pa+*pb=%d\n",*pa+*pb); //输出和 }

14 【例8.2】输入a和b两个整数,按先小后大的 顺序输出a和b。 #include <stdio.h> void main() { int a=10,b=5,t; int *pa,*pb; pa=&a;pb=&b; if(a>b) {t=*pa;*pa=*pb;*pb=t;} printf(" a=%d,b=%d\n",a,b); printf(" min=%d,max=%d\n",*pa,*pb); } 程序运行结果: a=5, b=10 min=5, max=10

15 2.指针的算术运算 : 指向数组的指针变量可以参与算术运算。 例如: int a[10]={0,1,2},*pa=a; 则pa经常参与pa+n、pa-n、pa++、++pa、pa--、--pa运算。 pa+n(pa-n)的含义是把指针变量中的地址值取出来再加(减)上n个元素的字节数。例如pa+2,假设pa中的值为3000(即数组a的首地址为3000),每个int型元素占4个字节,则pa+2的值为3000+2*4=3008。

16 pa++(pa--)的含义是pa先参与运算,然后自加(减)1。 例如: printf("%d",*pa++);
先输出指针变量pa所指向元素(即a[0])的值,然后自加1,即pa指向a[1]。 ++pa(--pa)的含义是先自加(减)1,然后参与运算。 例如:printf("%d",*++pa); 指针变量pa先自加1,即指向a[1],然后输出pa所指向元素(即a[1])的值。 注意:*pa++ 等价与*(pa++),请注意与(*pa)++的区别。(*pa)++的含义是取出pa所指向元素的值,然后该元素的值加1。

17 3.指针变量之间的关系运算 指向同一数组的两个指针变量之间进行关系运算可以表示它们所指向元素之间的地址关系。 例如:pa<pb 如果指针变量pa所指向数组元素在指针变量pb所指向元素之前则表达式为真,否则为假。其它关系表达式依此类推。

18 8.1.4 二级指针 二级指针变量的定义形式如下: 数据类型 **指针变量名 例如: int a=18; int *pa=&a;
一级指针变量中存放的是一个普通变量的地址,而二级指针变量中存放的是一个一级指针变量的地址,所以二级指针也称为指向指针的指针。 二级指针变量的定义形式如下: 数据类型 **指针变量名 例如: int a=18; int *pa=&a; int **ppa=&pa; 图8.6 二级指针示意图

19 程序输出结果: 【例8.3】利用二级指针实现两个整数相加。 #include <stdio.h> void main()
{ int a,b; int *pa, *pb; int **ppa,**ppb; a=10; b=20; pa=&a; //指针pa指向a pb=&b; //指针pb指向b ppa=&pa; //二级指针ppa指向指针pa ppb=&pb; //二级指针ppb指向指针pb printf("a+b=%d\n",a+b); printf("*pa+*pb=%d\n",*pa+*pb); //一级指针引用 printf("**ppa+**pb=%d\n",**ppa+**ppb); //二级指针 } 程序输出结果: a+b=30 *pa+*pb=30 **ppa+**pb=30

20 8.2 指针与数组 一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址。

21 定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。
8.2.1 指向一维数组的指针 1.指向一维数组的指针变量 定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。 例如: int a[10]={1,2,3,4,5,6,7,8,9,10}; //定义a为包含10个整型数据的数组 int *p; //定义p为指向整型变量的指针 p=a; //将数组a的首地址赋给指针变量p,表示p指向数组a

22 若有语句p=&a[4]; 则表示将数组元素a[4]的首地址赋给指针变量p,表示p指向数组元素a[4]。
一维数组a的首地址也就是数组元素a[0]的地址,语句“p=a”等价于“p=&a[0]”,如下图8.7所示。 图8.7 一维数组指针示意图 若有语句p=&a[4]; 则表示将数组元素a[4]的首地址赋给指针变量p,表示p指向数组元素a[4]。

23 2.数组元素的引用 引用一个数组元素,可以用: (1) 下标法,如a[i]形式; (2) 指针法,如*(a+i)或*(p+i)。
 (1) 下标法,如a[i]形式;  (2) 指针法,如*(a+i)或*(p+i)。 其中a是数组名,p是指向数组元素的指针变量,其初值p=a。因此 a[ i ]、*(a + i)和*(p + i)三种形式等价。 需要注意的是,假设p指向数组a的首地址,则p++表示指针变量p使用完毕后指向下一个元素,即a[1]。所以也可通过指针的不断移动存取数组元素。但a++是不合法的,因为数组名a是常量,不能被赋值。

24 【例8.4】输入10个整数到数组a中,并采用不同方法输出各 元素。 #include <stdio.h>
void main() { int i,a[10],*p; printf("输入10个数组元素的值: "); for(i=0;i<=9;i++) scanf(" %d",&a[i]); printf(" \n"); printf("输出10个数组元素的值:"); for(p=a;p<=a+9;p++) printf(" %5d",*p); printf("\n"); printf("输出10个数组元素的值: "); p=a; printf(" %5d",p[i]); }

25 【例8.5】输入5个整数到数组a中,求其平均值并输出小于平均值的数组元素的值。
#include <stdio.h> void main() { int i,a[5],*p; float ave,sum=0; printf("输入5个数组元素的值: "); p=a; //此语句不可少,它使p指向数组a的第一个元素 for(i=0;i<=4;i++) scanf(" %d",p++); printf(" \n"); for(p=a;p<=a+4;p++) //p=a;此语句使p重新指向数组a的第一个元素 sum=sum+*p; ave=sum/5; printf("5个数组元素的平均值为:%f\n",ave); printf("小于平均值的数组元素值为: "); p=a; { if(*p<ave) printf("%6d",*(p)); p++; }

26 8.2.2 指向多维数组的指针 二维数组和数组元素的地址 本节以指向二维数组的指针变量为例,说明指向多维数组的指针变量的定义与使用。
一维或多维数组都是占用一片连续的内存空间。二维数组在内存中是按照行优先的原则存放的,二维数组实际上是一个一维数组,只不过这个一维数组的每一个元素又是一个一维数组。

27 int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}},*p=a[0];
例如,若有以下定义: int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}},*p=a[0]; 二维数组a可由三个元素a[0]、a[1]、a[2]组成,而a[0]、a[1]、a[2]每个元素又分别是由4个整型元素组成的一维数组。例如a[0]代表的第0行由4个元素组成,分别是a[0][0]、a[0][1]、a[0][2]和a[0][3]。 其指针示意如下图8.8所示。 图8.8 二维数组指针示意图

28 2.指向二维数组的指针变量 与二维数组相关的指针变量有两种:一种是指向二维数组元素的指针变量,另一种是指向包含有m个元素的一维数组的指针变量。 (1)指向二维数组元素的指针变量。 这种形式的指针变量与普通指针变量的定义形式相同。其基类型需与数组元素值的类型相同。

29 【例8.6】输入一个3行3列矩阵的各个元素的值, 计算并输出各元素之和。
#include <stdio.h> void main() { int i,j,a[3][3],*p; long sum=0; printf("输入矩阵的各个元素的值:\n"); p=a[0]; for(i=0;i<=2;i++) for(j=0;j<=2;j++) scanf(" %d",p++); printf(" \n"); p=a[0]; //指针变量指向数组的首地址 sum=sum+*(p++); //求各元素之和 printf("输出各元素之和为:%6d",sum); }

30 【例8.6】输入一个3行3列矩阵的各个元素的值, 计算并输出各元素之和。
#include <stdio.h> void main() { int i,j,a[3][3],*p; long sum=0; printf("输入矩阵的各个元素的值:\n"); p=a[0]; for(i=0;i<=2;i++) for(j=0;j<=2;j++) scanf(" %d",p++); printf(" \n"); p=a[0]; //指针变量指向数组的首地址 sum=sum+*(p++); //求各元素之和 printf("输出各元素之和为:%6d",sum); }

31 【例8.7】找出二维数组中的最小值,并输出最小值所在的行列号及整个二维数组。
#include <stdio.h> void main() { int i,j,*p,min,h,l; int a[3][3]={2,4,6,8,-10,12,14,28,16}; p=a[0]; min=*p; //首先定义最小值为数组中第一个元素 for(i=0;i<=2;i++) for(j=0;j<=2;j++) { if(min>=*p) { min=*p; h=i; //记录当前最小值元素所在的行 l=j; //记录当前最小值元素所在的列 } p++; //注意此语句位置,在内层循环体中 p=a[0]; //指针p指向数组的首地址 for(i=0;i<=2;i++) //输出二维数组 { for(j=0;j<=2;j++) printf("a[%d][%d]=%-6d",i,j,*(p++)); printf(" \n"); printf("min是:%d 所在行号:%d 所在列号:%d\n",min,h,l);

32 (2)指向包含m个元素的一维数组的指针,可理解为 行指针。 其定义形式为: 「存储类型」类型说明符 (*指针变量名)[元素个数];
其中,元素个数由所声明指针变量所指向二维数组的第二维长度决定。 例如: int a[3][4]; //二维数组a的第二维长度为4 int (*p)[4]; //定义指针变量p,指向包含4个元素的一维数组 p=a; //将指针变量p指向二维数组a 注意:p所指的对象是有4个元素的一维数组,p的值是二维数组每行的首地址,而不是二维数组元素的首地址。 p+i等价于a+i,即二维数组第i行的首地址。p++是将指针变量p移动到当前行的下一行。

33 二维数组的引用方法和一维数组一样,也有两种方法:下标法和指针法。
例如p指向二维数组a的第0行,则p[i][j] (下标法)、*(*(p+i)+j)(指针法)、*(p[i]+j)(下标指针混合法)和(*(p+i))[j](下标指针混合法)四种形式等价。但此时仍需注意,p是变量,而a是常量。

34 【例8.8】通过行指针引用二维数组元素的方式实现例8.7的功能。
#include <stdio.h> void main() { int i,j,min,h,l; int (*p)[3]; int a[3][3]={2,4,6,8,-10,12,14,28,16}; p=a; //p指向第0行 min=**p; //将首元素赋给min,**p等价于*(*(p+0)+0)和 a[0][0] for(i=0;i<=2;i++) { for(j=0;j<=2;j++) //移动p指针指向二维数组的每一行 if(min>=*(*p+j)) { min=*(*p+j); h=i; l=j; } p++; //注意此语句位置,在内层循环外,外层循环体中

35 p=a; //p指向二维数组首地址 for(i=0;i<=2;i++) //p指针始终指向二维数组首地址 { for(j=0;j<=2;j++) printf("a[%d][%d]=%-6d",i,j,*(*(p+i)+j)); printf(" \n"); } printf(" min是:%d 所在行号:%d 所在列号:%d\n",min,h,l); 比较:此例中定义了一个行指针p,p++是指p移到下一行,语句“p++;”放在内层循环外、外层循环体中。而例8.7中采用的指针p是一个普通指针,p++是指p移动到下一个元素,语句p++;”放在内层循环内。请严格区分两者的含义与使用方法。

36 8.2.3 指向字符串的指针 字符串可以存储在数组中。因此,根据前面所述数组与指针的关系可知,字符串可以用指针表示。 例如:
char str1[]="Beijing"; 说明字符数组str1存放了一个字符串“Beijing”。 指向字符串的指针变量定义形式与指向字符变量的指针变量定义形式相同。例如: char *str2="Hello!"; 定义了一个指向字符串的指针变量,把字符串的首地址赋给str2。语句与下列两条语句等价: char *str2; str2="Hello!";

37 这时指针变量str2指向数组a,而a被分配在变量区,其值可以被改变。
在C语言中,字符串指针的使用方式和字符数组的使用方式相同。 例如,对字符串的整体输出可以用语句,如printf("%s",str2);”。其输出过程是从指针str2所指示的字符开始逐个输出,直到遇到字符串结束标志’\0’为止。 需要特别注意,语句char *str2=“Hello!”;功能是将指针变量str2指向被分配在常量区的字符串“Hello!”,这时使用语句scanf(“%s”,str2);是不合法的。 下面语句是合法的: char a[10],*str2=a; scanf("%s",str2); 这时指针变量str2指向数组a,而a被分配在变量区,其值可以被改变。

38 【例8.9】输出一个字符串中第n个字符前的所有字符。 #include <stdio.h>
#include <string.h> void main() { char a[]="Welcome to DeZhou!"; char *p; unsigned n; scanf("%d",&n); //逐个输出字符,直到第n个或已经到了字符串尾部 for(p=a;p<=a+(n-2)&&*p!='\0';p++) //注意p第n-1个字符的位置是在a+(n-2) printf("%c",*p); } 程序输出结果: 输入:8 输出:Welcome

39 8.2.4 指针数组 指针数组是指每个元素均为指针类型的数组,其定义的一般形式为: 「存储类型」 数据类型 *数组名[数组长度]
「存储类型」 数据类型 *数组名[数组长度] 其中数据类型为指针元素所指向变量的类型。 例如: int *p[10]; 定义一个具有10元素的一维数组p,每个元素均为基类型为int的指针变量。 遵照运算符的优先级,一对[]的优先级高于*号即*p[10]等价于*(p[10]),请注意和前面指向二维数组的指针(*p)[10]的区别。

40 【例8.10】将多个字符串按字典顺序排序后输出。
程序分析:首先建立一个指针数组,让它的每个元素都指向一个字符串。然后通过改变指针指向的方式对多个字符串进行排序。 #include <stdio.h> #include <string.h> void f(char *p[],int n) { char *t; int i,j; for(i=0;i<n-1;i++) //用冒泡法对字符串排序 for(j=i+1;j<n;j++) if(strcmp(p[i],p[j])>0) //比较字符串不能用p[i]>p[j] { t=p[i]; p[i]=p[j]; p[j]=t; } //交换指针指向 }

41 排序前、后指针数组各元素的指向关系如图8.9所示:
int main() { char *str[6]={ "zhang","wang","liu","zhao","qian","song"}; int i; f(str,6); //调用函数实现对字符串的排序功能 printf("the array has been sorted:\n"); for(i=0;i<6;i++) //输出排序后字符串 printf(" %s ",str[i]); return 0; } 排序前、后指针数组各元素的指向关系如图8.9所示: (a)排序前 (b) 排序后

42 8.3指针与函数 8.3.1指针变量作为函数参数 在第7章中已经学习了函数参数传递方式分传值和传址两种。引人了指针的概念后,传址方式共有以下四种实现方式:①形参为数组形式、实参为数组名;②形参为指针变量、实参为数组名;③形参为数组形式、实参为指针变量;④形参为指针变量、实参为指针变量。 形参即使写成数组形式,也不会按一个数组为其分配内存空间,而是和指针变量一样,按unsigned long int 类型为其分配内存单元,因此,形参为数组形式和形参为指针变量实质是一样的,可以统一看成指针变量。而指针变量、数组名、&普通变量名等均为指针的不同表现形式,因此都可以做相应实参。由此,可以将上述传址方式的四种实现形式归纳为:形参为指针变量,实参为指针。

43 程序输出结果为: 交换后的x和y为:x=5,y=3 输出指针px和py所指向变量的值:5,3
【例8.11】编写函数swap,利用指针实现变量x和y的交换。 #include <stdio.h> void swap(int *p1,int *p2) //等价于void swap(int p1[],int *p2[]) { int t; t=*p1; *p1=*p2; *p2=t; } void main() { int x,y,*px,*py; x=3; y=5; px=&x; py=&y; swap(px,py); //等价于swap(&x,&y); printf("交换后的x和y为: "); printf(" x=%d,y=%d\n",x,y); printf("输出指针px和py所指向变量的值:"); printf(" %d,%d\n",*px,*py); 程序输出结果为: 交换后的x和y为:x=5,y=3 输出指针px和py所指向变量的值:5,3

44 数据交换的过程如图8.10所示。 本例中,采用指针作为函数的参数,完成 (a) 形实参结合示意图 (b)在被调函数中交换*p1和*p2
(c)返回主调函数后*px和*py的值 图8.10 指针作为函数参数的示意图

45 void swap(int *p1,int *p2) { int *p; p=p1; p1=p2; p2=p; }
请思考一下能否利用下面的函数实现x和y互换? void swap(int *p1,int *p2) { int *p; p=p1; p1=p2; p2=p; }

46 #include <stdio.h>
【例8.12】输出一维数组各元素的值。 #include <stdio.h> void MyPrint(int *arr,int n) //int *arr等价于int arr[] { int *p,i; for(p=arr,i=0;i<n;i++,p++) printf(" %5d",*p); } void main() int a[5]={1,2,3,4,5},*p=a; MyPrint(p,5); //等价于MyPrint(a,5); 程序输出结果为:

47 程序分析:为了便于存放连接后的新字符串,str1必须是字符数组名,且长度要足够大。参数str2可以是字符数组名或字符常量。
【例8.13】用函数调用实现两个字符串的连接。 程序分析:为了便于存放连接后的新字符串,str1必须是字符数组名,且长度要足够大。参数str2可以是字符数组名或字符常量。 #include <stdio.h> void con_str(char str1[ ],char str2[ ]) { int i,j,k; i=0; while(str1[i]!='\0') //求str1数组的长度 i++; for(j=i,k=0;str2[k]!='\0';k++,j++) //将str2连接在str1后面 str1[j]=str2[k]; str1[j]='\0'; } void main() { char a[50]= " Welcome to DeZhou"; char b[ ]= " Hello world!"; con_str(a,b); printf("连接后的字符串为:%s\n",pa); 程序输出结果为: 连接后的字符串为:Welcome to DeZhouHello world!

48 【例8.14】求二维数组各元素的和,并输出二维数组各元素
的值及它们的和。 #include <stdio.h> #define N 3 void arrayprint(int *p1,int d1,int d2) //输出二维数组各元素的值 { int i,j; for(i=0;i<d1;i++) { for(j=0;j<d2;j++) printf(" %5d",*(p1+i*d2+j)); printf(" \n"); } int arraysum(int array[ ][4]) //求二维数组各元素的和 long sum=0; for(i=0;i<N;i++) for(j=0;j<4;j++) sum=sum+array[i][j]; return sum;

49 for(i=0;i<3;i++) //输入二维数组各元素的值 for(j=0;j<4;j++)
void main( ) { int i,j,a[3][4]; int (*p)[4],sum; p=a; for(i=0;i<3;i++) //输入二维数组各元素的值 for(j=0;j<4;j++) scanf(" %d",*(p+i)+j); arrayprint(*a,3,4); sum=arraysum(a); printf(" sum=%ld\n",sum); } 程序输出结果为: 输入: 输出: sum=60

50 8.3.2 指向函数的指针 1.用指向函数的指针变量调用函数 指向函数的指针变量定义的一般形式为:
一个函数占用一段连续的内存单元,而函数名就是这段内存单元的首地址,因此,可以定义一个指向函数的指针变量,通过该指针变量调用该函数。一旦指针变量指向了某个函数,它便与函数名具有同样的作用。 指向函数的指针变量定义的一般形式为: 数据类型 (*指针变量名)(「形式参数列表」); 例如: long (*p)(); 定义了指针变量p,使其指向返回值为长整型的函数。 用指针变量调用函数的一般形式为: (*指针变量名) (实参表)

51 #include <stdio.h> void main() { int n; long jie1,jie2;
【例8.15】指向函数的指针变量使用示例。 #include <stdio.h> void main() { int n; long jie1,jie2; long (*p)(int x); //定义p为指向函数的指针,该函数有一个参数 long fac(int x); //被调函数声明 p=fac; //函数首地址赋给指针p scanf(" %d",&n); jie1=(*p)(n); //用函数指针变量形式调用函数 jie2=fac(n); //用函数名形式调用函数 printf("调用方法一:%d!=%ld\n",n,jie1); printf("调用方法二:%d!=%ld\n",n,jie2); } long fac(int x) { int i; long f=1; for(i=1;i<=x;i++) f=f*i; return f; 程序输出结果为: 输入:5 输出:调用方法一:5!=120 调用方法二:5!=120

52 函数的参数不仅可以用指针变量、数组名等,还可以用指向函数的指针作为函数参数。这时,形参为指向函数的指针变量,接收从实参传来的函数首地址。
2.指向函数的指针作为函数参数 函数的参数不仅可以用指针变量、数组名等,还可以用指向函数的指针作为函数参数。这时,形参为指向函数的指针变量,接收从实参传来的函数首地址。 【例8.16】在main主函数中输入a和b两个数,求它们的和与差。 #include <stdio.h> void main() { int add(int,int); //函数声明 int sub(int,int); //函数声明 int result(int a,int b,int (*p)(int,int)); //函数声明 int a,b,s1,s2; printf("输入a和b: "); scanf("%d,%d",&a,&b); s1=result(a,b,add); s2=result(a,b,sub); printf(" a+b=%d\n",s1); printf(" a-b=%d\n",s2); }

53 程序输出结果为: 输入a和b:38,12 输出: a+b=50 a-b=26 int add(int x,int y) { int sum;
sum=x+y; return(sum); } int sub(int x,int y) int diff; diff=x-y; return(diff); //下面语句中,p是指向函数的指针,该函数的返回值为整型,有两个整型形参 int result(int a,int b,int (*p)(int,int)) int val; val=(*p)(a,b); //形参p接收从实参传来的函数入口地址 return(val); 程序输出结果为: 输入a和b:38,12 输出: a+b=50 a-b=26

54 8.3.3 返回值为指针的函数 函数的返回值的类型除了整型、实型和字符型外,还可以是一个指针(即地址)。 返回值为指针的函数定义形式为:
数据类型 *函数名(形参表) { 函数体 }

55 程序输出结果为: 输入a和b: 23,10 a+b=33 【例8.17】返回值为指针的函数示例。
#include <stdio.h> void main() { int a,b,*p; int *add(int x,int y); printf("输入a和b: "); scanf(" %d,%d",&a,&b); p=add(a,b); printf(" a+b=%d\n",*p); } int *add(int x,int y) { int sum; sum=x+y; return(&sum); 程序输出结果为: 输入a和b: 23,10 a+b=33

56 8.3.4 main函数的参数 在C语言中,main函数两个参数,其一般形式为:
数据类型 main(int argc,char *argv[]) 其中,argc(第一个形参)必须是整型变量,用于存放操作系统命令行参数(包括命令)的个数;argv必须是指向字符串的指针数组,用于存放接收以字符串常量形式存放的命令行参数(包括命令本身)。main函数的这两个形参可以使用任意合法的标识符,但一般习惯使用argc和argv。

57 main函数不能被其它函数调用,但可以在命令行方 式下获取实参。可执行文件在命令行方式下一般执行形式为:
其中,可执行程序名和各参数之间用一个或多个空格分隔, 参数多少不限。 假设一个工程文件exam,经编译和链接后生成可执行程序exam.exe,则在命令行方式下输入: exam English Math<CR> 该命令包括一个文件名exam和两个参数English、Math,因此argc的值为3。argv[0]指向文件名exam,argv[1]指向参数English,argv[2]指向参数Math,如图8.11所示 。 图8.11 main函数参数值示意图

58 【例8.18】带参数的main函数示例程序。 #include <stdio.h> int main(int argc,char *argv[]) { printf("Welcome"); printf(" %s ",argv[1]); printf(" %s\n",argv[2]); printf(" argc=%d\n",argc); printf(" argv[1]=%s\n",argv[1]); printf(" argv[2]=%s\n",argv[2]); return(0); } 将此源文件加入到exam工程中,经编译和链接后,用命令行方式运行: exam to dezhou 程序输出结果为: Welcome to dezhou argc=3 argv[1]=to argv[2]=dezhou

59 8.4 典型例题 【例8.19】下列程序的输出结果是( )。 point(char *p) { p+ =2; } void main( )
【例8.19】下列程序的输出结果是( )。 point(char *p) { p+ =2; } void main( ) { char b[4]={'e','f','g','h'},*p=b; point(p); printf("%c\n",*p); } A. e B. f C. g D. h 程序分析:本例主要考察在调用函数时实参和形参之间的值传递问题。在本例中,形参为指针变量,实参也为指针变量,形参和实参各占用不同的内存单元,属于单向的值传递。 因此在main()函数中调用point函数时,形实参结合的过程只是将实参的值(即数组b的首地址)赋给形参,但形参值的改变并不影响实参。所以在main()函数中输出*p的值仍为e,正确答案为A。

60 【例8.20】求一个3行4列二维数组每行元素中的最大值。
程序分析:本程序的主要目的是确定3×4列数组每行中数据的最大值,因此首先对数组的每一行进行循环操作,然后对该数组的每一行求最大值。 #include <stdio.h> void main() { int a[3][4]={{12,41,36,28},{19,33,15,27},{3,27,19,1}}; void func(int m ,int n,int ar[][4],int *br); //函数声明 int b[3]; int i; func(3,4,a,b); for(i=0;i<3;i++) printf(" %4d",b[i]); printf(" \n"); }

61 void func(int m ,int n,int ar[][4],int *br)
{ int i,j,x; for(i=0;i<m;i++) x=ar[i][0]; for(j=0;j<n;j++) if(x<ar[i][j]) x=ar[i][j]; br[i]=x; }

62 【例8.21】将N行N列的二维数组中每一行的元素进行排序,第0行从小到大排序,第1行从大到小排序,第2行从小到大排序,第3行从大到小排序,例如:

63 #include <stdio.h>
#define N 4 void sort(int a[ ][N]) { int i,j,k,t; int (*p)[N]; /*定义p为指向一个有4个元素的一维数组的指针变量*/ p=a; //p指向第0行 for(i=0;i<N;i ++) { for(j=0;j<N-1;j ++) { for(k=j;k<N;k++) if(i%2==1 ? *(*(p+i)+j)<*(*(p+i)+k):*(*(p+i)+j)>*(*(p+i)+k)) { //根据行号判断是递增排序还是递减排序 t=*(*(p+i)+j); *(*(p+i)+j)=*(*(p+i)+k); *(*(p+i)+k)=t; }

64 void outarr1(int a[N][N])
{ //用指向数组的指针处理矩阵的输出 printf("this array is :\n"); int *p=a[0]; for(int i=0;i<N;i ++) for(int j=0;j<N;j++) printf(" %4d ",*(p+i*N+j)); printf(" \n"); }

65 void outarr2(int a[N][N])
{ //用指向一维数组的行指针处理矩阵的输出 printf("this array is :\n"); int (*p)[N]; //定义p为指向一个有4个元素的一维数组的指针变量 p=a; //p指向第0行 for(int i=0;i<N;i ++) for(int j=0;j<N;j++) printf(" %4d ",*(*(p+i)+j)); printf(" \n"); }

66 int main() { int arr[N][N]={{2,3,4,1},{8,6,5,7},{11,12,10,9},{15,14,16,13}}; outarr1(arr); //用指向数组的指针处理矩阵的输出 sort(arr); //用一维数组指针对矩阵的数据进行排序 printf("the array has been sorted\n"); outarr2(arr); //用指向一维数组的行指针处理矩阵的输出 return 1; }

67 【例8.22】对一段英文文字进行加密、解密,本题采用最简单的字符替代法,譬如将a替换为b,b替换为c,…,z替换为a。其中标点符号和空格不进行加密处理。
程序分析:本程序中,对英文文字进行加密、解密。要加密的文字存放在字符串中,加密密钥采用最简单的顺序替代方法,存在一个字符变量key中,key表示当前待加密字符用其后面的第几个字符来替代,如果密钥key=1,则加密数据中依次是a(A)b(B),b(B)c(C)…z(Z)a(A);如果密钥key=2,则加密数据中依次是a(A)c(C),b(B)d(D)…z(Z)b(B),依此类推…。解密过程反之。

68 //字符加密函数 #include <stdio.h> #include <string.h>
#include <stdlib.h> //字符加密函数 void encode(char *szBuff,int nBuffSize,char key) { char szkey = key ; for(int i = 0; i<nBuffSize; i ++) { //只对a~z或者A~Z中的字符进行加密 if( 'a'<= *(szBuff+i) && *(szBuff+i)<= 'z' || ‘ A'<= *(szBuff+i) && *(szBuff+i)<= 'Z' ) if( 'a' <=(*(szBuff+i)+szkey) && (*(szBuff+i)+szkey)<= 'z' ||'A' <=(*(szBuff+i)+szkey) && (szBuff[i]+szkey)<= 'Z') *(szBuff+i)+=szkey; else *(szBuff+i)+=szkey-26; //保证循环加密正确 }

69 //字符解密函数 void decode(char *szBuff,int nBuffSize,char key) {
char szkey=key; for(int i=0; i<nBuffSize; i++) { //只对a~z或者A~Z中的字符进行密 if('a' <=*(szBuff+i) && *(szBuff+i)<= 'z' || 'A' <=*(szBuff+i) && *(szBuff+i)<= 'Z' ) if('a' <=(*(szBuff+i)-szkey) && (*(szBuff+i)-szkey)<= 'z' || 'A' <=(*(szBuff+i)-szkey) && (*(szBuff+i)-szkey)<= 'Z') *(szBuff+i)-=szkey; else *(szBuff+i)-=szkey-26; //保证循环解密正确 }

70 main() { char szBuff[256]; char ckey; //加密部分 printf(" \nplease input the string will be encripted:\n"); scanf(" %s",szBuff); getchar(); printf(" \nplease input the key will be encripted:\n"); scanf(" %d",&ckey); ckey=ckey % 26; //保证密钥key数值在0~25之间 encode(szBuff, strlen(szBuff),ckey); printf(" \nthe result string encripted is: %s \n" ,szBuff); //解密部分 printf(" \nplease input the string will be decripted:\n"); printf(" \nplease input the key will be decripted:\n"); ckey=ckey % 26; decode(szBuff, strlen(szBuff),ckey); printf(" \nthe result string decripted is :%s \n" ,szBuff); return 0; }

71 谢谢!祝大家学习进步!!


Download ppt "C语言程序设计教程 单位:德州学院计算机系."

Similar presentations


Ads by Google