350 likes | 470 Views
§10.3 指针数组和指向指针的指针. 10.3.1 指针数组 指针数组 数组的每个元素均存放地址 , 即每个元素是一个指针变量. 格式 类型标识符 *数组名 [ 数组长度 ] 例如 int *p[4]; 定义了 p 是数组 , 有 4 个元素 , 每个元素均为整型指针变量. 操作 指针数组的元素使用同单个指针变量。. 例 10-9 指针数组示例. main() {int i=1,j=2,k=3,m=4; int *p[4],n; p[0]=&i; p[1]=&j;
E N D
§10.3 指针数组和指向指针的指针 10.3.1指针数组 指针数组数组的每个元素均存放地址,即每个元素是一个指针变量 格式 类型标识符 *数组名[数组长度] 例如 int *p[4]; 定义了 p是数组,有4个元素,每个元素均为整型指针变量 操作 指针数组的元素使用同单个指针变量。
例10-9 指针数组示例 main() {int i=1,j=2,k=3,m=4; int *p[4],n; p[0]=&i; p[1]=&j; p[2]=&k; p[3]=&m; for(n=0;n<4;n++) printf("%d ",*p[n]); for(n=0;n<4;n++) printf("%x ",p[n]); } i 1 p p[0] j 2 p[1] p[2] k 3 p[3] m 4 输出 1 2 3 4 i、j、k、m的地址
i i 1 1 p p p[0] p[0] &i &m j j 2 2 p[1] p[1] &j &j p[2] p[2] &k &k k k 3 3 p[3] p[3] &i &m m m 4 4 i 1 p p[0] &i j 2 p[1] &j p[2] &k k 3 p[3] &m m 4 例10-10写出下列程序的运行结果 void main() {int i=1,j=2,k=3,m=4,n,t; int *p[4],*pt; p[0]=&i;p[1]=&j;p[2]=&k;p[3]=&m; pt=p[0];p[0]=p[3];p[3]=pt; for(n=0;n<4;n++) printf("%d ",*p[n]); printf("\n"); p[0]=&i;p[1]=&j;p[2]=&k;p[3]=&m; t=*p[0];*p[0]=*p[3];*p[3]=t; for(n=0;n<4;n++) printf("%d ",*p[n]); printf("\n"); } 输出:4 2 3 1 运行结果如下: 4 2 3 1 4 2 3 1 输出:4 2 3 1
10.3.2指向指针的指针变量 指向指针的指针变量一个指针变量指向另一个指针变量 如设 i 为整型变量,p为指针变量,pp为指向指针的指针变量 a=10; p=&a; pp=&p; 则 p pp a &p &a 10 指向指针的指针变量的定义 格式 类型标识符 **指针变量名; 如 int **pp; char **gg; 使用时有三种形式 pp, *pp, **pp pp表示指向指针的指针变量的地址值 如 &p *pp表示指向指针变量的地址值 如 &a **pp表示指向指针变量所指单元的值 如 10
例10-11 写出运行结果 #include<stdio.h> void main() {int a,*p,**pp; p=&a; pp=&p; a=10; printf("a=%d,*p=%d,**pp=%d\n",a,*p,**pp); **p=20; printf("a=%d,*p=%d,**pp=%d\n",a,*p,**pp); **pp=30; printf("a=%d,*p=%d,**pp=%d\n",a,*p,**pp); } p pp a &p &a *pp **pp 运算输出 a=10,*p=10,**pp=10 a=20,*p=20,**pp=20 a=30,*p=30,**pp=30
ppa pa a &pb &a 10 pb ppb b &pa &b 20 例10-12对如下变量定义和初始化,依次执行操作(1)~(3)后,请分析部分变量的值。 pa ppa a int a=10,b=20,t; int *pa=&a,*pb=&b,*pt; int **ppa=&pa,**ppb=&pb,**ppt; &pa &a 10 pb ppb b **ppa **ppb *pa *pb a b 10 20 10 20 10 20 &pb &b 20 操作(1): ppt=ppb;ppb=ppa;ppa=ppt; pa ppa a &pa &a 10 pb ppb b &pb &b 20 **ppa **ppb *pa *pb a b 20 10 10 20 10 20
pa ppa a &pb &b 10 pb ppb b &pa &a 20 pa pa ppa ppa a a &pb &pb &b &b 10 20 pb pb ppb ppb b b &pa &pa &a &a 20 10 再操作(2): pt=pb;pb=pa;pa=pt; pa ppa a &pb &a 10 pb ppb b &pa &b 20 **ppa **ppb *pa *pb a b 10 20 20 10 10 20 再操作(3): t=b;b=a;a=t; **ppa **ppb *pa *pb a b 20 10 10 20 20 10
间接访问利用指针变量访问另一个变量 利用指针变量访问一个变量值,称为间接访问,叫单级间址 利用指向指针的指针变量访问一个变量值称为间接的间接访问, 叫二级间址。依次类推可以延伸更多的多级间址。 指针变量 变量 单级间址 地址 变量值 指向指针的指针 指针变量 变量 二级间址 地址1 地址2 变量值 指向指针的指针的指针 三级间址 地址1 指针变量 变量 指向指针的指针 地址3 变量值 地址2
例 main() {int i,*q,**p,***s; i=10; q=&i; p=&q; s=&p; printf("s=%o\n",s); printf("*s=%o\n",*s); printf("**s=%o\n",**s); printf("***s=%o\n",***s); } 运行结果 s=4577564 *s=4577570 **s=4577574 ***s=12 注使用三级间址在二级间址的“**”再加上一个“*”,多级 间址依次类推。
10.3.3指针数组、二维字符数组和字符串 存储多个字符串,通常使用二维字符数组(字符串数组 )。 如 char a[][6]={"Wang","Li","Zheng","Jin","Xian"}; a 字符串具有两个要点: 起始地址和结束符'\0' 二维数组的行元素是行起始地址 显然二维数组一行是一个字符串 a[0] a[1] a[2] a[3] a[4] 例 main() {int i;char a[5][6]={"Wang","Li","Zheng","Jin","Xian"}; for(i=0;i<5;i++) printf("%s",a[i]); } 输出: Wang Li Zheng Jin Xian
pname W a n g \0 L i \0 Z h e n g \0 J i n \0 X i a n \0 由于二维数组的列必须确定长度, 要按字符串中的最大长度确定列数,这样做浪费了内存资源 a 如改用指向字符串的指针数组 char *pname[] ={"Wang","Li","Zheng","Jin","Xian"}; 例 main() {int i; char *pname[] ={"Wang","Li","Zheng","Jin","Xian"}; for(i=0;i<5;i++) printf("%s", pname[i]); } 输出: Wang Li Zheng Jin Xian
month_name[0] month_name[1] month_name[2] month_name[3] month_name[4] month_name[5] month_name[6] month_name[7] month_name[8] month_name[9] 例10-13输入月份,输出对应的英文名称。例如,输入5,输出May 分析 定义一个有13个元素的指针数组month_name,首元素指向一个空字符串,其系元素依次指向一个英文月份的名称,数组下标对应月份,输出对应的字符串 void main() {int month; char *month_name[]={"","January","February","March","April", "May","June","July","August","September","October","November", "December"}; printf("Enter month:\n"); scanf("%d",&month); if(month>=1&&month<=12) printf("%s\n",month_name[month]; else printf("Illegal month"); }
例10-14将5个字符串从小到大排序后输出。(用冒泡法)例10-14将5个字符串从小到大排序后输出。(用冒泡法) 分析 与整数排序类同,但数据类型不同 void sort(char*a[],int n) {int i,j; char*t; for(i=0;i<n-1;i++) for(j=0;j<n-1-i;j++) if(strcmp(a[j],a[j+1])>0) {t=a[j];a[j]=a[j+1];a[j+1]=t;} } main() {int i;char*a[5]={"Wang","Li", "Zheng","Jin","Xian"}; sort(a,5); for(i=0;i<5;i++) printf("%s ",a[i]); } void sort(int a[],int n) {int i,j;intt; for(i=0;i<n-1;i++) for(j=0;j<n-1-i;j++) if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} } main() {int i;inta[5]={6,5,2,81}; sort(a,5); for(i=0;i<5;i++) printf("%d ",a[i]); }
注:指针数组的元素所指向各自的字符串的,需要交换时,直接交换指针数组元素的值,即改变它们的指向。注:指针数组的元素所指向各自的字符串的,需要交换时,直接交换指针数组元素的值,即改变它们的指向。 void sort(char*a[],int n) {int i,j;char*t; for(i=0;i<n-1;i++) for(j=0;j<n-1-i;j++) if(strcmp(a[j],a[j+1])>0) {t=a[j];a[j]=a[j+1];a[j+1]=t;} } main() {int i;char*a[5]={"Wang","Li", "Zheng","Jin","Xian"}; sort(a,5); for(i=0;i<5;i++) printf("%s ",a[i]); } a W a n g \0 a[0] a[1] L i \0 Z h e n g \0 a[2] a[3] J i n \0 a[4] X i a n \0 a W a n g \0 a[0] a[1] L i \0 Z h e n g \0 a[2] a[3] J i n \0 a[4] X i a n \0
指针数组做 改为二维字符数组做 void sort(char *a[],int n) {int i,j;char *t; for(i=0;i<n-1;i++) for(j=0;j<n-1-i;j++) if(strcmp(a[j],a[j+1])>0) {t=a[j];a[j]=a[j+1];a[j+1]=t;} } main() {int i;char *a[5]={"Wang","Li", "Zheng","Jin","Xian"}; sort(a,5); for(i=0;i<5;i++) printf("%s",a[i]); } void sort(char a[][6],int n) {int i,j;char t[6]; for(i=0;i<n-1;i++) for(j=0;j<n-1-i;j++) if(strcmp(a[j],a[j+1])>0) {strcpy(t,a[j]); strcpy(a[j],a[j+1]); strcpy(a[j+1],t);} } main() {int i;char a[5][6]={"Wang","Li", "Zheng","Jin","Xian"}; sort(a,5); for(i=0;i<5;i++) printf("%s",a[i]); }
二维字符数组做 指针数组做 a a a[0] W a n g \0 a[0] a[1] a[1] L i \0 a[2] Z h e n g \0 a[2] a[3] a[3] J i n \0 a[4] a[4] X i a n \0 a W a n g \0 a[0] a[0] a[1] a[1] L i \0 Z h e n g \0 a[2] a[2] a[3] J i n \0 a[3] a[4] X i a n \0 a[4] 注二维字符数组多用了内存 二维字符数组直接改变存储单元字符串内容,而指针数组改变指针所指的方向
1 a[0] p n[0] &i a[1] 3 &j n[1] &k n[2] a[2] 5 &m n[3] a[3] 7 10.3.4指针数组和二级指针 指针数组的每个元素是指针变量,指向指针数组元素的指针变量 必须是二级指针 例 main() {int a[5]={1,3,5,7}; int *n[4]={&a[0],&a[1], &a[2],&a[3]}; int **p,i; p=n; for(i=0;i<4;i++) { printf("%d\t",**p);p++; } } 运算结果 1 3 5 7
例 main() {int i,j;char *a[5]={"Wang","Li","Zheng","Jin","Xian"},**p; p=a; for(i=0;i<5;i++) printf("%s %s %s %s\n ",a[i],*(a+i),p[i],*(p+i)); i=0;j=2; printf("%c %c %c %c\n",*(a[i]+j),*(*(a+i)+j),*(p[i]+j),*(*(p+i)+j)); } 输出: Wang Wang Wang Wang Li Li Li Li Zheng Zheng Zheng Zheng Jin Jin Jin Jin Xian Xian Xian Xian n n n n a[0]+2 p,a, a+0 a+1 W a n g \0 a+2 L i \0 a+3 Z h e n g \0 a+4 J i n \0 X i a n \0
改为二维数组存放字符串组 char a[][6]={"Wang","Li","Zheng","Jin","Xian"}; a[0]+2,&a[0][2] a, a+0 a+1 a+2 a+3 a+4 注:a+i,a[i]是行指针,也可用*(a+i)表示 但不能用指向指针的指针变量(二级指针)指向二维数组的字符串 char a[5][6],**p; p=a; 不允许 而是用要用行指针指向字符串,行指针定义: char (*p)[6]; 下标表示字符串的长度
main() {int i,j;char a[5][6]={"Wang","Li","Zheng","Jin","Xian"},(*p)[6]; p=a; for(i=0;i<5;i++) printf("%s %s %s %s %s\n ",a+i,*(a+i),a[i],p[i],*(p+i)); i=0;j=2; printf( "%c %c %c %c %c\n",a[i][j],*(a[i]+j),*(*(a+i)+j),*(p[i]+j),*(*(p+i)+j)); } 输出: Wang Wang Wang Wang Wang Li Li Li Li Li Zheng Zheng Zheng Zheng Zheng Jin Jin Jin Jin Jin Xian Xian Xian Xian Xian n n n n n
10.3.5命令行参数(指针数组作为main( )函数的形参) 一般使用main函数无参数 定义main( ) {... } 其实 main( )函数可以带2个参数 定义main( int argc, char *argv[]) {... } 其中 argc 是整型变量 *argv[] 是指向字符串的指针数组 注argc,argv这两个参数是系统规定的;但参数名可以任取,习惯上 按上述名取。
利用命令行传递参数 格式 命令名 参数1,参数2,...,参数n C语言程序经过编译、连接产生执行文件 文件名.exe 则文件名就是命令名,而后的实参相应传递给这两个参数 其中 argc 存放命令行的字符串个数,包括命令名 n+1 argv[0] 存放命令名(tc包括路径) argv[1] 存放参数1 argv[2] 存放参数2 ... argv[n] 存放参数n
例 void main(argc,argv) int argc; char *argv[]; {int i; for(i=0;i<argc;i++) printf("%s\n",argv[i]); } 存于文件file.c,经过编译、连接产生执行文件file.exe 运行:D:\c_now\10\file\debug>file China Beijing argc=3 argv 输出:file China Beijing
对于如下程序test.c, 编译后运行 test hello world 将输出_________: #include <stdio.h> main(int agrc, char *argv[]) { printf("%d %s", argc, argv[1]+1); } 答案: 3 ello 将程序prog.c编译后运行:prog –nla hello world 则 *(*(argv+2)) 是______。 A、'p' B、'-' C、'h' D、'w' 答案: C
§10.4 指针和函数 10.4.1 指针作为函数的返回值 函数返回值可以是数值、字符同样也可以返回地址值(指针) 1.返回指针的函数的定义 格式 类型标识符 *函数名(参数表) 参数说明; { return(指针); } 与一般函数定义区别 (1)在函数名前多了* (2)在 return语句中要求地址值 例如 int *a(x,y) int x,y; {int *p; … return(p); } 2.返回指针的函数调用形式同一般函数调用 函数名(实参表)
例10-18输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的字符。例10-18输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的字符。 例如,输入字符r和字符串program后,输出rogram。 要求定义函数match(s,ch)在字符串,中查找字符ch,如果找到,返回第一次找到的该字符在字符串中的位置(地址);否则,返回空指针NULL。 分析:match(s,ch)返回一个地址,所以函数返回值的类型是指针。 char *match(char *s,char ch) {while(*s!='\0') if(*s==ch) return s; else s++; return(NULL); } main() {char ch,str[80],*p=NULL; scanf("%s",str);ch=getchar(); if((p=match(str, ch))!=NULL) printf("%s\n",p); else printf("Not found\n");} 输入: program r str s 输出: rogram
例10-19定义函数mystrcpy(char *s,char *t),将t复制到s,并返回s的值。 分析:要求返回s的值,即返回字符串的起始地址。 char *mystrcpy(char *s,char *t) { char *ss=s; while(*s++=*t++) return ss; } 例10-8 void strcpy(char *to,char *from) /*第2.2版*/ { while(*to++=*from++) } 注: 由于在函数中改变了指针s的值,又定义了一个指针ss保存s的初值
10.4.2函数指针和指向函数的指针变量 函数指针函数在编译时分配内存给一个入口地址,这个入口地址以 函数名表示,称为函数指针。所谓入口地址函数编译后成为若干条 指令依次存储在内存,第一条指令存储的单元地址称为入口地址 指向函数的指针变量指针变量的值是函数的入口地址 1.用指向函数的指针变量调用函数 例 求a和b的大值 max max(x,y) int x,y; {int z; if(x>y) z=x; else z=y; return(z); } main() {int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("%d\n",c); }
若使用指向函数指针变量调用函数 main() {int a,b,c,(*p)(); scanf("%d,%d",&a,&b); p=max; c=p(a,b); printf("%d\n",c); } max(x,y) int x,y; {int z; if(x>y) z=x; else z=y; return(z); } max p 2.指向函数的指针变量定义 格式 类型标识符 (*指针变量名)( ); 其中 (1)类型标识符为指向函数的返回值的类型 如 int (*p)( ); 返回值为整型 (2) (*指针变量名)( )表示定义一个指向函数的指针变量,专门 用来存放函数的入口地址
3.指向函数的指针变量调用函数 (1)将函数的入口地址赋于指针变量 格式 指针变量名=函数名; 如上例中 p=max; (2)调用形式 格式(*指向函数的指针变量名)(函数实参表) 或 指向函数的指针变量名(函数实参表) 如上例中 c=p(a,b); 或 c= (*p)(a,b); 注 函数的指针变量只能用于调用函数,而不能进行指针变量的个各 种运算。如 p++,p+i,*p+i 是不允许
例10-20函数指针示例 #include <stdio.h> int odd(int x) {return x%2!=0;} int even(int x){return x%2==0;} void main() {int r1,r2,r3,r4; int (*fp)(); /*定义函数指针*/ fp=odd; /*将函数odd的入口地址赋给函数指针*/ rl=odd(5); /*通过函数名调用函数odd*/ r2=(*fp)(5); /*通过函数指针调用函数odd*/ printf("%d,%d\n",r1,12); fp=even; /*将函数even的入口地址赋给函数指针*/ r3=even(5); /*通过函数名调用函数even*/ r4=(*fp)(5); /*通过函数指针调用函数even*/ printf("%d,%d\n",r3,r4); } 运行 1,1 0,0
4. 函数指针作为函数参数 形参为指向函数的指针变量(函数指针) 实参可为函数名或已赋值的函数指针 虚实结合:形参指向实参所代表函数的入口地址。 指向函数的指针变量作为函数参数给模块编程带来较大的灵活性 例10-21编一个求任意函数的定积分,用梯形公式积分 然后计算 分析:先编一个任意函数的定积分 函数模块 然后调用函数实现定积分。
编函数,按积分的几何意义采取数值积分 分析 对 f(a+i*h) f(a+(i-1)h) 等分n个小梯形 f(x) 小梯形宽: h 小梯形面积: n i 1 2 x a+2h a b a+(i-1)h 0 a+h a+i*h 则梯形积分公式 函数的参数决定: 已知a,b,n和f(x) 求积分值
则梯形积分公式 double calc(f,a,b,n) double (*f)(),a,b;int n; {int i; double h,s; s=((*f)(a)+(*f)(b))/2; h=(b-a)/n; for(i=1;i<n;i++) s=s+(*f)(a+i*h); s=s*h; return(s); } double f1(double x) {return(x*x);} double f2(double x) {return(sin(x)/x);} double f3(double x) {return(x/(1+x*x));} void main() {float y1,y2,y3; y1=calc(f1,0.0,1.0,100); y2=calc(f2,1.0,2.0,100); y3=calc(f3,0.0,1.0,100); printf("y1=%f\n",y1); printf("y2=%f\n",y2); printf("y3=%f\n",y3); } 运行: y1=0.333350 y2=0.659329 y3=0.346565
习题10(2) 1.选择题 (5)、(6) 2.阅读题 (4)、(5) 编程题 7, 9,11 概念题 12