C语言程序设计 李祥
第十一章 指针(二) 目录 1 2 3 4 5 指针数组和指向指针的指针 指向函数的旨针和指向函数的指针变量 返回指针的函数 指向结构体类型数据的指针 5 动态存储分配函数
知识点要求: 技能要求: 教学要求 指针数组的概念 1 指向结构体指针变量的使用 2 3 1 2 动态分配函数的使用 掌握指向结构体的指针变量的应用 2 掌握动态分配函数的使用
注意:它与前面介绍的指向二维数组行的行指针变量的定义形式上的区别。 11.1 指针数组和指向指针的指针 注意:它与前面介绍的指向二维数组行的行指针变量的定义形式上的区别。 11.1.1 指针数组的概念及应用 指针数组是指数组元素均为指针类型的数组,即指针数组中的每一个元素都是指针变量。 指针数组的定义形式为: 类型标识符 *数组名[数组长度]; 例如,语句: int *p[5]; 定义了一个指针数组,数组名为p,共有5个元素,每个元素都是一个可以指向int型变量的指针变量。
11.1 指针数组和指向指针的指针 指针数组特别适合于用来处理指向若干个字符串的问题,它将使字符串的处理更加方便灵活。 例如,某班有若干个人,每个人都有一个姓名,我们可以采用二维字符数组来处理全班同学的花名册问题。定义一维字符指针数组并初始化如下: char *name[5]={“ZHANG XIAOHUA","WANG JUN","LU SHENG","ZHAO YICHENG","ZHOU YUAN"};
11.1 指针数组和指向指针的指针 char *name[5]={“ZHANG XIAOHUA","WANG JUN","LU SHENG","ZHAO YICHENG","ZHOU YUAN"};
【例11.1】 对花名册按姓名字母升序排序后输出 #include <stdio.h> void sort(char *name[ ],int n) { int i,j,minpost; char *t; for(i=0;i<n-1;i++) { minpost=i; for(j=i+1;j<n;j++) if(strcmp(name[minpost],name[j])>0) minpost=j; if(minpost!=i) { t=name[i]; name[i]=name[minpost]; name[minpost]=t; } }
void prn(char *name[ ],int n) { int i; for(i=0;i<n;i++) printf("%s\n",name[i]); } void main( ) { char *name[]={"ZHANG XIAOHUA","WANG JUN", "LU SHENG", "ZHAO YICHENG ","ZHOU YUAN"}; int count=5; sort(name,count); prn(name,count); return; 输出结果:
11.1 指针数组和指向指针的指针 11.1.2 用指针数组作main函数的形参 其中,argc是int型参数,它接受的值是命令行参数的个数;argv是指向char型的指针数组,它的每个数组元素接受的值分别是命令行各字符串参数的指针。 有些情况下,当程序开始执行时,希望通过命令行将某些参数传递给程序,以控制程序的执行,这时main函数就需要带参数,以便来接受命令行的参数。命令行的一般形式为: 命令名 参数1 参数2 … 参数n 带参数的main函数的一般格式如下: main( int argc, char *argv[]) { … }
11.1 指针数组和指向指针的指针 11.1.3 指向一维数组的指针数组 11.1.3 指向一维数组的指针数组 指向一维数组的指针数组是指该数组的元素均为指向一维数组的指针变量。指向一维数组的指针数组的定义形式为: 类型标识符 (* 数组名[数组长度])[一维数组长度]; 例如: 语句: int ( *p[5])[6];
【例11.2】 某班有4个学生,学4门课程,每个学生4门课程的成绩及总成绩已经给出,要求将他们按总成绩由高到低排序后输出。 #include <stdio.h> void main( ) { int score[4][6]={{9601,80,82,88,93,343}, {9602,86,54,95,57,292}, {9603,80,56,88,93,317}, {9604,95,89,77,96,357}}; int i, j, post, (*t)[6], (*p[4])[6]; for (i=0; i<4; i++) p[i]=score+i; 初始化指向一维数组的指针数组,使得p[i]分别指向score的每一行
for (i=0; i<=2; i++) { post=i; for (j=i+1; j<=3; j++) if ( *(*p[j]+5)> *(*p[post]+5)) post=j; if(post!=i) { t=p[i]; p[i]=p[post]; p[post]=t; } } printf("No.\tscore1\tscore2\tscore3\tscore4\ttotal\n"); for (i=0; i<4;i++) { for (j=0; j<6; j++) printf("%4d\t", *(*p[i]+j)); printf("\n"); } return; 输出结果:
11.1 指针数组和指向指针的指针 11.1.4 指向指针的指针 1. 指向指针的指针和指向指针的指针变量 假设有一个指向int型变量i的指针变量p,p的值是指针,它是变量i的指针&i; 指针变量p本身又有指针,即&p; 再定义一个指针变量b来指向指针变量p,b的值也是指针,它是指针变量p的指针&p。 则称指针变量b是一个指向指针的指针变量。如下图所示:
11.1 指针数组和指向指针的指针 指向指针的指针变量定义形式如下: 类型标识符 ** 变量名; 例如, 语句:int **b;
for (i=0;i<5;i++,b++) 【例11.3】 指向指针的指针变量的应用。 #include <stdio.h> void main( ) { char *name[]={ "ZHANG XIAOHUA","WANG JUN", "LU SHENG","ZHAO YICHENG ","ZHOU YUAN"}; char **b=name; //定义指向指针的指针变量 int i; for (i=0;i<5;i++,b++) printf("%c ",**b); printf("\n"); b=name; printf("%s\n", *b); //*b指向每个字符串 printf("\n"); return; } 输出结果:
11.1 指针数组和指向指针的指针 2.指向指针的指针数组 指向指针的指针数组是指数组元素为指向指针的指针类型的数组,即数组中的每一个元素都是指向指针的指针变量。指向指针的指针数组的定义形式为: 类型标识符 ** 数组名[数组长度]; 例如,语句: int **p[5];
11.2 指向函数的指针和指向函数的指针变量 11.2.1 函数的指针和指向函数的指针变量 11.2.1 函数的指针和指向函数的指针变量 指针不仅可以指向一般变量,也可以指向数组,还可以指向函数。 每一个函数都分配了一个入口地址,这个入口地址就称为函数的指针。 指向函数的指针变量的一般定义形式如下: 类型标识符(* 变量名)(); 例如,语句: int (*p) ( );
11.2 指向函数的指针和指向函数的指针变量 11.2.2 使用指向函数的指针变量来调用函数 11.2.2 使用指向函数的指针变量来调用函数 将某个函数的指针赋给指向函数的指针变量后,该指针变量就指向该函数,以后就可以通过该指针变量来调用该函数。 通过指向函数的指针变量调用所指向函数的一般调用形式为: (* 指针变量名)(实参表列);
11.2 指向函数的指针和指向函数的指针变量 例如,有一返回int型值的函数 int max(int a,int b),则: int (*p)( ); //定义指向函数的指针变量p p=max; //使指针变量p指向函数max z=(*p)(a,b); //通过指针变量p调用函数max 等价于: z=max(a,b);
11.2 指向函数的指针和指向函数的指针变量 11.2.3 指向函数的指针数组 指向函数的指针数组是指数组元素为指向函数的指针类型的数组,即数组中的每一个元素都是指向函数的指针变量。 指向函数的指针数组的定义形式为: 类型标识符(* 数组名[数组长度])( ); 例如,语句 int (*p[5])( );
11.3 返回指针的函数 11.3.1返回指针的函数 一个函数不仅可以返回一个整型值、字符值、实型值等,也可以返回一个指针型的值。这种返回指针的函数的一般说明形式如下: 类型标识符 *函数名(参数表) 例如: int *fun (int x,int y) { int *p; … return p; }
11.3 返回指针的函数 11.3.2 指向返回指针的函数的指针变量 11.3.2 指向返回指针的函数的指针变量 前面已经介绍了指向函数的指针变量和返回指针的函数是如何定义的。将它们结合起来使用,就得到指向返回指针的函数的指针变量。 它的一般定义形式为 类型标识符 *(* 变量名)( ); 例如: int * ( *p) ( );
11.3 返回指针的函数 11.3.3 指向返回指针的函数的指针数组 11.3.3 指向返回指针的函数的指针数组 把指向返回指针的函数的指针变量和指向函数的指针数组结合起来使用,就可得到指向返回指针的函数的指针数组。 它的一般定义形式为: 类型标识符 *(* 变量名[数组长度])(); 例如: int * (*p[4]) ( );
11.3 返回指针的函数 11.3.4 返回行指针的函数 把返回指针的函数和二维数组行指针的概念结合起来使用,就可得到返回行指针的函数的一般说明形式: 类型标识符( * 函数名(参数表))[数组长度] 例如: int ( * fun())[4] { int (*p)[4]; … return p; }
11.3 返回指针的函数 11.3.5 指向返回行指针的函数的指针变量 把指向函数的指针变量和返回行指针的函数结合起来使用,就可得到指向返回行指针的函数的指针变量的一般定义形式: 类型标识符(*(*变量名)())[数组长度]; 例如: int ( *(*p) ( ) )[4]; 定义了一个指针变量p,它可以指向一个函数,该函数将返回包含4个元素的行指针。
11.4 指向结构体类型数据的指针 结构体变量也有自己的指针,一个结构体变量的指针就是该变量所占据的内存段的起始地址。 可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址,通过该指针可以间接的访问该结构体变量。 指针变量也可以用来指向结构体数组中的元素。
11.4 指向结构体类型数据的指针 11.4.1 指向结构体变量的指针 【例11.4】 指向结构体变量的指针的应用。 11.4.1 指向结构体变量的指针 【例11.4】 指向结构体变量的指针的应用。 #include <stdio.h> void main( ) { struct student { long int num; char name[20]; char sex; float score; };
struct student stu_1; struct student *p; p=&stu_1; stu_1.num=89101; strcpy (stu_1.name, “Li Lin”); stu_1.sex=’M’; stu_1.score=89.5; printf(“No.:%ld\nname:%s\nsex:%c\nscore:%f\n”,stu_1.num,stu_1.name,stu_1.sex,stu_1.score); printf(“\nNo.:%ld\nname:%s\nsex:%c\nscore:%f\n”, ( *p).num,(*p).name,(*p).sex,(*p).score); return; } 输出结果:
11.4 指向结构体类型数据的指针 在C语言中,为了使用方便和使之直观,可以把(*p).num改用p->num来代替,即:p所指向的结构体变量中的num成员。同样,(*p).name等价于p->name。也就是说,以下三种形式等价: ①结构体变量. 成员名; ② (*p).成员名; ③ p->成员名。
11.4 指向结构体类型数据的指针 11.4.2 指向结构体数组的指针 【例11.5 】指向结构体数组的指针的应用 。 11.4.2 指向结构体数组的指针 【例11.5 】指向结构体数组的指针的应用 。 #include <stdio.h> struct student { int num; char name[20]; char sex; int age; }; struct student stu[3]={{10101, “Li Lin”, ’M’,18}, {10102, “Zhang Fun”,’M’,19}, {10104, “Wang Min”,’F’,20}};
void main( ) { struct student *p; printf(“ No. Name sex age\n”); for (p=stu;p<stu+3;p++) printf(“%5d %-20s %2c %4d\n”,p->num,p->name,p->sex,p->age); return; } 输出结果:
11.4 指向结构体类型数据的指针 注意以下两点: (1) 如果p的初值为stu,即指向第一个元素,则p+1后指向下一个元素的起始地址。 例如: (++p)->num 先使p自加1,然后得到它指向的元素中的num成员值 (p++)->num 先得到p->num的值,然后使p自加1,指向stu[1]。
11.4 指向结构体类型数据的指针 (2) 指针p已定义为指向struct student类型的数据,它只能指向一个结构体型数据,而不能指向一元素中的某一成员。 例如,下面是不对的: p=&stu[2].num 千万不要认为,反正p是存放地址的,可以将任何地址赋给它。如果地址类型不相同,可以用强制类型转换。 例如: p=(struct student *) &stu[2].num;
11.4 指向结构体类型数据的指针 11.4.3 用指向结构体的指针作函数参数 【例11.6】有一个结构体变量stu,内含学生学号、姓名和三门课的成绩。要求在main函数中赋以值,在另一函数printf中将它们打印输出。 #include <stdio.h> #define format “%d\n%s\n%f\n%f\n%f\n” struct student { int num; char name[20]; float score[3]; };
void print(struct student *p) { printf (format, p->num, p->name, p->score[0], p->score[1], p->score[2]); printf(“\n”); } void main( ) { struct student stu; stu.num=12345; strcpy(stu.name, “Li Li”); stu.score[0]=67.5; stu.score[1]=89; stu.score[2]=78.6; print(&stu); return; 输出结果:
11.4 指向结构体类型数据的指针 注意: ANSI C允许用整个结构体作为函数的参数传递,但是必须保证实参与形参的类型相同。 把一个完整的结构体变量作为参数传递,虽然合法,但要将全部成员值一个一个传递,费时间又费空间,开销大。如果结构体类型中的成员很多,或有一些成员是数组,则程序运行效率会大大降低。在这种情况下,用指针作函数参数比较好,能提高运行效率。
11.4 指向结构体类型数据的指针 11.4.4 应用举例 【例11.7】 有4个学生,每个学生包括学号、姓名、成绩。要求找出成绩最高者的姓名和成绩。 #include <stdio.h> void main( ) { struct student { int num; char name[20]; float score; }; struct student stu[4]; struct student *p; int i,temp=0; float max;
{ printf(”请输入第%d个学生的学号,姓名,成绩:\n”,i+1); for (i=0;i<4;i++) { printf(”请输入第%d个学生的学号,姓名,成绩:\n”,i+1); scanf (“%d %s %f”,&stu[i].num,stu[i].name,&stu[i].score); } for (max=stu[0].score, i=1;i<4;i++) if (stu[i].score>max) { max=stu[i].score; temp=i; } p=stu + temp; //将p指向成绩最高的学生记录 printf(“\nThe maximum score:\n”); printf(“No.:%d\nname:%s\nscore:%4.1f\n”, p->num,p->name,p->score); return;
11.5 动态存储分配函数 C语言新标准ANSI C要求各C编译版本提供的标准库函数中应包括动态存储分配的函数,它们是: malloc( ) calloc( ) free( ) realloc( )
11.5 动态存储分配函数 1. malloc函数 它的作用是在内存开辟指定大小的存储空间,并将此存储空间的起始地址作为函数值带回。 void * malloc(unsigned int size)
11.5 动态存储分配函数 malloc函数的模型(原型)为: void * malloc(unsigned int size) 注意: p=(long *) malloc(8); (2)如果内存缺乏足够大的空间进行分配,则malloc函数值为“空指针”,即地址为0。
11.5 动态存储分配函数 2. calloc函数 其函数模型为: void *calloc(unsigned int num, unsigned int size) 它有两个形参num和size。其作用是分配num个大小为size字节的空间。 例如用calloc(10,20)可以开辟10个(每个大小为20字节)的空间,即总长为200字节。此函数返回值为该空间的首地址。
11.5 动态存储分配函数 3. free函数 其模型为: void free(void *ptr) 注意: ptr值不能是任意的地址,而只能是由在程序中执行过的malloc或calloc函数所返回的地址。 下面这样用是可以的: p=(long *)malloc(8); … free(p);
11.5 动态存储分配函数 4. realloc函数 用来使已分配的空间改变大小,即重新分配。 其函数原型为: void *realloc(void *ptr, unsigned int size) 作用是将ptr指向的存储区(是原先用malloc函数分配的)的大小分配为size个字节。
11.5 动态存储分配函数 注意:以上4个函数的声明在stdlib.h头文件中,在用到这些函数时应当用“#include <stdlib.h>”指令把stdlib.h头文件包含到程序文件中。
本章小结 重点: 1 指向结构体类型数据的指针 2 动态存储分配函数 难点: 1 指向结构体类型数据的指针的应用 2 动态存储分配函数的应用
本章结束!