Presentation is loading. Please wait.

Presentation is loading. Please wait.

第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错

Similar presentations


Presentation on theme: "第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错"— Presentation transcript:

1 第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错
第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错 学习时应特别细心, 多动脑、多对比、多上机

2 7.1.1 变量的地址和变量的值 内存编址: 内存分配: 变量引用: 变量赋值: 内存是连续的存储空间。 对内存进行了编址
变量的地址和变量的值 内存编址: 内存是连续的存储空间。 对内存进行了编址 内存编址是连续的, 它的基本单位为字节 内存分配: 系统根据类型为变量分配内存单元 变量内存单元的起始地址即为变量的地址 编译后, 每个变量名对应一个地址 对变量的访问就是通过这个地址进行的 变量引用: 从变量名对应的地址开始的若干内存单元(字节数由定义类型确定)中取出数据; 变量赋值: 将数据按该变量定义的类型存入对应的内存单元中 内存单元的内容就是变量的值。

3 变量与地址 i k 程序中: int i; float k; 变量是对程序中数据 存储空间的抽象 内存中每个字节有一个编号-----地址
…... 2000 2001 2002 2005 内存 2003 程序中: int i; float k; i k 编译或函数调用时为其分配内存单元 变量是对程序中数据 存储空间的抽象

4 指针变量的定义 指针变量与其所指向的变量之间的关系 一般形式: [存储类型] 数据类型 *指针变量名; 例 int *p1,*p2;
3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 3 变量i 2000 i_pointer *i_pointer i &i i=3; *i_pointer=3 指针变量的定义 一般形式: [存储类型] 数据类型 *指针变量名; 例 int *p1,*p2; float *q ; int i=3, *i_pointer; i_pointer=&i; 表示定义指针变量 不是‘*’运算符 合法标识符 指针的目标变量的数据类型 注意: 1、int *p1, *p2; 与 int *p1, p2; 2、指针变量名是p1,p2 ,不是*p1,*p2 3、指针变量只能指向定义时所规定类型的变量 4、指针变量定义后,变量值不确定,应用前必须先赋值

5 指针变量:专门存放变量地址的变量叫指针变量
指针:一个变量的地址 指针变量:专门存放变量地址的变量叫指针变量 int a=10, *p; p=&a; 整型变量 指针 …... 2000 2004 2006 2005 变量a 10 变量p 2001 2002 2003 变量的内容 变量的地址 2000 指针变量 指针变量 变量 变量地址(指针) 变量值 指向 地址存入

6 指针变量的初始化 一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量,
一般形式:[存储类型] 数据类型 *指针名=初始地址值; 例 int i; int *p=&i; 赋给指针变量, 不是赋给目标变量 变量必须已说明过 类型应一致 例 int i; int *p=&i; int *q=p; 用已初始化指针变量作初值 例 int *p=&i; int i;

7 指针变量必须先赋值,再使用 main() 例 main( ) { { int i=10; int *p; int i=10,*p;
printf(“%d”,*p); } 例 main( ) { int i=10; int *p; *p=i; printf(“%d”,*p); } …... 2000 2004 2006 2005 整型变量i 10 指针变量p 2001 2002 2003 随机 危险! 例 main( ) { int i=10,k=3; int *p; p=&k; *p=i; printf(“%d”,*p); }

8 7.2.2 指针变量的引用 #include <stdio.h> void main() { int *p; int a;
p=&a; printf("please input first int number:"); scanf("%d",&a); printf("%d %d\n",*p, a); printf("please input second int number:"); scanf("%d", p); *p=*p+2; a=a+2; }

9 空指针 p指向地址为0的单元, 系统保证该单元不作它用 表示指针变量值没有意义 #define NULL 0 int *p=NULL:
定义:指针变量值为零 表示: int * p=0; #define NULL 0 int *p=NULL: p=NULL与未对p赋值不同 用途: 避免指针变量的非法引用 在程序中常作为状态比较 例 int *p; ...... while(p!=NULL) { … }

10 i_pointer &i &(*i_pointer) i *i_pointer *(&i)
&与*运算符 含义 含义: 取变量的地址 单目运算符 优先级: 2 结合性:自右向左 含义: 取指针所指向变量的内容 单目运算符 优先级: 2 结合性:自右向左 两者关系:互为逆运算 理解 2000 10 i_pointer *i_pointer &i_pointer i …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 指针变量 i_pointer &i &(*i_pointer) i *i_pointer *(&i) i_pointer = &i = &(*i_pointer) i = *i_pointer = *(&i) i_pointer-----指针变量,它的内容是地址量 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址

11 注意: (1)  指针变量定义和引用时“*”含义有差别。 在引用中 “*”是运算符, 表示指针变量指向的变量。 在指针变量定义时“*”理解为指针类型定义符 表示定义的变量是指针变量。 (2) 不能引用没有赋值的指针变量。(盲指针) (3) p=&a;是给指针变量p赋值。 *p=3; 是给p指向的变量赋值。 两者含义完全不同。 (4)必须用同类型的指针给指针变量赋值。 指针变量只存放地址。 不能直接用整型量(或非地址量)赋值给指针变量。

12 直接访问与间接访问 直接访问:按变量名存取变量值 间接访问:通过存放变量地址的变量去访问变量 例 i=3; -----直接访问 10
指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 例 i=3; 直接访问 20 3 例 *i_pointer=20; 间接访问 前提条件 i_pointer=&i;

13 10 例 int k, i, *i_pointer=&i; k=i; --直接访问 k=*i_pointer; --间接访问 10 …...
指针变量 …... 2000 2004 2006 2005 整型变量i 10 变量i_pointer 2001 2002 2003 整型变量k 10

14 例 7.1 取地址运算符&和指向运算符*的应用。 main() { int m, n; int *p=&m,*q=&n; printf("Input m,n:"); scanf("%d %d",p,&n); /* 指针变量p之前不加&,它与&m相同 */ printf("m=%d &m=%X\n",m,&m); printf("*p=%d p=%X\n",*p,p); printf("n=%d &n=%X\n",n,&n); printf("*q=%d q=%X\n",*q,q); }

15 7.2.3 函数中用指针变量作形参实现变量的引用传递(地址传递)
函数中用指针变量作形参实现变量的引用传递(地址传递) 特点:共享内存,“双向”传递 例 将数从大到小输出 …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); 5 5 9 9 变量temp 变量y 变量x (swap) COPY 9 5 5

16 特点:共享内存,“双向”传递 例 将数从大到小输出 swap(int x,int y) { int temp; 5 temp=x; x=y;
…... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) swap(int x,int y) { int temp; temp=x; x=y; y=temp; } main() { int a,b; scanf("%d,%d",&a,&b); if(a<b) swap(a,b); printf("\n%d,%d\n",a,b); 5 9 值传递 运行结果:5, 9

17 int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b);
例 将数从大到小输出 swap(int *p1, int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b)swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 2000 2000 2002 2002 COPY (swap) 指针p1 指针p2 整型p 5

18 int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b);
例 将数从大到小输出 swap(int *p1, int *p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a; pointer_2=&b; if(a<b)swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); …... 2000 2008 200A 2002 2004 2006 200C 200E 2010 ... 整型变量a 整型变量b (main) 指针pointer_1 指针pointer_2 9 5 5 9 地址传递 2000 2002 运行结果:9,5

19 7.3 指针与数组 数组名代表该数组0号元素的地址(数组首地址); 数组名是指向该数组首元素的指针常量; 数组名与指针有相同的概念 ;
数组名是指针类型的符号常量; 指向一维数组元素的指针 1.定义指向数组元素的指针变量 int a[10]; /* 数组元素是整型变量 */ int *p; /* 定义p是指向整型变量的指针变量 */ p=&a[0]; /* 赋值后p指向a数组的0号元素 */ p=&a[5]; /* 赋值后p指向a数组的5号元素 */ p=&a[0]; 和 p=a; 两句等价

20 指针与数组 指向数组元素的指针变量 p array[0] array[1] 例 int array[10]; int *p;
... 整型指针p &array[0] p 例 int array[10]; int *p; p=&array[0];  p=array; 或 int *p=&array[0]; 或 int *p=array; 数组名是表示数组首地址的地址常量

21 2.指针运算 1、指针变量可以和整数做加减操作 2、指针变量加减一个整数n, 表示指针前后移动n个元素
3、地址值增减量等于所指向变量占的字节数sizeof(type) (步长 d) int a[10]; int *p1=&a[5]; /* 定义了p1指向整数类型(d=2),初值为a[5]的地址 */ p1- -; /* p1减1, p1指向a[4],地址值减(1×sizeof(int)) */ p1+=3; /* p1加3, p1指向a[7],地址值加(3×sizeof(int)) */ 两个同类型指针可以相减得到一个整数,等于对应元素下标差, 等于地址值的差除以地址步进单位。 两个指针之间不能进行加法、乘法、除法等算术运算。

22 指针的运算 指针变量的赋值运算 如 int i, *p; p=1000; () i=p; () p=&a; (将变量a地址p)
p=array; (将数组array首地址p) p=&array[i]; (将数组元素地址p) p1=p2; (指针变量p2值p1) 不能把一个整数p,也不能把p的值整型变量 如 int i, *p; p=1000; () i=p; ()

23 指针的算术运算: 1 pi 相当于 p id (i为整型数,d为p指向的变量所占字节数)
p++, p--, p+i, p-i, p+=i, p-=i等 若p1与p2指向同一数组,p1-p2=两指针间元素个数(p1-p2)/d p1+p2 无意义 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a数组 p p+1,a+1 p+i,a+i p+9,a+9 例 p指向int型数组,且p=&a[0]; 则p+1 指向a[1] 例 int a[10]; int *p=&a[2]; p++; *p=1; 1 例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则:p2-p1=3;

24 指针变量的关系运算 若p1和p2指向同一数组,则 p1<p2 表示p1指的元素在前 p1>p2 表示p1指的元素在后
p==NULL或p!=NULL

25 a[i]  p[i]  *(p+i) *(a+i)
数组元素表示方法 [] 变址运算符 a[i]  *(a+i) p=a; a[0] a[1] a[2] a[3] a[9] ... a a+9 a+1 a+2 地址 元素 下标法 a[0] a[1] a[2] a[3] a[9] ... p p+9 p+1 p+2 地址 元素 指针法 *p *(p+1) *(p+2) *(p+9) *a *(a+1) *(a+2) *(a+9) p[0] p[1] p[2] p[9] a[i]  p[i]  *(p+i) *(a+i)

26 #include<stdio.h> void main() { int a[10],i;
3.通过指针引用数组元素 #include<stdio.h> void main() { int a[10],i; for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) printf(“%d”,a[i]); } #include<stdio.h> void main() { int a[10],i,*p; p=a; for(i=0;i<10;i++) scanf(“%d”,p+i); for(i=0;i<10;i++) printf(“%d”, *(p+i)); }

27 #include<stdio.h>
void main() { int a[10],*p; for(p=a;p<a+10;p++) scanf(“%d”,p); for(p=a;p<a+10;p++) printf(“%d”, *p); } #include<stdio.h> void main() { int a[10],i,*p; for(p=a ,i=0; i<10; i++,p++) scanf(“%d”,p); for(p=a ,i=0; i<10; i++,p++) printf(“%d”, *p); }

28 for(i=0;i<7;i++,p++) printf("%d",*p); }
例 注意指针的当前值 main() { int i,*p,a[7]; p=a; for(i=0;i<7;i++) scanf("%d",p++); printf("\n"); for(i=0;i<7;i++,p++) printf("%d",*p); } 5 8 7 6 2 3 1 4 a p p p p p p=a; p p p 指针变量可以指到数组后的内存单元

29 例 7.4 数组名与指向数组元素的指针变量。 main() { int a[5]={10,20,30,40,50}, *p=a; printf("p=%x p+1=%x\n",p,p+1); printf("a=%x a+1=%x\n",a,a+1); /* a地址值步进单位是2不是10 */ printf("&a[0]=%x &a[1]=%x \n",&a[0],&a[1]); printf("*p+2=%d *(p+2)=%d\n",*p+2,*(p+2)); /* 注意*p+2和*(p+2)的区别 */ printf("*a+2=%d *(a+2)=%d\n",*a+2,*(a+2)); }

30 4.地址越界问题 (1)不要引用没有赋值的指针变量, 使用指针变量前一定要对它正确赋值。
指针变量重新赋值后, 其中的地址值发生了变化, 新的地址值是否指向所需要的变量, 新的地址值是否有实际意义, 系统对此都不作检查, 需要由程序员自己检查 (1)不要引用没有赋值的指针变量, 使用指针变量前一定要对它正确赋值。 (2) 用指针变量访问数组元素, 随时要检查指针的变化范围, 始终不能超越上下界。 (3)指针运算中注意各运算符的优先级和结合顺序, 多使用括号, 使程序容易理解。

31 5.用数组名或指针变量作函数参数。 数组名作函数参数,是地址传递 数组名作函数参数,实参与形参的对应关系 实参 形参 数组名 指针变量

32 例 7.6 通过调用一个函数, 将整型数组的所有元素加10。
参数传递用四种方法实现。 (1)    程序如下:  #include<stdio.h> void add(int b[ ], int n) { int i; for(i=0; i<n; i++) b[i]+=10; } void main( ) { int i, a[10]= {1,2,3,4,5,6,7,8,9,10}; add(a,10); for(i=0; i<10; i++) printf("%4d",a[i]);

33 (2)   程序如下: #include<stdio.h> void add(int *p, int n) { int *pend=p+n; for(; p<pend; p++) *p+=10; } void main( ) { int a[10]={1,2,3,4,5,6,7,8,9,10}, *q=a; add(q,10); for(q=a;q<a+10;q++) printf("%4d",*q);

34 (3)   程序如下:  #include<stdio.h> void add(int *p, int n) { int *pend=p+n; for(; p<pend; p++) *p+=10; } void main( ) { int i, a[10]= {1,2,3,4,5,6,7,8,9,10}; add(a,10); for(i=0; i<10; i++) printf("%4d",a[i]);

35 (4) 程序如下: #include<stdio.h> void add(int b[ ], int n) { int i; for(i=0; i<n; i++) b[i]+=10; } void main( ) { int a[10]={1,2,3,4,5,6,7,8,9,10}, *q=a; add(q,10); for(q=a;q<a+10;q++) printf("%4d",*q);

36 例 7.7将一维数组的各元素循环右移m个位置, 用函数实现
#include<stdio.h> void rmove(int a[ ],int m, int n) { int i,*p,t; for(i=0; i<m; i++) { p=a+n-1; t=*p; for(; p>a; p--) *p=*(p-1); *p=t; } void main() { int m,n,a[100],*p; printf("\n n="); scanf("%d",&n); printf("Input %d numbers:\n",n); for(p=a; p<a+n; p++) scanf("%d", p); printf("How many place you want to move?"); scanf("%d", &m); rmove(a,m,n); printf("New:\n"); for(p=a; p<a+n; p++) printf("%5d",*p); 例 7.7将一维数组的各元素循环右移m个位置, 用函数实现

37 一级指针变量与一维数组的关系 int *p 与 int q[10] 数组名是指针(地址)常量 p=q; p+i 是q[i]的地址
数组元素的表示方法:下标法和指针法, 即若p=q, 则 p[i]  q[i]  *(p+i)  *(q+i) 形参数组实质上是指针变量,即int q[ ]  int *q 在定义指针变量(不是形参)时,不能把int *p 写成int p[]; 系统只给p分配能保存一个指针值的内存区(一般2字节);而给q分配2*10字节的内存区

38 指向多维数组元素和指向分数组的指针 *(*(a+0)+1) *(a[0]+1) int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}; a[0] a[1] a[2] 2000 2008 2016 a a+1 a+2 2000 2002 2008 2010 2016 2018 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a[0]+1 a[1]+1 a[2]+1 *(a+0)+1 *(a+1)+1 *(a+2)+1 对于二维数组: (1)a是数组名, 包含三个元素 a[0],a[1],a[2] (2)每个元素a[i] 又是一个一维 数组,包含4个 元素

39 int a[3][4]; a a+1 a+2 a[0] a[1] a[2] 2000 2008 2016 2002 2010 2018
a+i-----第i行的首地址 a[i]  *(a+i)------第i行第0列的元素地址 a[i]+j  *(a+i)+j -----第i行第j列的元素地址 *(a[i]+j)  *(*(a+i)+j)  a[i][j] a+i=&a[i]=a[i]=*(a+i) =&a[i][0], 值相等,但地址运算时步进值不相同 a+i  &a[i],表示第i行首地址, a+1由2000步进2008,实际是4个元素空间 a[i]  *(a+i)  &a[i][0], 表示第i行第0列元素地址 a[0]+1由2000步进2002,实际是1个元素空间

40 int a[3][4]; 地址表示: (1) a+1 (2) &a[1][0] (3) a[1] (4) *(a+1) 地址表示:
二维数组元素表示形式: (1)a[1][2] (2)*(a[1]+2) (3)*(*(a+1)+2) (4)*(&a[0][0]+2*4+4)

41 *(a[1]+2),*(*(a+1)+2),a[1][2]
表示形式 含义 地址 a 二维数组名,数组首地址 a[0],*(a+0),*a 第0行第0列元素地址 a+1 第1行首地址 a[1],*(a+1) 第1行第0列元素地址 a[1]+2,*(a+1)+2,&a[1][2] 第1行第2列元素地址 *(a[1]+2),*(*(a+1)+2),a[1][2] 第1行第2列元素值 2000 2016 2024 13 a为二维数组名 a[i] <=> *(a+i)

42 2.指向数组元素的指针变量 例 7.8 用指针变量输出数组元素的值。 main()
{ int a[3][5]={ {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11,12,13,14,15} }; int *p; for (p=a[0];p<a[0]+15;p++) { if((p-a[0])%5==0) printf("\n"); printf("%4d",*p); }

43 3.指向分数组的指针变量 a a+1 a+2 p p+1 p+2 表示*p有4个元素,每个元素为整型,即p是指向一维数组的指针。
定义形式: 数据类型 (*指针名)[数组维数]; 例 int (*p)[4]; int a[3][4]; a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] a[0][2] a[0][3] a[1][2] a[1][3] a[2][2] a[2][3] a a+1 a+2 p p+1 p+2 表示*p有4个元素,每个元素为整型,即p是指向一维数组的指针。 可让p指向二维数组某一行 如 int a[3][4], (*p)[4]=a; p[0]+1或 *p+1 p[1]+2或 *(p+1)+2 p的值是一维数组的 首地址,p是行指针 ( )不能少 int (*p)[4]与int *p[4]不同 *(*p+1)或 (*p)[1] *(*(p+1)+2) 一维数组指针变量维数和 二维数组列数必须相同 指向多维数组中的分数组的指针变量,所指向的应该是降一维的整个分数组。

44 例 7.9 用指向二维数组中分数组的指针变量, 按行输出二维数组各元素的值。
main() { int a[3][5]={ {1, 2, 3, 4, 5 }, {6, 7, 8, 9, 10}, {11,12,13,14,15} }; int *q,(*p)[5]; for(p=a;p<a+3;p++) /* d=10 */ { for(q=*p;q<*p+5;q++) /* d=2 */ printf("%5d",*q); printf("\n"); }

45 4.用多维数组名和指针变量作函数参数 (1) 用多维数组名作实参或形参。 如: f(int a[][5], int n);
(1) 用多维数组名作实参或形参。 如: f(int a[][5], int n); (2)  用指向元素的指针变量作实参或形参。 f1(int *p); (3)   用指向分数组的指针变量作实参或形参。 如:f2(int (*q)[5], int m); 注意: 后两者之间指针移动时地址的增量是不同的。

46 例 7.10 调用函数求两个矩阵之和 #define M 2 main() #define N 4
void add(int (*p1)[N],int (*p2)[N]) { int *q1,*q2,(*u)[N]=p1+M; for(;p1<u;p1++,p2++) { q1=*p1; for(q2=*p2;q1<*p1+N;q1++) { *q1+=*q2; q2++; } void print(int (*p)[N]) { int *q,(*u)[N]=p+M; for(;p<u;p++) { for(q=*p;q<*p+N;q++) printf("%6d",*q); printf("\n"); main() { int i,j,a[M][N]={ {1,2,3,4}, {5,6,7,8} }; int b[M][N]={{10,20,30,40}, {50,60,70,80} print(a); print(b); add(a,b); for(i=0;i<M;i++) { for(j=0;j<N;j++) printf("%6d",a[i][j]); printf("\n"); }

47 7.3.3 用字符数组和字符型指针访问字符串 1.用字符型指针变量整体输入/输出字符串 用字符型数组和字符指针两种方法整体输入输出字符串。
用字符数组和字符型指针访问字符串 1.用字符型指针变量整体输入/输出字符串 用字符型数组和字符指针两种方法整体输入输出字符串。 #include<stdio.h> main() /*定义pt为指向字符串首字符的指针变量 */ { char s[20]="I am a student!",*p=s; char *pt=“You are a teacher!”; printf(“%s\n”,s); /*用字符型数组整体输出字符串 */ printf(“%s\n”,pt); /* 用字符指针整体输出字符串 */ scanf("%s",s); printf("%s\n",s); scanf("%s",p); printf("%s\n",p); }

48 字符指针初始化:把字符串首地址赋给string  char *string; string=“I love China!”;
用字符指针实现 I l o v e C h i string n ! a \0 字符指针初始化:把字符串首地址赋给string  char *string; string=“I love China!”; 例 main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } string *string!=0

49 2.对使用字符指针变量和字符数组的讨论 (1)存储内容不同:字符指针变量存放字符串0号元素的地址;字符数组存放若干字符; (2)分配的内存单元不同: (3)赋值方法不同:字符数组只能在变量定义是整体赋初值,不能用赋值语句整体赋值,用赋值语句只能对各个元素分开赋值。字符指针变量,赋值语句将字符串首地址赋值给它,起到整体赋值的效果。 (4)指针变量的值是可以改变的, 字符数组名是地址常量, 它的值是不能改变的 char *p , char s[16]; scanf("%s",s); /* s是地址常量, 有确定地址, 正确 */ scanf (“%s”,p); /* p未指向一个具体的地址,不应这样做 */ char str[16]="I am a srudent."; /* 只能在变量定义时整体赋值 */ 可以用赋值语句将字符串首地址赋值给字符指针变量 char *a; a="I am a srudent.";

50 字符指针变量与字符数组 char *cp; 与 char str[20];
str由若干元素组成,每个元素放一个字符;而cp中存放字符串首地址 char str[20]; str=“I love China!”; () char *cp; cp=“I love China!”; () str是地址常量;cp是地址变量 cp接受键入字符串时,必须先开辟存储空间 例 char str[10]; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); () 改为: char *cp,str[10]; cp=str; scanf(“%s”,cp); ()

51 3.用字符数组或字符指针作函数参数传递字符串
void strcpy(char s1[],char s2[]) { int i=0; while(s2[i]!='\0') { s1[i]=s2[i]; i++; } s1[i]='\0'; 用字符数组名或用指向字符的指针变量做函数参数,将实参字符串的首地址赋值给被调函数的形参。 void strcpy(char *s1, char *s2) { for( ; *s2!='\0'; s1++, s2++) *s1=*s2; *s1='\0'; }

52 7.3.4 指针数组和指向指针的指针 用于处理二维数组或多个字符串 1.指针数组 定义:数组中的元素为指针变量
指针数组和指向指针的指针 用于处理二维数组或多个字符串 1.指针数组 定义:数组中的元素为指针变量 定义形式:[存储类型] 数据类型 *数组名[数组长度说明]; 例 int *p[4]; 指针数组赋值与初始化 指针本身的存储类型 指针所指向变量的数据类型 赋值: main() { int b[2][3],*pb[2]; pb[0]=b[0]; pb[1]=b[1]; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6 区分int *p[4]与int (*p)[4] 初始化: main() { int b[2][3],*pb[ ]={b[0],b[1]}; …….. } int *pb[2] pb[0] pb[1] int b[2][3] 1 2 3 4 6

53 p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4];
指针数组赋值与初始化 L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3] 赋值: main() { char a[]="Fortran"; char b[]="Lisp"; char c[]="Basic"; char *p[4]; p[0]=a; p[1]=b; p[2]=c; p[3]=NULL; …….. } 或: { char *p[4]; p[0]= "Fortran"; p[1]= "Lisp"; p[2]= "Basic"; p[3]=NULL; 初始化: main() { char *p[]={"Fortran", "Lisp", "Basic",NULL}; …….. } L i s p \0 F o r t r a n \0 B a s i c \0 p[0] p[1] p[2] p[3]

54 多级指针(指向指针的指针) 定义: 指向指针的指针 一级指针:指针变量中存放目标变量的地址 例 int *p; int i=3; p=&i;
单级间接寻址 二级指针:指针变量中存放一级指针变量的地址 例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=3; p1 &p2 &i 3 P2(指针变量) i(整型变量) 二级指针 一级指针 目标变量 二级间接寻址

55 p=&i; ()//p是二级指针,不能用变量地址为其赋值
定义形式:[存储类型] 数据类型 **指针名; 如 char **p; *p是p间接指向对象的地址 **p是p间接指向对象的值 指针本身的存储类型 最终目标变量的数据类型 例 int i=3; int *p1; int **p2; p1=&i; p2=&p1; i p1 p2 3 &i &p1 **p2, *p1 *p2 例 int i, **p; p=&i; ()//p是二级指针,不能用变量地址为其赋值 多级指针 例 三级指针 int ***p; 四级指针 char ****p;

56 例 7.15 指针数组的各元素指向整型数据的简单实例
#include <stdio.h> main() { static int a[5]={10,20,30,40,50}; /* 要用a的元素地址做初值,应加static */ int *q[5]={&a[0],&a[1],&a[2],&a[3],&a[4]}; int **p; for(p=q;p<q+5;p++) printf("%d\t",**p); }

57 例 7.16 利用指向分数组的指针变量, 输入多个字符串, 将它们按行存储在二维字符数组中, 然后输出全部字符串。再用字符型指针数组和指向指针变量的指针变量实现。
main() { char a[4][20]; char (*p)[20]; /* p是指向分数组的指针变量*/ printf("Input strings:\n"); for(p=a;p<a+4;p++) gets(*p); printf("Output strings:\n"); printf("%s\n",*p); }

58 3.指针数组作main( )函数的形参 例 7.17 /* 下标法 */ main( int argc, char *argv[ ])
{ int i; for (i=1;i<argc;i++) printf("%s\n",argv[i]); } /* 指针法 */ main( int argc, char **argv) { while(--argc>0) printf("%s\n",*++argv); }

59 7.4结构体和指针 1.指向结构体变量的指针 定义形式:struct 结构体名 *结构体指针名; main()
例 struct student *p; main() { struct student { long int num; char name[20]; char sex; float score; }stu_1,*p; p=&stu_1; stu_1.num=89101; strcpy(stu_1.name,"Li Lin"); p->sex='M'; p->score=89.5; printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n", (*p).num,p->name,stu_1.sex,p->score); } 使用结构体指针变量引用成员形式 num name sex age stu p struct student { int num; char name[20]; char sex; int age; }stu; struct student *p=&stu; 存放结构体变量在内存的起始地址 (*结构体指针名).成员名 结构体指针名->成员名 结构体变量名.成员名 例 int n; int *p=&n; *p=10;  n=10 struct student stu1; struct student *p=&stu1; stu1.num=101;  (*p).num=101 例 指向结构体的指针变量 指向运算符 优先级: 1 结合方向:从左向右

60 *p表示p指向的结构体变量,在访问结构体成员时,由于成员运算符 “·”优先于“*”运算符,*p两侧应加圆括号,即(*p)
例: (*p).number -> 称为指向运算符,具有最高的优先级,按自左至右的方向结合。

61 7.4.2 指向结构体数组元素的指针 指针变量可以指向结构体数组中的元素,改变指针变量的值就可以通过它访问结构体数组中的各元素。
指向结构体数组元素的指针 指针变量可以指向结构体数组中的元素,改变指针变量的值就可以通过它访问结构体数组中的各元素。 struct examp { int n, m; }; struct examp a[10],*p=a; a[0].m=0; a[0].n=0; p++; p->n=1; p->m=1; p=p+2; p->n=a[0].n+1; p->m= a[0].m+1;

62 struct examp { int n, m; }; struct examp a[10],*p=a; p->n++ 引用p指向的结构体变量中成员n的值, 然后使n增1。 (p++)->n 引用p指向的结构体变量中成员n的值, 然后使p增1 ++p->n 使p指向的结构体变量中成员n的值先增1(不是p的值增1), 再引用n的值。 (++p)->n 使p的值增1, 再引用p指向的结构体变量中成员n的值。

63 指向结构体数组的指针 例 指向结构体数组的指针 struct student { int num; char name[20];
例 指向结构体数组的指针 struct student { int num; char name[20]; char sex; int age; }stu[3]={{10101,"Li Lin",'M',18}, {10102,"Zhang Fun",'M',19}, {10104,"Wang Min",'F',20}}; main() { struct student *p; for(p=stu;p<stu+3;p++) printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age); } num name sex age stu[0] p stu[1] stu[2] p+1

64 #define N 10 /* 结构体数组按成绩进行选择排序 */
#include<string.h> struct student { int num; char name[12]; float score;}; struct student s[N]; main( ) { struct student *p ,*q[N]; int i,j,k; printf (" Input %d student's num name score\n",N); p=s; for (i=0; i<N; i++) { scanf("%d%s%f",&p->num,p->name,&p->score); q[i]=p++; } for (i=0; i<N-1; i++) { k=i; for (j=i+1; j<N; j++) if (q[k]->score<q[j]->score) k=j; if (k!=i) { p=q[i]; q[i]=q[k]; q[k]=p; } printf("No.: Name: score\n"); printf("%5d%15s %8.1f\n",q[i]->num,q[i]->name,q[i]->score);

65 77 88 66 53 63 74 89 78 98 54 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 77 88 66 53 63 74 89 78 98 54 s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9

66 7.4.3 用指向结构体的指针作函数参数 1、采取“值传递”的方式 2、采取“引用传递”的方式(传地址)
用指向结构体的指针作函数参数 1、采取“值传递”的方式 函数调用时, 将实参结构体的全部成员按顺序赋值给形参, 这种传递方式在空间和时间上开销较大,由于采用值传递方式, 如果在被调用函数中改变了结构体形参的值, 该值不能返回主调函数。因此一般较少使用 2、采取“引用传递”的方式(传地址) 用指向结构体变量(或数组)的指针作实参, 将结构体变量(或数组)的地址传给形参。如果在被调用函数中改变了形参指向的结构体的值, 该值可以带回主调函数

67 #define N 10 /*用指向结构体的指针作函数参数 */
#include<string.h> #define FMT "%4d %12s%8d%8d%8d%10.1f\n" struct student { int num; char name[12]; int sc[3]; float av; }; void input(struct student *s ); main() { struct student s[N],*p; printf("Input student:number name score1 score2 score3\n"); for(p=s; p<s+N; p++) input(p); printf("number name score1 score2 score3 average\n"); printf(FMT,p->nu,p->na,p->sc[0],p->sc[1],p->sc[2],p->av); } void input(struct student *s) { scanf("%d%s%d%d%d",&s->nu,s->na,&s->sc[0],&s->sc[1],&s->sc[2]); s->av=(s->sc[0]+s->sc[1]+s->sc[2])/3.0;

68 7.5 指针与函数 7.5.1 返回指针值的函数 定义格式: 类型名 *函数名(参数表);
返回指针值的函数 定义格式: 类型名 *函数名(参数表); 例如: int *f(int x, int y); char *strcpy(char *s1,char *s2) { char *p=s1; while(*s1++=*s2++); return(p); } main() { char s[20]= "I am a student."; printf("%s\n",strcpy(s,"You are a eacher."));

69 例 写一个函数,求两个int型变量中居于较大值的变量的地址
int *f1(int *x,int *y) { if(*x>*y) return x; else return y; } main() { int a=2,b=3; int *p; p=f1(&a, &b); printf("%d\n",*p); …... 2000 2008 200A 2002 2004 2006 变量a 变量b (main) 指针变量p COPY 2 3 2002 ** 指针变量y 指针变量x (f1) 2000 2002

70 7.5.2 指向函数的指针变量和函数参数 1.定义 类型说明符 (*变量名)( ); int (*p)( ); 2.赋值 变量名=函数名
指向函数的指针变量和函数参数 1.定义 类型说明符 (*变量名)( ); int (*p)( ); (*变量名)表示该变量名被定义为指针变量,其后的空括号()表示指针变量所指向的是一个函数。类型说明符定义了该函数的返回值的类型。 2.赋值 变量名=函数名 p=functionname; 3.调用 变量名(实参列表) p(x,y);

71 例 7.23 指向函数的指针变量程序举例。 #include<stdio.h> int f(int x); { return 3*x*x+5*x-7; } void main() { int (*p)( ); /* 定义p为指向函数的指针变量*/ int a,y; p=f; /* 对指向函数的指针变量p赋值*/ printf("input x= "); scanf("%d",&a); y=(*p)(a); /* 用函数指针变量形式调用函数, 相当于y=f(a) */ printf("y=%d\n ",y); }

72 用指向函数的指针变量作函数参数 例 用函数指针变量作参数,求最大值、最小值和两数之和 void main()
例 用函数指针变量作参数,求最大值、最小值和两数之和 void main() { int a,b,max(int,int), min(int,int),add(int,int); void process(int,int,int (*fun)()); scanf("%d,%d",&a,&b); process(a,b,max); process(a,b,min); process(a,b,add); } void process(int x,int y,int (*fun)()) { int result; result=(*fun)(x,y); printf("%d\n",result); max(int x,int y) { printf(“max=”); return(x>y?x:y); min(int x,int y) { printf(“min=”); return(x<y?x:y); add(int x,int y) { printf(“sum=”); return(x+y);

73 总 结 int i; 定义整形变量i; int *p; p为指向整形变量的指针变量;
总 结 int i; 定义整形变量i; int *p; p为指向整形变量的指针变量; int p[n]; 定义整形一维数组p,它有n 个整形元素; int *p[n];定义一维指针数组p,它有n个指向整形变量的指针元素; int (*p)[n];定义p为指向一维数组(含有n个整形元素)的指针变量; int p( ) ; p为返回整形函数值的函数; int *p( ); p为返回一个指针的函数,该指针指向整形数据; int (*p)( ); p为指向函数的指针变量,该函数返回一个整形值; int **p; p是一个指向整形指针变量的指针变量;

74 作业 7.1 ~~7.15 7.18~~7.20


Download ppt "第7章 指针 存储地址的变量的类型就是指针类型 能直接对内存地址操作, 实现动态存储管理 容易产生副作用, 初学者常会出错"

Similar presentations


Ads by Google