440 likes | 564 Views
第 11 章 指针进阶. 11.1 指针数组. 1. 指针数组概念. 指针数组 数组的每个元素均存放地址 , 即每个元素是一个指针变量. 格式 类型标识符 *数组名 [ 数组长度 ] 例如 int *p[4]; 定义了 p 是数组 , 有 4 个元素 , 每个元素均为整型指针变量. 操作 指针数组的元素使用同单个指针变量。. 例 指针数组示例. main() {int i=1,j=2,k=3,m=4; int *p[4],n; p[0]=&i; p[1]=&j;
E N D
第11章 指针进阶 11.1指针数组 1.指针数组概念 指针数组数组的每个元素均存放地址,即每个元素是一个指针变量 格式 类型标识符 *数组名[数组长度] 例如 int *p[4]; 定义了 p是数组,有4个元素,每个元素均为整型指针变量 操作 指针数组的元素使用同单个指针变量。
例 指针数组示例 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的地址
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] 2.指针数组用于处理多个字符串 例 输入月份,输出对应的英文名称。例如,输入5,输出May 分析 定义一个有13个元素的指针数组month_name,首元素指向一个空字符串,其系元素依次指向一个英文月份的名称,数组下标对应月份,输出对应的字符串 输入 5 输出 May 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"); }
例11-1已知一个不透明的布袋里装有红、蓝、黄、绿、紫同样大小的圆球各一个,现从中一次抓出两个,问可能抓到的是什么颜色的球?例11-1已知一个不透明的布袋里装有红、蓝、黄、绿、紫同样大小的圆球各一个,现从中一次抓出两个,问可能抓到的是什么颜色的球? 1 red blue 2 red yellow 3 red green 4 red purple 5 blue red 6 blue yellow 7 blue green 8 blue purple …… 15 green yellow 16 green purple 17 purple red 18 purple blue 19 purple yellow 20 purple green 3 排列 A5 =5*4=20种可能 #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=0;j<=4;j++) { /* j代表第二个球对应的颜色下标 */ if(i==j) continue; /* 两个球不能同色 */ count++; printf("%6d", count); printf("%10s %10s\n", color[i], color[j]); } }
3 组合 C5 =5*4/2=10种可能 #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]); } } 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
3.二维数组用于处理多个字符串 如 char color[][7]={"red","blue","yellow","green","purple"}; color[0] 二维数组的行元素是 行起始地址 显然二维数组一行是 一个字符串 color[1] color[2] color[3] color[4] 例 main() {int i; char color[][7]={"red","blue","yellow","green","purple"}; for(i=0;i<5;i++) printf("%s",color[i]); } 输出: red blue yellow green purple
pcolor r e d \0 b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \0 4.二维数组与指针数组用于处理多个字符串的不同点 (1)内存占有量 由于二维数组的列必须确定长度, 要按字符串中的最大长度确定列数,这样做浪费了内存资源 如 char color[][7]={"red","blue","yellow","green","purple"}; color[0] color[1] color[2] color[3] color[4] 如用指向字符串的指针数组,节约内存资源 char *pcolor[] ={"red","blue","yellow","green","purple"};
(2)二维数组直接改变存储单元字符串内容,而指针数组改变指针所指的方向(2)二维数组直接改变存储单元字符串内容,而指针数组改变指针所指的方向 例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[]={"red","blue", "yellow","green","purple"}; 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]); }
a a[0] r e d \0 a[1] b l u e \0 a[2] a g r e e n \0 a[3] r e d \0 a[4] b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \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[]={"red","blue", "yellow","green","purple"}; sort(a,5); for(i=0;i<5;i++) printf("%s ",a[i]); } a[0] a[1] a[2] a[3] a[4] y e l l o w \0 p u r p l e \0
注:二维字符数组直接改变存储单元字符串内容注:二维字符数组直接改变存储单元字符串内容 void sort(char a[][7],int n) {int i,j;char t[7]; 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][7]={"red","blue", "yellow","green","purple"}; sort(a,5); for(i=0;i<5;i++) printf("%s",a[i]); } a[0] a[1] a[2] a[3] a[4] a[0] a[1] a[2] a[3] a[4]
5.动态输入多个字符串 当字符串是已知的,通过初始化的方式对指针数组赋值 若字符串要通过输入语句输入,可用动态申请内存输入 例11-6输入一些球的颜色,以#作为输入结束标志,再输出这些颜色。 其中颜色数小于20,颜色的英文名称不超过10个字符。 #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]); } 请输入颜色名称,每行一个,#结束输入: red blue yellow # 你输入的颜色是:red blue yellow
11.2指向指针的指针变量 1.二级指针 指向指针的指针变量一个指针变量指向另一个指针变量 如设 i 为整型变量,p为指针变量,pp为指向指针的指针变量 a=10; p=&a; pp=&p; 则 p pp a &p &a 10 指向指针的指针变量的定义 格式 类型标识符 **指针变量名; 如 int a,*p,**pp; a=10; p=&a; pp=&p; 使用时有三种形式 pp, *pp, **pp pp表示指向指针的指针变量的地址值 如 &p *pp表示指向指针变量的地址值 如 &a **pp表示指向指针变量所指单元的值 如 10
ppa pa a pa ppa a &pb &a 10 &pa &a 10 pb ppb b pb ppb b &pa &b 20 &pb &b 20 例11-4对如下变量定义和初始化,依次执行操作(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; **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
2.多级指针 间接访问利用指针变量访问另一个变量 利用指针变量访问一个变量值,称为间接访问,叫单级间址 利用指向指针的指针变量访问一个变量值称为间接的间接访问, 叫二级间址。依次类推可以延伸更多的多级间址。 指针变量 变量 单级间址 地址 变量值 指向指针的指针 指针变量 变量 二级间址 地址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 注使用三级间址在二级间址的“**”再加上一个“*”,多级 间址依次类推。
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 3.指针数组和二级指针 指针数组的每个元素是指针变量,指向指针数组元素的指针变量 必须是二级指针 例 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
a r e d \0 b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \0 例 main() {int i,j;char *a[5]={"red","blue","yellow","green","purple"},**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)); } 输出: red red red red blue blue blue blue yello yello yellow yello green green green green purple purple purple purple d d d d a[0]+2 p, a+0 a+1 a+2 a+3 a+4
a r e d \0 b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \0 改为二维数组存放字符串组 例 main() {int i,j;char a[5][7]={"red","blue","yellow","green","purple"},**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)); } a[0]+2 a+0 a+1 a+2 a+3 a+4 注:不能用指向指针的指针变量(二级指针)指向二维数组的字符串 上例程序中 p=a; 不允许
a r e d \0 b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \0 二维数组存放字符串与一级指针 例 main() {int i,j;char a[5][7]={"red","blue","yellow","green","purple"},*p; p=&a[0][0];/*不能p=a;*/ for(i=0;i<5;i++) printf("%s %s %s\n",a[i],*(a+i),(p+i*7)); i=0;j=2; printf("%c %c %c %c\n",a[i][j],*(a[i]+j),*(*(a+i)+j),*(p+i*7+j)); } 输出: red red red blue blue blue yello yello yellow green green green purple purple purple d d d d a[0]+2 p, a+0 a+1 a+2 a+3 a+4
a r e d \0 b l u e \0 y e l l o w \0 g r e e n \0 p u r p l e \0 二维数组存放字符串与行指针 main() {int i,j;char a[5][7]={"red","blue","yellow","green","purple"},(*p)[7]; p=a;/*或p=a[0]*/ 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)); } a[0]+2 输出: red red red red blue blue blue blue yello yello yellow yello green green green green purple purple purple purple d d d d p, a+0 a+1 a+2 a+3 a+4
11.3命令行参数 指针数组作为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\11\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
11.4 指针作为函数的返回值 函数返回值可以是数值、字符同样也可以返回地址值(指针) 1.返回指针的函数的定义 格式 类型标识符 *函数名(参数表) 参数说明; { return(指针); } 与一般函数定义区别 (1)在函数名前多了* (2)在 return语句中要求地址值 例如 int *s(int x,int y) {int *p; … return(p); } 2.返回指针的函数调用形式同一般函数调用 函数名(实参表)
例11-9输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的字符。例11-9输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的字符。 例如,输入字符串program和字符r后,输出rogram。 定义函数int match(char *s,char ch)在字符串s,中查找字符ch,如果找到,返回第一次找到的该字符在字符串中的次序位置;否则,返回-1。 int match(char *s,char ch) {int i=0; while(*s!='\0') if(*s==ch) break; else {s++;i++;} if(*s== '\0') i=-1; return(i); } void main() {char ch,str[80];int p; scanf("%s",str); getchar();/*读取'\n'*/ ch=getchar(); if((p=match(str, ch))!=-1) printf("%s\n",str+p); else printf("Not found\n");} 输入: program r str 1 输出: rogram
改用求函数返回值指针做. 定义函数char *match(char *s,char ch)在字符串s,中查找字符ch,如果找到,返回第一次找到的该字符在字符串中的位置(地址);否则,返回空指针NULL。 void main() {char ch,str[80],*p=NULL; scanf("%s",str); getchar(); ch=getchar(); if((p=match(str, ch))!=NULL) printf("%s\n",p); else printf("Not found\n");} char *match(char *s,char ch) {while(*s!='\0') if(*s==ch) return s; else s++; return(NULL); } 输入: program r str 输出: rogram s
例11-8输入一首藏头诗(假设只有4句),输出其真实含义。例11-8输入一首藏头诗(假设只有4句),输出其真实含义。 藏头诗:每一句的第一个字连起来,该内容就是该诗的真正含义。 #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); } 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; } 请输入藏头诗: 一叶轻舟向东流, 帆梢轻握杨柳手, 风纤碧波微起舞, 顺水任从雅客悠。 一帆风顺 注:一个汉字两个字节表示
在字符串中怎么判别是汉字? 一个汉字两个ASCII联合表示,最高位置1 例 输入一行字符,输出其中的汉字 #include <stdio.h> void main() {int i,j=0;char s[100],h[100]; gets(s); for(i=0;s[i]!='\0';i++) if(s[i]<0) {h[j++]=s[i++];h[j++]=s[i];} h[j]='\0'; printf("%s\n", h); }
注:返回的指针不能指向临时区域 例 char *mm(int s) {char ch[]=“aasss”; if(s>0) return ch; else return ch+2; } main() {int a; scanf("%d",&a); printf("%s\n",mm(a)); } 例11-8 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; } 但 在函数内定义的字符串可用指针返回字符串 若char *ch=“aasss”;或static char ch[]=“aasss”; 能返回
11.5指向函数的指针变量 函数指针函数在编译时分配内存给一个入口地址,这个入口地址以 函数名表示,称为函数指针。所谓入口地址函数编译后成为若干条 指令依次存储在内存,第一条指令存储的单元地址称为入口地址 指向函数的指针变量指针变量的值是函数的入口地址 1.用指向函数的指针变量调用函数 例 求a和b的大值 max int max(x,y) int x,y; {int z; if(x>y) z=x; else z=y; return(z); } void main() {int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("%d\n",c); }
若使用指向函数指针变量调用函数 void main() {int a,b,c,(*p)(); scanf("%d,%d",&a,&b); p=max; c=p(a,b); printf("%d\n",c); } int 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 是不允许
例 函数指针示例 #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. 函数指针作为函数参数 形参为指向函数的指针变量(函数指针) 实参可为函数名或已赋值的函数指针 虚实结合:形参指向实参所代表函数的入口地址。 指向函数的指针变量作为函数参数给模块编程带来较大的灵活性 例11-10编一个求任意函数的定积分,用梯形公式积分 然后计算 分析:先编一个任意函数的定积分 函数模块 然后调用函数实现定积分。
编函数,按积分的几何意义采取数值积分 分析 对 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
11.6用typedef定义类型 C语言的数据类型有 标准类型int,char,float,double,long,unsigned 构造类型struct,union,enum 指针类型、 空类型 使用构造类型可以定义新的不同类型 例 struct stud_type {int num; char name[20]; int score; }; struct stud_type student; 上述 struct stud_type 视为类型不直观。可用typedef改名
格式typedef 原类型名 新类型名表; 例如typedef int INTEGER,NUM; INTEGER a; NUM b; INTEGER,NUM是整型类型 a,b为整型变量 又例struct stud_type {int num; char name[20]; int score; }; typedef struct stud_type STUDENT STUDENT stud; STUDENT定义为 struct stud_type结构类型 stud为struct stud_type类型变量 即 typedef struct stud_type {int num; char name[20]; int score; }STUDENT; STUDENT stud;
NUM是整数数组类型 n为数组变量n[100] 再例typedef int NUM[100]; NUM n; typedef char *STRING; STRING p,s[10]; STRING是字符指针类型 p为指针变量,s为指针数组 typedef int (*POINTER)( ); POINTER p1,p2; POINTER是指针函数类型 p1,p2为指针函数变量 注仅取名作新类型关键字
例 用typedef重新定义数据类型 typedef int NUM; main() {NUM a,b; a=5;b=6; printf("a=%d\tb=%d\n"a,b); {float NUM; NUM=3.0; printf("2*NUM=%.2f\n",2*NUM); } } 输出: a=5 b=6 2*NUM=6.00
注(1)typedef不能定义新的数据类型,只不过是将一种类型名用另注(1)typedef不能定义新的数据类型,只不过是将一种类型名用另 一种类型名来表示 (2)习惯上新的类型名使用大写字母 (3)使用typedef能使用户按习惯的类型名表示各种标准和使结构 体、共同体、枚举类型直观表 用typedef定义一个10个元素的整型数组类型ARR,再用ARR定义一个10个元素的整型数组a。 用typedef定义一个指向函数的指针类型FP,该函数返回值为整型____________。 用typedef定义一个10个元素的字符指针数组类型CP____________。 typedef int ARR[10]; ARR a; typedet int (*FP)(); typedef char *CP[10];
作业:第11章作业 下载http://www.ccea.zju.edu.cn/clearn/ 实验:第12次实验 10.71.5.8