1 / 59

第 8 章 指针 指针 : C 的一个 重要概念 、 重要特色 。它使 C 具备了强大的功能,使 C 成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。

第 8 章 指针 指针 : C 的一个 重要概念 、 重要特色 。它使 C 具备了强大的功能,使 C 成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。 不掌握 指针 等于没有掌握 C 语言的 精华 。. 8 . 1 指针的概念 简单地说, 指针就是地址 。 要掌握指针的概念就必须弄清: ■ 内 s 存地址概念 ? ■ 变量与地址的关系 ? ■ 如何通过地址进行变量的存取 ?. int i,j,k; i=3; j=6; k=i+j;. 程序经编译后,变量名被转化为对应的存储单元的地址。. 说明例:

cale
Télécharger la présentation

第 8 章 指针 指针 : C 的一个 重要概念 、 重要特色 。它使 C 具备了强大的功能,使 C 成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第8章 指针 指针:C的一个重要概念、重要特色。它使C具备了强大的功能,使C成为程序设计语言之首。正确而灵活地运用它,就可以方便地处理很多其它高级语言所不能处理的问题。 不掌握指针等于没有掌握C语言的精华。

  2. 8.1 指针的概念 简单地说,指针就是地址。 要掌握指针的概念就必须弄清: ■内s存地址概念? ■变量与地址的关系? ■如何通过地址进行变量的存取?

  3. int i,j,k; i=3; j=6; k=i+j; 程序经编译后,变量名被转化为对应的存储单元的地址。 说明例: 内存用户数据 1000 3i 1002 6j 1004 9k 对变量值的存取总是按地址进行的----直接访问。

  4. 也可以采用“间接访问”方式: 先将变量i的地址存放到另一变量p1中,要访问i时,先取出p1的内容(变量i的地址),再去访问该地址所对应的内存单元中的内容(变量i的值)。

  5. int i,j,k; i=3; j=6; k=i+j; int *p1, *p2; p1=&i; p2=&j; 内存用户数据 1000 3 i 1002 6 j 1004 9 k 2000 1000 p1 2004 1002 p2

  6. i 1000 1002 j k 1004 在以上概念的基础上对指针下定义: 变量的地址就是该变量的指针。 存放地址的变量称指针变量。 p1是指向变量i的指针变量。 1000 p1 p2 1002

  7. 8.2 变量的指针和指向变量的指针变量 变量的指针 指针变量 指向变量的指针变量 用“*”代表“指向” 如*p1代表它所指向的变量i,同一内存单元。 以下两个语句等价: i=3; 直接访问 *p1=3; 间接访问

  8. int i,j,k; i=3; j=6; k=i+j; int *p1=&i; int *p2=&j; 4 内存用户数据 1000 3 i 1002 6 j 1004 9 k 2002 1000 p1 20041002 p2 *p1=4;

  9. ■指针变量的定义 指针变量也必须先定义后使用。 int *p1; ① *表示该变量为指针变量,但变量名是p1。 ② 一个指针变量只能指向同一类型的变量。 ③ 初始化. ④ 所占用的内存空间,操作系统32位、64位 int i,*p1; int i; float a; int *p1=&i; p1=&i; 合法 p1=&a; 不合法

  10. ■指针变量的使用 两种用法: ①用地址运算符& p1=&i; ②用指针运算符* (实行间接访问) *p1=100; k=*p1; 注意:1 指针变量只能放地址(指针) 2 必须初始化才能使用。 3 不能把地址赋给*p int i; int *p1,*p2; p1=100; p1=&i; *p1=100; *p2=100; p2=p1; *p2=10; *p1=&i;

  11. 例: main() { int a=100,b=10; int *p1,*p2; 定义指针变量,尚无体指向 p1=&a; p1指向a p2=&b; p2指向b //int *p1=&a,*p2=&b; 定义时完成指向 printf(“\n %d,%d”,a,b); printf(“\n %d,%d”, *p1,*p2); } 注意:要区别定义和引用中的“*”

  12. p 要特别注意以下用法的后果: int *p; *p=100; 程序将执行的命令: 指针p所指向的整型变量所在的内存单元赋值100

  13. 例:输入a和b两个整数,按先大后小的顺序输出main()例:输入a和b两个整数,按先大后小的顺序输出main() { int a,b,*p1, *p2, *p; scanf(“%d,%d”,&a,&b);1000 5 a p1=&a; p2=&b;1002 9 b if(a<b) { p=p1; p1=p2; p2=p;} printf(“\n %d,%d”,a,b);2000p1 printf(“\n%d,%d”,*p1,*p2); 2004p2 }2006 p 改变p1和p2的指向 1000 1002 1002 1000

  14. 总结: 指针变量是专门用来存储变量地址的特殊变量 只要将某一变量的地址存入指针变量中,就可通过指针变量间接访问该变量。 为什么C语言要舍近求远来间接访问变量的内容呢?

  15. ■指针变量作为函数的参数 可将指针变量作函数的参数,接受实参地址,获得具体指向,进而通过指针变量间接访问主调函数的变量。

  16. 跨越逻辑上的限制 9 5 swap(int *p1, int *p2) { int t; t=*p1;  *p1=*p2; *p2=t; 1000 5 a } 1002 9 b main() { int a,b; scanf(“%d,%d”,&a,&b);2000 p1 if(a<b)swap(&a,&b);2004 p2 printf(“\n %d,%d”,a,b);2006 t } 1000 1002

  17. swap(int p1, int p2) 不用指针变量情况 { int t; t=p1;   p1=p2; p2=t; 1000 5 a } 1002 9 b main() { int a,b; scanf(“%d,%d”,&a,&b);2000 p1 if(a<b)swap(a, b);2004 p2 printf(“\n %d,%d”,a,b);2006 t } 5 9 9 5

  18. 重要概念:使用指针变量作函数参数,被调函数可以将多个结果交给主调函数。重要概念:使用指针变量作函数参数,被调函数可以将多个结果交给主调函数。

  19. int float *x1, float *x2 *x1 *x2 main() { int k; float a,b,c,x1,x2; scanf(“%f,%f,%f”,&a,&b,&c); k=root(a,b,c,&x1,&x2); if(k) printf(“\n %f,%f”,x1,x2); } 例:编写函数,求一元二次方程的两个实根。 #include “math.h” ? root(float a,float b,float c, ) { float d; d=b*b-4*a*c; if(d<0||a==0)return(0); ? =(-b+sqrt(d))/2/a; ? =(-b-sqrt(d))/2/a; return(1); }

  20. main() { int a[100],i,max,min; float av; for(i=0;i<100;i++) scanf(“%d”,&a[i]); av=aver(a,100,&max,&min); printf(“ %d,%d,%f”,max,min,av); } 例:求n个数的最大值、最小值和平均值。 float aver(int a[], int n, int *max, int *min) { int i; float s=0; *max=a[0]; *min=a[0]; for(i=0;i<n;i++) { s+=a[i]; if(a[i]>*max) *max=a[i]; if(a[i]<*min) *min=a[i]; } return(s/100); }

  21. 1. 指针,指针变量: int *p,a; 初始化及引用: p=&a; *p=1; 2. 指针变量作为函数参数 void swap(int *a,int*b){….} int main(){ int a=9,b=8; swap(&a,&b); … }

  22. int a[8]; 2000 5 2002 3 2004 2 2006 1 6 8.3 数组的指针和指向数组的指针变量 数组有一个首地址: 数组的指针。 每个数组元素也都有地址: 数组元素的指针。 8 7 4

  23. p 2000 5 2002 1 2004 2 4 2006 7 6 8 ■指向数组元素的指针变量 int a[10],*p; p=a; 指向数组 p=&a[0]; 指向数组元素 0 3 9

  24. ■通过指针引用数组元素 p=&a[0]; p指向a[0](下标变量) *p=1; 等效于a[0]=1; 即可通过p来访问a[0] 也可以通过p来访问其它元素: *(p+1)=3; 等效于a[1]=3;(*(a+1)=3) 其中p+1指向a[1] 注意:p+1不是地址加1,而是加一个数据类型单 位。 int a[10];int *p=a; *(p+1)=5;

  25. 一般地,当p指向a[0]时: p+i ∽ a+i∽ &a[i] *(p+i) ∽ *(a+i)∽ a[i]∽ p[i] 即以下几个语句等效: a[i]=10; *(p+i)=10; *(a+i)=10; p[i]=10;

  26. 例:从键盘输入10个数到数组a: int a[10],i,*p=a,s=0 for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) scanf(“%d”,a+i); for(i=0;i<10;i++) scanf(“%d”,p+i);

  27. 累加求和的各种用法: for(i=0;i<10;i++)s+=a[i]; for(i=0;i<10;i++)s+=*(a+i); for(i=0;i<10;i++)s+=*(p+i); for(i=0;i<10;i++)s+=p[i]; for(i=0;i<10;i++)s+=*p++; 等效于*(p++),右结合 for(p=a;p<a+10;p++)s+=*p;不能使用a++

  28. p 使用指针变量访问数组时,要特别注意指针变量的当前值。 注意下例: main() { int a[10],*p=a,i; for(p=a;p<a+10;p++) scanf(“%d”,p); for(i=0;i<10;i++)printf(“%d”,*p++); } p=a;

  29. ■数组名作为函数参数 有了指针概念的基础上,重新回顾数组名作为函数参数时,数据的传递情况: 例:将数组a中的n个数按相反顺序存放。

  30. x a i=0 m=4 int inv(int x[], int n) { i,j,m,t; main() m=(n-1)/2; { for(i=0;i<=m;i++) int a[10],i; { j=n-1-i; 输入a t=x[i]; inv(a,10); x[i]=x[j]; 输出a x[j]=t; } } } a与x共用同一片内存单元 j=9

  31. a 1000 { j=n-1-i; t=*(x+i); *(x+i)= *(x+j); *(x+j)=t; } 指针法 int inv(int *x, int n) 指针变量作函数参数时的传递情况 { i,j,m,t; main() m=(n-1)/2; { for(i=0;i<=m;i++) int a[10],i; { j=n-1-i; 输入a t=x[i]; inv(a,10); x[i]=x[j]; 输出a x[j]=t; } } 下标法 } x 1000

  32. i j 进一步优化: int inv(int *x, int n) { main() int *i=x,*j=x+n,t; { for(;i<j;i++,j--) int a[10],i; { 输入a t=*i; inv(a,10); *i=*j;输出a *j=t; } } } 优点:简练 效率高

  33. 选择法:首先选择最小的数放在0位置,再在剩下的数中选择最小的数放在下一位置,┈┈,依次类推。选择法:首先选择最小的数放在0位置,再在剩下的数中选择最小的数放在下一位置,┈┈,依次类推。 void sort(int *a, int n) { int i,j,t; for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if(a[i]>a[j]){t=a[i]; a[i]=a[j]; a[j]=t;} } 只将形参改为指针变量,仍按下标法使用 void sort(int *a, int n) { int i,j,t; for(i=0;i<n-1;i++) for(j=i+1;j<n;j++) if(*(a+i)>*(a+j)) {t=*(a+i); *(a+i)= *(a+j); *(a+j)=t;} } 按指针法使用 例:选择法排序函数

  34. 进一步优化: void sort(int *a, int n) { int *i, *j,t; for(i=a;i<a+n-1;i++) for(j=i+1;j<a+n;j++) if(*i>*j) {t=*i; *i=*j; *j=t;} } main() { int a[10],j; for(j=0;j<10;j++)scanf(“%d”,a+j); sort(a,10); for(j=0;j<10;j++)printf(“%5d”,a[j]); }

  35. sort(a,5); sort(a+5,5); 分段排序? main() { int a[10],j; for(j=0;j<10;j++)scanf(“%d”,a+j); for(j=0;j<10;j++)printf(“%5d”,a[j]); }

  36. 数组名和指针的区别 • 1. 数组名指代一种数据结构,这种数据结构就是数组; char str[10];  char *pStr = str; sizeof(str)=10; sizeof(pStr)=4(平台32B);;;\ • 2. 数组名可以转换为指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;char str[10]; char *pStr = str;str++; //编译出错,提示str不是左值pStr++; //编译正确

  37. 数组名和指针的区别 • 3.数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;而且它还失去了其常量特性,可以作自增、自减等操作,可以被修改。 • void arrayTest(char str[]){    printf(“sizeof(str) =%d”,sizeof(str) );//输出指针长度str++; //编译正确 } • int main()  {char str1[10] = “Hello";arrayTest(str1);      return 0;}

  38. ■指向多维数组的指针和指针变量 从本质上说,多维数组的指针与一维数组的指针相同,但在概念上和使用上,多维数组的指针要复杂些。 以二维数组的指针为例:

  39. 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a[1]+i+1); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a+i+1); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a[1]+i); 输入数据:1 2 3 讨论以下用法的效果: for(i=0;i<3;i++) scanf(“%d”,a+i); 输入数据:1 2 3 int a[3][4] a[0] a+0 1000 3 5 8 7 a+1 a[1] 1008 2 4 6 9 a+2 a[2] ●二维数组的地址 : 一维:a,&a[i],a+i 二维: a,&a[i][j],a+i(行指针),a[i](特殊的一维数组元素,列指针),a[i]+j a+i:只能指向第i行的第一个元素,行指针 a[i]+j:只能指向第i行中的某一个元素,列指针 1016 1 6 0 4

  40. a+0 1000 a[0] a+1 1008 a[1] 15 a+2 1016 a[2] *(a[1])=15 *(a+1)=1008 注意:指针运算符*作用在行指针上的结果仍是指针----列指针; *作用在列指针上的结果---具体元素。 *(a+0),*(a+1),*(a+2) ——仍是地址。*(a+i) *(a[0]),*(a[1]),*(a[2]) ——具体元素值。*(a[i]) *(a+i)+j 也是地址,但要区别: (a+i)+j——行指针(a+1)+1 ? *(a+i)+j——列指针*(a+1)+1 ?

  41. 如果要通过a+i形式的地址访问数组元素的具体内容,则:如果要通过a+i形式的地址访问数组元素的具体内容,则: *(*(a+i))或 *(*(a+i)+j) 如:*(*(a+1)) —— a[1][0] *(*(a+1)+2) —— a[1][2] 讨论:int a[3][3]; *(a+2) *(*(a+1)+2) *(a[1]+1) *(*(a+1)+3)

  42. 例:求数组a的所有元素之和。 可有多种用法: for(i=0;i<3;i++) for(i=0;i<3;i++) for(j=0;j<4;j++) for(j=0;j<4;j++) s+=a[i][j]; s+=*(a[i]+j); for(i=0;i<3;i++) for(j=0;j<4;j++) s+=*(*(a+i)+j);

  43. k=p[1*4+2]; 合法 k=*(p+1*4+2); k=p[1][2]; 不合法 k=*(*(p+1)+2); 1 2 3 4 5 6 7 8 ●指向二维数组的指针变量 main() { int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; int i,k,*p; // int b[10];int *q=b; p=*a;//p=*(a+0);p=a[0];p=&a[0][0];(列指针变量) k=*p; k=*(p+2); for(p=*a;p<*a+2;p++)printf(“%3d”,*p); for(p=*a;p<*(a+1)+2;p++)printf(“%3d”,*p); for(p=*a;p<a[1]+2;p++)printf(“%3d”,*p); } 9 10 11 12 12 123456 123456

  44. ●指向由m个元素组成的一维数组的指针变量 可以这样定义一个指针变量: int (*p)[4] 表示p为指向由4个整型数据组成的一维数组。 注意不能省略:() int *p[4]; 具有4个指针类型数据的一维数组 int a[3][4]; 初始化:p=a+i; (行指针变量) p=a; 可通过p引用a[i][j]: p[i][j]或 *(*(p+i)+j)

  45. 例:求矩阵的上三角元素之和。 main() { int a[3][4],(*p)[4],i,j,s=0; 输入a p=a; for(i=0;i<3;i++) for(j=i;j<4;j++) s+=p[i][j]; 或 s+=*(*(p+i)+j) printf(“\n %d”,s); }

  46. ●多维数组的指针作函数参数 用于接受实参数组地址的形参可用两种:行指针和列指针。 以方阵转置为例: void at(int (*a)[3]) 用行指针 { int i,j,t; 缺点:不通用 for(i=0;i<3;i++) for(j=i+1;j<3;j++) { t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t; } }

  47. 用列指针: void at(int *a,int n) { int i,j,t; for(i=0;i<n;i++) for(j=i+1;j<n;j++) { t=a[i*n+j]; a[i*n+j]=a[j*n+i]; a[j*n+i]=t; } } 优点:通用 在编通用函数时,一般使用列指针。

  48. 8.4 字符串的指针和指向字符串的指针变量 ■字符串的表示形式 可用两种方法访问字符串: ①用字符数组存放字符串 ②用字符指针指向一个字符串

  49. main() { char c[5]=“abc”; 定义字符数组,并将字符串存入 char *p1=c,*p=“abc”;定义指针变量,指向字符串 printf(“%s”,c); 通过数组名访问字符串 printf(“%s”,p); 通过指针变量访问字符串 printf(“%-c”,*(p+1)); 通过指针变量访问字符 } c 1000 a c \0 b a b c \0 p 1000

  50. 用指针变量处理 例:字符串拷贝操作。 main() { char a[]=“abcdef”,b[20]; int i; for(i=0; *(a+i)!=’\0’; i++) *(b+i)=*(a+i); *(b+i)=’\0’; printf(“%s”,b); } main() { char a[]=“abcdef”,b[20],*p1, *p2; p1=a; p2=b; for( ; *p1!=’\0’;p1++,p2++) *p2=*p1; *p2=’\0’; printf(“%s”,b); }

More Related