1、,2. 指向多维数组的指针变量1) 指向数组元素的指针变量 (见例10.11)2)指向由m个元素组成的一维数组的指针变量 类型说明符 (*指针变量名)长度int (*p)4而 p+i ( a+i ) 则指向一维数组 ai 或 *(a+i), *(p+i)+j 是二维数组i行j列的 元素的地址,而 *(*(p+i)+j ) 则是i行j列 元素的值。 中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类型。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维组的列数。应注意“(*指针变量名)”两边的括号不可少,如缺少括号则表示是指针数组,意义就完全不同了。,p 是一个
2、指针变量,它指向包含4个元素的一维数组。,例10.11 用指针变量输出二维数组元素的值,(1) 指向数组元素的指针变量,#include void main() int 341,3,5,7,9,11,13,15,17,19,21,23;int*p;for( pa;pa12;) if(pa)printf();printf(,*p); ,运行结果如下:1 3 5 79 11 13 15 19 21 23,若 int a34; 则 int (*p1)4=a; 指向行(0行) 行指针int *p2=a0; 指向元素(0行0列) 列指针int *p3=*a; 指向元素(0行0列) 列指针,(2) 指向由
3、个元素组成的一维数组的指针变量,例10.12 输出二维数组任一行任一列元素的值,#include void main ( ) int a= 1, 3, 5, 7, 9, 11,13, 15,17, 19, 21, 23 ; int (*p),; p = a;scanf( ,);printf(,n, *(*(pi)j); ,运行情况如下: ,(本行为键盘输入) ,,3. 用指向数组的指针作函数参数,例10.13 有一个班,个学生,各学门课,计算总平均分数以及第个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数average求总平均成绩;用函数search找
4、出并输出第i个学生的成绩。,例 : 3个学生各学4门课,计算总平均分,并输出第n个学生成绩,void main() void average(float *p, int n);void search(float (*p)4, int n);float score34= 65,67,70,60,80,87,90,81,90,99,100,98;average( *score, 12 );search( score, 2 ); ,void average(float *p, int n) float *p_end, sum=0, aver;p_end= p+n-1;for( ; p=p_end;
5、p+)sum=sum+(*p);aver=sum/n;printf(“average=%5.2fn“, aver); void search( float (*p)4, int n ) int i;printf(“ No.%d :n“, n );for( i=0; i4; i+)printf(“%5.2f “, *(*(p+n)+i); ,列指针,行指针,函数说明,float p 4, pni,程序运行结果如下: average The score of No.2 are: . . . .,例 3个学生各学4门课,计算总平均分,并查找一门以上课不及格学生, 输出其各门课成绩, pji,略,在C
6、语言中,可以用两种方法访问一个字符串。 1) 用字符数组存放一个字符串,然后输出该字符串 【例10.15】#include void main( )char string = “I love China!” ;printf(“%sn“, string );,string是数组名,它代表字符数组的首地址。,10. 字符串与指针,10.4.字符串的表示形式,1.数组string的长度? 14,2. strlen(string)的值? 13,2) 用字符串指针指向一个字符串。 【例10.16】main( )char *string = “I love China!” ;printf(“%sn“, s
7、tring ) ;,注意:语句 char *string = “I love China!“; 等价于 char *string;string = “I love China!“;它把字符串常量的首地址赋给指针string。不能理解为把字符串常量赋值给指针变量。*string =“I love China!“;,从以上两个例子中,可以看到:1)字符数组和字符指针的概念不同。2)字符指针指向字符串,而C语言中,字符串按数组方 式处理。因此,字符数组和字符指针的访问方式相同。 例如,均可以使用%s格式控制符进行整体输入输出。但应注意如果不是字符数组,而是整型、实型等数字 型数组,不能用%s,只能逐
8、个元素处理。,也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。,通过指针变量引用字符串,#include #include main( ) int i;char a8=“First“;puts(a); strcpy( a, “Second“ ); for( i=0; *(a+i) != 0 ; i+ ) putchar( *(a+i) ); ,for( i=0; *(a+i) != 0 ; i+ ) putchar( *(a+i) );, printf(“%sn“, a);,a=“Second“; ,等价于ai,First,Second,char a8=“First“;,puts(
9、a);,strcpy( a, “Second“ );,通过字符数组名引用字符串,printf(“%sn“, a); puts(a);,#include main( ) char *p=“First“;puts(p); p=“Second“; for( ; *p != 0 ; p+ )putchar(*p); ,p,char *p=“First“;,puts(p);,p=“Second“;,First,Second,for( ; *p != 0 ; p+ )putchar(*p);,puts(p);,例10.7 将字符串复制为字符串。,#include void ain() char a am
10、a boy,b20;int ;for(0;*()!0;)*()*();*();printf(string a is :%sn,a);printf(string b is:);for(;b!;)printf(,b );printf();,b i =a i ; b i = 0;,通过字符数组名引用字符串,ai!=0,例10.18 将字符串a复制到字符串b。(用指针处理),printf( “string a is: %sn”, p1 );,printf( “string a is: %sn”, b);,printf( “string a is: %sn”, p2 );,程序必须保证使 p1 和 p2
11、 同步移动,当p1=a时,当p2=b时,【思考】编写字符串比较的程序,while( ai=bi ,while( *p = *q ,char a =“first”, b =“Second” p =a ; q =b;,Char *p=“first”, *q=“Second”,10.4.2 字符指针作函数参数,将一个字符串从一个函数传递到另一个函数,可以使用传地址的方式,即用字符数组名或指向字符串的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。有以下四种情况:,将字符串from复制为字符串to, 不用函数调用。,#include void ain() ch
12、ar from am a boy,to20;int i;for(i0;*(fromi )!0;i)*(toi)*(fromi);*(toi);printf(string from is :%sn,from);printf(string to is: %sn,to );printf();,to i =from i ; to i = 0;,通过字符数组名引用字符串,fromi!=0,char from ” am a boy”, to20;,#include void main() void copy_string(char from , char to );char a = am a teache
13、r;char b =you are a student;printf(“string a=s string s, a,b); printf(“copy string a to string b:n ”);copy_string (a,b);printf(“nstring a=%snstring b=%sn“,a,b); ,void copy_string(char from ,char to ) int ;while(from!) tofrom; to; ,例10.19 用函数调用实现字符串的复制,(1) 用字符数组 作参数,程序运行结果如下:string a I am a teacherst
14、ring b you are a studentcopy string a to string b: string a I am a teacher string b I am a teacher,(2) 形参用字符指针变量,#include void main() void copy_string(char * from, char *to );char *aI am a teacher .;char *byou are a student .;printf(“string a=sn string bsn,a, b );printf(“copy string a to string b:n
15、“);copy_string( a, b ) ;printf(“nstring a=%snstring b=%sn“,a, b ); ,void copy_string(char *from, char *to) for( ; *from!=0; from+, to+)*to=*from;*to=0; ,(3) 对copy_string 函数还可作简化,1、 将copy_string函数改写为void copy_string (char *from,char *to) while(*to*from)!) to;from;,copy_string函数的函数体还可改为while(*to*from)
16、!);,pass,copy_string函数的函数体还可写成 while(*from! )*to*from;*to; ,上面的while语句还可以进一步简化为下面的while语句:while( *to*from ); 它与下面语句等价:while(*to*from)!);将*from赋给*to,如果赋值后的*to值等于, 则循环终止(已赋给*to),pass,函数体中while语句也可以改用for语句:for(;(*to*from)!;); 或for(;*to*from;);,也可用指针变量,函数copy_string可写为void copy_string (char from ,char t
17、o ) char*p,*p;p1from;p2to ;while(*p2*p1)!);,pass,应该:strcpy( str, “Iove China!” ),10.4.3 对使用字符指针变量和字符数组的讨论,例10.20main() char *a=“ I love motherland”;a=a+7;printf(“%s”, a ); 说明:指针变量的值是可以改变的,即指针变量a的值可以变化,输出字符串时从a当时所指向的单元开始输出各个字符,直到遇0为止。而数组名的值是不能改变的。 char str = “ I love motherland” str=str+7; printf(“%s
18、”, str );,输出: motherland,指针变量的值是可以改变的 !,#include void main() char*a love motherland!;int i ;printf ( “ The sixth character is %cn“,a5 );for(;!;)printf(,a i );,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。例如:,下标,注意:,void main( ) char y10=”987654321”, *p;p= ,void main() char f5; f0 = H ; f1 = A ; f2
19、 = N ; f3 = D ; f4 = 0; /* 空字符表示结束 */printf(“The f is %sn“, f ); 输出结果是 ? printf(“One letter is %cn“, f2 ); 输出结果是 ? printf(“Part of the f is %snn“, 输出结果是 ? ,在语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针变量”。 函数指针变量定义的一般形式为
20、:类型说明符 (*指针变量名)( ); 其中 “类型说明符”表示被指函数的返回值的类型。“*”后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。,例如:int (*pf)( ); 表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。,10. 指向函数的指针,10.5.1 用函数指针变量调用函数,例 用函数指针变量调用函数,比较两个数大小,void main() int max(int , int);int a, b, c;scanf(“%d,%d“, ,void main( ) int max( int , int );int (*p)( );int a,
21、 b, c;p=max; scanf(“%d,%d“, ,关键!,比较 c=max(a,b);,定义指针,#include ,说明: 1)语句p=max,把函数max的入口地址赋给函数指针p,因此,c=(*p)(a,b)中,*p就是调用函数max。 注意: 语句 p=max;是给指针变量赋值,只需给出函数名,而不必给出参数。函数名代表函数的入口地址,不牵涉到实参和形参的结合问题。 用函数指针调用函数时,应指定实参。 2)(*p)( )表示定义了一个指向函数的指针变量,用来存放函数的入口地址,它可以先后指向不同的函数。 3)指向函数的指针变量p,象p+、p-、p+n等运算是无意义的。,10.5.
22、2 用指向函数的指针作函数参数,函数指针变量常用的用途之一是把指针作为参数传递到其他函数。指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。,实参函数名 f1 f2 void sub( int (*x1)(int),int (*x2)(int, int) ) int a,b,;a(*x1)(); *调用函数*b(*x2)(,); *调用函数*,原理简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2),定义x1和x2为指向函数的指针变量。在调用函数sub时,实参用两个函数名f1和f2给形参传递函数地址。这样在函数sub中就可以调用f1和
23、f2函数了。 如:,int f1(int i);,int f2(int i, int j);,int sub( int (*x1)(int), int (*x2)(int, int);,int f1( int i ) printf(“f1, i=%dn“, i );return i*2; ,int f2( int i, int j ) printf(“f2, i=%d, j=%dn“, i, j );return i+j; ,int sub( int (*x1)(int), int (*x2)(int, int) int a, b, i=3, j=2; a = (*x1)(i); b = (*
24、x2)(i, j); printf(“sub, a=%d, b=%dn“, a, b); ,void main ( ) sub(f1, f2) ; ,略,例10. 23 设一个函数process,在调用它的时候,每次实现不同的功能。 输入a和b两个数:第一次调用时找出a和b中大者,第二次调用时找出a和b中小者,第三次调用时求a与b之和。 分析:将求大值、求小值、求和值分别设计为函数max、min、add。 process使用指针调用这些函数: void process( int x, int y, int (*fun)(int, int) );,一个函数可以带回一个整型值、字符值、实型值等,也
25、可以带回指针型的数据,即地址。 一般形式:类型名 *函数名(参数表); 例: int *a ( int x, int y );float *f( int m, char n) ;1) 声明一个函数,函数名为a; 2) 其返回值类型是“指向整型的指针”; 3) 函数形式参数为int x 和 int y。,10.6 返回指针值的函数,例 指针函数实现:有若干学生成绩,要求输入学生序号后,能输出其全部成绩,void main() float score 4=60,70,80,90,56,89,67,88,34,78,90,66;float *search(float (*pointer)4, int
26、 n), *p;int i, m;printf(“输入学生的号数:“);scanf(“%d“, ,运行情况如下: enter the number of student: The scores of No. are: 56.00 89.00 67.00 88.00,例10.24,略,【补例】写一个函数,求两个int型变量中居于较大值的变量的地址,#include“stdio.h“ void main( ) int *f1(int*x,int *y);int a=2, b=3;int *p;p=f1( ,2,3,2002,2000,*,?,int *f1( int *x, int *y ) if
27、( *x*y )return x;elsereturn y; ,例10.25 对上例中的学生,找出其中有不及格课程的学生及其学生号。,#include void main() float score 4= 60,70,80,90, 56, 89,67,88, 34,78,90,66 ;float search(float (*pointer)4);float*p;int ,;,for(;) psearch(score );if(p*( score) )printf( scores:,i);for(;)printf(5.2,*( p));printf();,略,指针数组说明的一般形式:类型说明符
28、 *数组名数组长度 其中类型说明符为指针值所指向的变量的类型。,一个数组的元素值为指针则是指针数组。 指针数组是一组有序的指针的集合。 指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。,10.7.1 指针数组的概念,10.7 指针数组和指向指针的指针,指针数组,元素均为指针类型数据的数组,称为指针数组 定义形式为: 类型关键字 *数组名数组长度; 例如char *pStr5;,int (*p)3;表示定义一个行指针,它指向有3个元素的一维数组。,例如:int *p 3; 表示p是一个指针数组,它有三个数组元素p0,p1,p2,每个元素值都是一个指针变量,指向整型变量。,
29、注意区分:,用途:可用于处理二维数组或多个字符串,指针数组赋值 与初始化,略,略,例10.26 将若干字符串按字母顺序(由小到大)输出。,#include #include void main() void sort(char *name ,int n);void printf(char *name ,int n);char *name = “Follow me“, “BASIC“, “Great Wall,“FORTRAN“,“Computer design“ ;int ;sort( name ,);print( name ,); ,void sort(char *name ,int n)
30、char *temp;int ,;for (;) ;for (;)if(strcmp (namek,name j )0)k=j;if(!) temp=namei; namei=namek;namek=temp; ,void print(char *name ,int ) int i;for (;)printf(%s,name); ,运行结果为: Computer design FORTRAN Follow me Great Wall,如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。通过指针访问变量称为间接访问。由于指针变量直接指向变量,所以称为“单级间址”
31、。而如果通过指向指针的指针变量来访问变量则构成“二级间址”。,10.7.2 指向指针的指针,一级指针: 指针变量中存放目标变量的地址,例 int *p; int i=3;p=,一级指针,单级间接寻址,5,例 int *p1; int *p2;int i=3;p2=,二级指针,一级指针,目标变量,二级间接寻址,定义一个指向指针数据的指针变量 ?char *p ;p的前面有两个*号。*运算符的结合性是从右到左,因此*p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的。*p就是p
32、所指向的另一个指针变量。,二级指针: 指针变量中存放一级指针变量的地址,*p1=p2=,5,一级指针: 指针变量中存放目标变量的地址,例10.27 使用指向指针的指针,#include void main() char *name= “Follow me“,“BASIC“,“Great Wall,“FORTRAN“,“Computer design“ ;char *p;int ;for(;) pnamei;printf(,*p); ,运行结果如下: Follow me BASIC Great Wall FORTRAN Computer design,字符指针数组 char *name 指向字符
33、串。 定义指向指针的指针p:char *p, 使其指向name。 p = name + 2; printf(“%on”, *p ); /* 以八进制形式输出name2的值, 它是字符串“Great Wall”的地址 */ printf(“%sn“, *p ); /* 输出name2指向的字符串 */,补例 用二级指针处理字符串,#define NULL 0 void main() char *p;char *name = “hello“,“good“,“world“,“bye“, “0“ ;p=name+1;printf(“%o : %s “, *p, *p);p+=2;while(*p!=N
34、ULL)printf(“%sn“, *p+); ,运行结果: 644 : good bye,用*p可输出地址(%o或%x), 也可用它输出字符串(%s),*(p+),例10.28 一个指针数组的元素指向整型数据的简单例子。,#include void main() int a5= 1, 3, 5, 7, 9 ;int *num5= &a0, &a1, &a2, &a3, &a4 ; int *p,;pnum;for(;) printf( ,*p);p; ,指针数组可以不指向字符串,而指向整型数据或实型数据,运行结果: 1 3 5 7 9,二级指针 与 指针数组 int *p 与 int *q1
35、0 若p=q; 则p+i 是qi的地址 指针数组名q是二级指针常量,而给q分配10块内存区,每块可保存一个指针值,系统只给p分配能保存一个指针值的内存区;,关系,指针数组的一个重要应用是作为main函数的形参。如: void main(int argc, char argv )main函数是由系统调用的,当处于操作命令状态下,输入main函数所在的文件名(经过编译、连接以后得到的可执行的文件名),系统就调用main函数。那么,main函数的形参的值从何处得到呢?实际上实参是和命令一起给出的,也就是在一个命令行中包括命令名和需要传给main函数的参数 运行程序的命令行的一般形式为:命令名 参数1
36、 参数2 . 参数n (命令名和参数之间用空格分隔)。,10.7.3 指针数组作main函数的形参,命令行参数,通过命令行参数,使用户可以根据需要来决定我们的程序干什么、怎么干当把main函数写成这样时main(int argc, char *argv )argc的值为程序执行时参数的数目(包括命令本身) argvi为指向第i个参数的字符指针 这两个内设形参用于接收命令行参数,例如:若有一目标文件 file1,今想将两个字符串“China”和“Beijing”作为传送给main函数的参数,可写成以下形式: file1 China Beijing 注意以上参数与main函数中形参的关系: mai
37、n函数中argc表示命令行参数的个数(包括命令名)argv是一个指向字符串的指针数组,用于存放参数(包括命令名)带参数的main函数的原型是:main( int argc, char *argv );,上例中:argc = 3; argv0 =“file1.exe“, argv1 =“China“, argv2 = “Beijing“,例:如果有以下一个main函数,它所在的文件名是file1: void main( int argc, char *argv ) while (argc1 ) +argv ;printf(“%sn“, *argv ) ;-argc ; ,输入:file1 Chi
38、na Beijing 输出:China Beijing,在DOS命令状态下输入的命令行为,10.8 有关指针的数据类型和 指针运算的小结,10.8.1 有关指针的数据类型的小结,有关指针的数据类型的小结,1、指针变量加/减运算p+、p -、p+i、p-i、p+=i、p-=i加1表示指向下一个数据。,一个指针变量加(减)一个整数并不是简单地将原值加(减)一个整数, 而是将该指针变量的原值(是一个地址)和它指向的变量所占用的内存单元字节数加(减)。,10.8.2 指针运算小结,2、指针变量赋值 p = 指针p2的值赋给指针p1,即p1、p2所指的数据相同,注意:1)不能把一个整型变量赋给指针变量。
39、如:p = 1000;只能将变量已分配的地址赋给指针变量。2)不能把指针变量的值赋给一个整型变量。如:int i; i = p; 3、指针变量可以有空值,即该指针不指向任何变量表示如下:p = NULL; /*p不指向任何数据*/在stdio.h中,NULL被定义为0:#define NULL 0,思考:能够赋给指针的唯一 整数是,0,习惯上,不使用 p = 0,而使用 p = NULL。 指针变量p可以与NULL作比较,例: if (p = NULL) . 注意:空指针不指向任何数据,与p未赋值不同。当p未赋值时,其值是不确定的,而空指针的值是确定数0。4、指针变量相减 当p1、p2指向同一
40、个数组的元素,指针相减p2-p1等于p1、p2间的元素个数。 注意:指针相加无意义。,5、两个指针的比较 当p1、p2指向同一个数组的元素时,可以比较,如:p1 p2;若p1、p2不是指向同一个数组的元素,比较无意义。,三、空类型指针 void void *p,表示p是空类型指针,不指定它是指向哪一种类型数据的,即它可以指向任何数据类型。空类型指针与其他类型指针之间赋值时,应进行强制类型转换。例如: char *p1; void *p2;p1 = (char *)p2; p2 = (void *)p1;,ANSI C新标准增加了一种“void”指针类型,即可定义一个指针变量,但不指定它是指向哪
41、一种类型数据的,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。,也可以将一个函数定义为 *类型 例如:void *fun(char ch1,char ch2) 表示函数返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如对该函数调用得到的地址要进行以下转换:p1(char *)fun( ch1 , ch2 );,概 念 指 针 变 量 数 组与指针 字 符 数 组 函数参数与指针 函数与指针 指 针 数 组,指 针,教材p278: 上机:10.1、 10.2 、10.3 、10.6、 10.7、 10.8、10.9、10.14、10.18 注意:有关设计程序的作业,必须经过上机调试!,作业及上机要求:,10,