Download presentation
Presentation is loading. Please wait.
Published byΧθόνια Μαυρίδης Modified 5年之前
1
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。
第十章 指针 指针是C语言的重要概念,是C语言的特色,是C语言的精华。 10.1 地址和指针的概念 内存中的每一个字节都有一个地址。 内存单元的地址与内存单元的内容是两个完全不同的概念。 在编译时给出了变量名和地址的对应关系。
2
以前在程序中定义整形等变量。如 int a,b; 表示a和b申请整形单元,分别存放两个整形值。 我们也可以定义一种特殊的变量,它用来存放内存单元地址。这种变量的值是一个地址,它的作用是:根据地址去找相应的单元,因此,这种装载地址的变量又可称为“指针变量”。 指针变量的值就是指针即地址。
3
10.2 变量的指针和指向变量的指针变量 变量的指针就是变量的地址。存放地址的变量称谓指针变量。 为了表示指针变量和它所指向的变量之间联系,程序中用*符号表示“指向” 如用i_point表示指针变量,而*i_point就表示i_point指向的变量。 定义一个指针变量 C语言规定所有变量在使用前必须定义,指定其类型,并按此分配内存单元。
4
int i,j; int *point1,*point2; 定义了两个指向整形变量的指针变量。 定义指针变量要指定“基类型” 基类型是指针变量指向的变量的类型。 指针变量的定义形式: 基类型 *指针变量名 那么如何给指针变量赋值呢? point1=&i; point1=&j;
5
指针变量的引用 指针变量只能存放地址(指针),有两个有关的运算符: (1)&:取址运算符,&a (2)*:指针运算符(“间接运算符”),*p main() {int a,b;int *point1,*point2; a=100;b=20; point1=&a; point2=&b; printf(“%d,%d\n”,a,b); printf(“%d,%d\n”, *point1,*point2);}
6
point1=&*point2; ? b=*&a; ? main() { int *p1,*p2,*p,a,b; a=10;b=90; p1= &a;p2=&b; if(a<b){p=p1;p1=p2;p2=p;} printf(“%d,%d\n”,*p1,*p2); }
7
指针变量作为函数参数 函数参数可以是指针变量 swap(int *p1,int *p2) { int temp; temp=*p1;*p1=*p2;*p2=temp;} main() {int a,b, *point1,*point2; a=10;b=90; point1=&a; point2=&b; if(a<b)swap(point1,point2); printf(“%d,%d\n”, *point1,*point2);}
8
10.3 数组的指针和指向数组的指针变量 一个变量有地址,一个数组包括若干个元素,每个元素都有地址. 数组的指针是指数组的起始元素的地址.
引用数组元素可用下标法,也可用指针法,即用指针指向所引用的元素.
9
指向数组元素的指针 int a[10]; int *p; p=&a[8]; C语言规定数组名代表数组的首地址,也就是第0号元素的地址. 下面两个句子等价: p=&a[0]; p=a;
10
通过指针引用数组元素 按C语言规定:如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素(而不是将的p的值简单加1). 如果p的初值为&a[0],则 (1)p+i和a+i就是a[i]的地址 (2)*(p+i)和*(a+i)是p+i和a+i所指向的数组元素,即a[i]。 可以看出,[ ]实际上是变址运算符 (3)指向数组的指针变量也可以带下标,如 p[i]与*(p+i)等价.
11
用数组名作函数参数 例: main() inv(int arr[ ],int n) { int array[10]; { : : inv(array,10); } : } 用数组名作参数时,如果形参数组中各元素值发生变化,实参数组值随之变化.
12
void inv(int arr[ ],int n)
{ int t,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i;t=arr[i];arr[i]=arr[j];arr[j]=t;} } main() { int i,a[10]={2,4,54,6,7,3,31,56,23,65}; inv(a,10); for(i=0;i<=9;i++) printf(“%d”,a[i]);
13
f(int arr[ ],int n)等价于f(int *arr,int n)
例: 函数定义为: 函数调用为: void swap(int x,int y) swap(a[1],a[2]); 请大家注意: (1)“值传递”和“地址传递” (2)两者之间的异同点
14
int m1,m2; void m_value(int array[ ],int n) { int *p,*array_end; array_end=array+n; m1=m2=*array; for(p=array+1;p<array_end;p++) if(*p>m1)m1=*p; else if(*p<m2)m2=*p;} main() { int i,num[10]; for(i=0;i<10;i++)scanf(“%d”,&num[i]); m_value(num,10); printf(“%d,%d”,m1,m2);}
15
函数参数为数组时: (1)形参和实参都是数组名,如: main() f(int arr[ ],int n) { int a[10]; { : : f(a,10); } : } (2)实参是数组名,形参是指针变量,如: main() f(int *arr,int n) f(a,10); }
16
(3)形参和实参都是指针变量,如: main() f(int *arr,int n) { int a[10],*p=a; { : : f(p,10); } : } (4)实参是指针变量,形参是数组名,如: main() f(int arr[ ],int n) f(p,10); }
17
选择法排序 sort(int x[ ],int n) { int i,j,k,t; for(i=0;i<n-1;i++) {k=i; for(j=i+1;j<n;j++) if(x[j]>x[k])k=j; if(k!=i){t=x[i];x[i]=x[k];x[k]=t;} }
18
指向多维数组的指针和指针变量 1、多维数组的地址 考查:int a[3][4]; a是一个数组名。 a数组包含3行,即看成3个元素: a[0]、a[1]、a[2]。而一个元素又是一个一维数组,包含4个元素。 a[0]所代表的一维数组包含的4个元素是: a[0][0]、a[0][1]、a[0][2]、a[0][3] 从二维数组来看,
19
a代表整个二维数组的首地址, 也就是第0行的首地址,即a[0]地址 a+1代表第1行的首地址,即a[1]地址 a+2代表第2行的首地址,即a[2]地址 实际上a[0]、 a[1]和 a[2]分别表示的是二维的第0行、第1行和第2行。 基于这种认识,把a[0]、 a[1]和 a[2]看成是一维数组名,C语言又规定数组名代表数组的首地址,因此a[0]代表第0行一维数组中第0列元素的地址,即&a[0][0]
20
依此类推a[1]的值是 &a[1][0] 依此类推a[2]的值是 &a[2][0] 第0行第1列元素的地址可表示为: a[0]+1 在一维数组中, a[0]和*(a+0)等价, a[1]和*(a+1)等价,等 理解: a是二维数组的首地址 a[1]即*(a+1)是第二行的首地址
21
用地址法如何得到a[0][1]呢? 它的地址表示为 a[0]+1,值表示为*(a[0]+1) 进一步表示为:*(*(a+0)+1) 说明:对二维数组来说, a[i]表示的是一维数组名, a[i]本身不占实际的内存单元,它只是一个地址,表示地址的一种计算方法,如同一维数组x[10]的数组名.
22
2、指向多维数组的指针变量 (1)指向数组元素的指针变量 main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int *p; for(p=a[0];p<a[0]+12;p++) {if((p-a[0])%4==0)printf(“\n”); printf(“%4d”,*p); }
23
(2)指向由m个元素组成的一维数组指针变量
输出二维数组任一行任一列元素的值 main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4],i,j; p=a; scanf(“i=%d,j=%d”,&i,&j); printf(“a[%d,%d]=%d\n”,i,j,*(*(p+i)+j)); }
24
注意(1).* p[4]是指针数组 (2). int a[4]; a有四个元素 (3). int (*p)[4]; *p有4个元素,每个元素都是整型.p所指的对象是有4个整型元素的数组,即p是行指针.应该记住此时p只能指向一个包含4个元素的一维数组,p的值就是该一维数组的首地址. p不能指向一维数组中的第j个元素.
25
3、多维数组的指针作函数参数 用指针变量作形参以接受实参数组名传来的地址时,有两种方法: (1)用指向变量的指针变量 (2)用指向一维数组的指针变量 通过指针变量存取数组元素速度快,程序简明.用指针变量作形参,可以允许数组的行数不同.因此数组与指针常常是紧密相联的.
26
10.4 字符串的指针和指向字符串的指针变量 10.4.1 字符串的表示形式 在C中有两种方法
字符串的表示形式 在C中有两种方法 (1)用字符数组存放一个字符串,然后输出该字符串 (2)用字符指针指向一个字符串 显然用%s可以对一个字符串进行整体的输入与输出
27
main() {char a[]=“I am a boy.”, b[20], *p1,*p2; int i; p1=a;p2=b; for(;*p1!=‘\0’;p1++,p2++)*p2=*p1; *p2=‘\0’; printf(“string a is :%s\n”,a); printf(“string b is:”); for(i=0;b[i]!=‘\0’;i++)printf(“%c”,b[i]); printf(“\n”); }
28
字符串指针作函数参数 将一个字符串从一个函数传递到另一个函数,可以用地址传递,用数组名或指针. void str(char *f,char t[ ]){ } main( ) {char a[10],*b; … str(a,b); 对使用字符指针变量和字符数组的讨论 字符指针变量和字符数组实现字符串的存贮与运算,两者之间是有差别的. char str[10]; scanf(“%s”,str); char *a; scanf(“%s”,a);
29
10.5 函数的指针和指向函数的指针变量 10.5.1 用函数指针变量调用函数
用函数指针变量调用函数 可以用指针变量指向整型变量,字符串,数组,也可以指向一个函数.一个函数在编译时被分配一个入口地址,这个入口地址就称为函数的指针,可以用一个指针变量指向函数,然后通过该指针变量调用此函数.
30
main() { int a,b,c; scanf(“%d,%d”,&a,&b); c=max(a,b); printf(“Max is %d”,c);} int max(int x, int y) { int z; z=x>y?x:y; return(z); }
31
将main函数改写为: main() { int max(int,int); int (*p)(); int a,b,c; p=max; //将max的入口地址赋给p scanf(“%d,%d”,&a,&b); c=(*p)(a,b); printf(“Max is %d”,c);} int (*p)()定义p是一个指向函数的指针变量,此函数带回整型的返回值.
32
定义指向函数的指针变量的一般形式 数据类型 (*指针变量)( ); 其它注意事项见教材
33
用指向函数的指针作函数参数 函数的参数可以是变量,指向变量的指针变量,数组名,指向数组的指针变量,等等 函数指针变量常用的用途之一是把指针作为参数传递到其他函数,是C语言中深入的部分. sub(int (*x1)(int),int (*x2)(int,int)) { int a,b,i=1,j=2; a=(*x1)(i); b=(*x2)(i,j); …….}
34
10.6 返回指针值的函数 一个函数可以带回一个整型值,字符值,实型值等,也可带回指针型的数据,即地址.
10.6 返回指针值的函数 一个函数可以带回一个整型值,字符值,实型值等,也可带回指针型的数据,即地址. 其本质与以前类似,只是带回的值是指针类型而已 带回指针值的函数,可定义为: 基类型 *函数名(参数表); 如: int *a(int x,int y);
35
main() {float score[][4]={{60,70,80,90},{50,89,67,88},{ 34,78,90,66}},*p; float *search(float (*ptr)[4]); int i,j; for(i=0;i<3;i++) {p=search(score+i); if(p==*(score+i)) {printf(“No. %d scores:”,i); for(j=0;j<4;j++)printf(“%5.2f”,*(p+j)); printf(“\n”);} }
36
float *search(float (*ptr)[4])
{int i; float *pt; pt=*(ptr+1); for(i=0;i<4;i++) if(*(*ptr+i)<60) {pt=*ptr; break;} return(pt); }
37
10.7 指针数组和指向指针的指针 指针数组的概念 一个数组其元素均为指针类型数据,称为指针数组.指针数组中的每一个元素都相当于指针变量.一维指针数组的定义为: 类型名 *数组名[数组长度] 主要用于指向若干个字符串,使字符串的处理更加灵活,如: char *name[ ]={“Tom”,”Bill”,”David”};
38
指向指针的指针 指针数组的数组名就是一个指针的指针. char **p; 相当于 char *(*p); 指针p是指向一个字符指针变量的变量. *p就是p指向的另一个指针变量 变量的直接访问,间接访问(单级间址,二级间址,多级间址).
39
指针数组作main函数的形参 指针数组的一个重要作用就是作main函数的形参. main函数的一般形式为: main( ) 实际上main函数可以有形参,如 main(argc,argv) main函数是系统调用的,它的形参对应的实参从何而来呢? 从OS命令中得到:
40
命令名 参数1 参数2……参数n 利用指针数组作main函数的形参,可以向程序传送命令行参数(这些参数是字符串).由于参数的长度和个数都是不定的,所以用指针数组恰好能满足这一要求.
41
10.8 有关指针的数据类型和指针运算的小结 10.8.1 有关指针的数据类型的小结 int i; int *p;
有关指针的数据类型的小结 int i; int *p; int a[n]; int *p[n]; int (*p)[n]; int f( ) int *p( ) int (*p)( ) int **p;
42
指针运算小结 (1)指针变量加(减)一个整数 如:p++,p-i等 (2)指针变量赋值 p=&a; p=array; p=&array[i]; p=max; p1=p2;
43
(3)指针变量赋空值,表示不指向任何变量 p=NULL; (4)两个指针变量可以相减 指向同一数组的两个不同元素的指针相减是什么意义,而相加呢? (5)两个指针变量可以比较
44
void指针类型 ANSI增加 void指针类型,可定义一个指针变量,但不指定它是指向哪一种类型数据的,将它的值赋给另一指针变量时要进行强制类型转换. char *p1; void *p2; p1=(char *)p2;
45
总结 指针变量的优点: 1.提高程序效率 2.可以实现函数改变多个值 3.可以实现灵活的和动态的存贮分配 缺点: 难理解,灵活难以把握,容易出错 作业:
Similar presentations