ch10 指针
DESCRIPTION
Ch10 指针. 内容提要: ★指针的概念 ★指针变量 ★指针与数组 ★指针与字符串 ★指针与函数 ★返回指针值的函数 ★指针数组和多级指针. 内存. 0. …. 2000. 2001. 2002. 2003. 2005. …. 内存中每个字节有一个编号 ----- 地址. 10.1 指针的概念 变量与地址. 程序中 : int i; float k;. i. - PowerPoint PPT PresentationTRANSCRIPT
1
第十章 指针
Ch10 指针内容提要:
★ 指针的概念
★ 指针变量
★ 指针与数组
★ 指针与字符串
★ 指针与函数
★ 返回指针值的函数
★ 指针数组和多级指针
2
第十章 指针• 10.1 指针的概念
– 变量与地址
程序中 : int i;
float k;
内存中每个字节有一个编号 ----- 地址
….
..…
...
2000
2001
2002
2005
内存0
2003
i
k编译或函数调用时为其分配内存单元
变量是对程序中数据存储空间的抽象
3
第十章 指针
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
– 指针与指针变量• 指针:一个变量的地址• 指针变量:专门存放变量地址的变量叫 ~
2000
指针
指针变量
变量的内容 变量的地址
指针变量
变量
变量地址 ( 指针 )
变量值指向 地址存入
指针变量
4
第十章 指针– & 与 * 运算符
• 含义含义 : 取变量的地址单目运算符优先级 : 2结合性 : 自右向左
含义 : 取指针所指向变量的内容单目运算符优先级 : 2结合性 : 自右向左
• 两者关系:互为逆运算• 理解
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000 指针变量
i_pointer----- 指针变量,它的内容是地址量*i_pointer---- 指针的目标变量,它的内容是数据&i_pointer--- 指针变量占用内存的地址
2000 10
i_pointer *i_pointer
&i_pointer
i
i_pointer &i &(*i_pointer)i *i_pointer *(&i)i_pointer = &i = &(*i_pointer)i = *i_pointer = *(&i)
5
第十章 指针– 直接访问与间接访问
• 直接访问:按变量地址存取变量值• 间接访问:通过存放变量地址的变量去访问变量
例 i=3; ---- 直接访问
指针变量
….
..…
...
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000
3
例 *i_pointer=20; --- 间接访问
20
6
第十章 指针
指针变量…
...
….
..
2000
2004
2006
2005
整型变量 i10
变量 i_pointer
2001
2002
2003
2000
整型变量 k
例 k=i; -- 直接访问 k=*i_pointer; -- 间接访问
10
例 k=i; k=*i_pointer;
7
第十章 指针• 10.2 指针变量
指针变量与其所指向的变量之间的关系
– 指针变量的定义• 一般形式: [ 存储类型 ] 数据类型 * 指针名;
3
变量 i
2000
i_pointer
*i_pointer
i *i_pointer&i i_pointer
i=3; *i_pointer=33
变量 i
2000
i_pointer
*i_pointer
i *i_pointer&i i_pointer
i=3; *i_pointer=3
合法标识符指针变量本身的存储类型指针的目标变量的数据类型表示定义指针变量不是‘ *’ 运算符
例 int *p1,*p2; float *q ; static char *name;
注意:1 、 int *p1, *p2; 与 int *p1, p2;2 、指针变量名是 p1,p2 , 不是 *p1,*p23 、指针变量只能指向定义时所规定类型的变量4 、指针变量定义后,变量值不确定,应用前必须先赋值
8
第十章 指针– 指针变量的初始化
一般形式: [ 存储类型 ] 数据类型 * 指针名 = 初始地址值;
赋给指针变量,不是赋给目标变量
例 int i; int *p=&i; 变量必须已说明过
类型应一致
例 int *p=&i; int i;
例 int i; int *p=&i; int *q=p;
用已初始化指针变量作初值例 main( ) { int i; static int *p=&i; .............. }
不能用 auto 变量的地址去初始化 static 型指针()
10-1.c
9
第十章 指针
例 main( ) { int i=10; int *p; *p=i; printf(“%d”,*p); }
危险!
例 main( ) { int i=10,k; int *p; p=&k; *p=i; printf(“%d”,*p); }
指针变量必须先赋值 ,再使用
….
..…
...
2000
2004
2006
2005
整型变量 i10
指针变量 p
2001
2002
2003
随机
10
第十章 指针– 零指针与空类型指针
• 零指针: ( 空指针 )– 定义 : 指针变量值为零– 表示: int * p=0;
p 指向地址为 0的单元,系统保证该单元不作它用表示指针变量值没有意义
#define NULL 0int *p=NULL:
– p=NULL与未对 p 赋值不同– 用途 :
» 避免指针变量的非法引用» 在程序中常作为状态比较
例 int *p; ...... while(p!=NULL) { ...… }
• void * 类型指针– 表示 : void *p;
– 使用时要进行强制类型转换
例 char *p1; void *p2; p1=(char *)p2; p2=(void *)p1;表示不指定 p是指向哪一
种类型数据的指针变量
11
第十章 指针
例 指针的概念
main(){ int a; int *pa=&a; a=10; printf("a:%d\n",a); printf("*pa:%d\n",*pa); printf("&a:%x(hex)\n",&a); printf("pa:%x(hex)\n",pa); printf("&pa:%x(hex)\n",&pa);}
运行结果:a:10*pa:10&a:f86(hex)pa:f86(hex)&pa:f88(hex)
….
..…
...
f86
f8a
f8c
f8b
整型变量 a10
指针变量 paf87
f88
f89 f86
Ch9_0.c
12
第十章 指针例 : 输入两个数,并使其从大到小输出
main(){ int *p1,*p2,*p,a,b; scanf("%d,%d",&a,&b); p1=&a; p2=&b; if(a<b) { p=p1; p1=p2; p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2);}
运行结果: a=5,b=9 max=9,min=5
….
..…
...
指针变量 p1
指针变量 p
2000
2008
2002
2004
2006
指针变量 p2
整型变量 b
整型变量 a5
2006
9
20082006
2008
2006
Ch9_01.c
13
第十章 指针– 指针变量作为函数参数——地址传递
特点:共享内存 ,“ 双向”传递
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);}
例 将数从大到小输出…
...
….
..
2000
2008
200A
2002
2004
2006
5变量 a
变量 b
(main)
9
变量 temp
变量 y
变量 x(swap)
5
5
959
COPY
14
第十章 指针
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);}
例 将数从大到小输出
值传递
….
..…
...
2000
2008
200A
2002
2004
2006
5变量 a
变量 b
(main)
9
运行结果: 5, 9
15
第十章 指针
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 ...
59
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002(swap)
指针 p1
指针 p2
整型 p
59
20002002
COPY
5
例 将数从大到小输出 Ch9_03.c
16
第十章 指针
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 ...
59
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
59
例 将数从大到小输出
运行结果: 9 , 5
地址传递
Ch9_3.c
17
第十章 指针
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);} 运行结果: 9 , 9
例 将数从大到小输出
….
..
2000
2008
200A
2002
2004
2006
200C
200E
2010 ...
59
整型变量 a
整型变量 b
(main)
指针 pointer_1
指针 pointer_220002002
99
20002002
COPY (swap)指针 p1
指针 p2
指针 p**** 假设 2000
指针变量在使用前必须赋值!
Ch9_31.c
int x;int *p=&x;
18
第十章 指针
/*ch10_32.c*/swap(int x,int y){ int t; t=x; x=y; y=t;}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);}
运行结果: 5 , 9
例 将数从大到小输出
值传递…
...
2000
2008
200A
2002
2004
2006
200C
200E
2010 ...
59
整型 a
整型 b
(main)
pointer_1
pointer_220002002
9
COPY (swap)整型 x
整型 b
整型 t5559
Ch9_32.c
19
第十章 指针
运行结果: 5 , 9
例 将数从大到小输出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("%d,%d",*pointer_1,*pointer_2);}
….
..
2000
2008
200A
2002
2004
2006
200C
200E
2010 ...
59
整型 a
整型 b
(main)
pointer_1
pointer_220002002
20002002
COPY (swap)指针 p1
指针 p2
指针 p****2000
地址传递
20002002
Ch9_33.c
20
第十章 指针
• 10.3 指针与数组– 指向数组元素的指针变量
例 int array[10]; int *p; p=&array[0]; // p=array;或 int *p=&array[0];或 int *p=array;
array[0]array[1]array[2]array[3]
array[9]
...
整型指针 p &array[0]
p
数组名是表示数组首地址的地址常量
21
第十章 指针– 指针的运算
• 指针变量的赋值运算– 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; ()
•指针变量的关系运算– 若 p1 和 p2 指向同一数组,则
» p1<p2 表示 p1 指的元素在前» p1>p2 表示 p1 指的元素在后» p1==p2 表示 p1 与 p2 指向同一元素
– 若 p1 与 p2 不指向同一数组,比较无意义– p==NULL 或 p!=NULL
22
第十章 指针•指针的算术运算:
–pi
–p++, p--, p+i, p-i, p+=i, p-=i 等–若 p1 与 p2 指向同一数组, p1-p2= 两指针间元素个数–p1+p2 无意义
例 p 指向 float 数,则 p+1 p+1 4
例 p 指向 int 型数组,且 p=&a[0]; 则 p+1 指向 a[1]
例 int a[10]; int *p=&a[2]; p++; *p=1;
例 int a[10]; int *p1=&a[2]; int *p2=&a[5]; 则: p2-p1=3;
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
23
第十章 指针– 数组元素表示方法
a[0]a[1]a[2]a[3]
a[9]
...
a
a+9
a+1a+2
地址 元素
下标法
a[0]a[1]a[2]
a[9]
a[0]a[1]a[2]a[3]
a[9]
...
p
p+9
p+1p+2
地址 元素
指针法
*p*(p+1)*(p+2)
*(p+9)
[] 变址运算符a[i] *(a+i)
a[i] p[i] *(p+i) *(a+i)
*a*(a+1)*(a+2)
*(a+9)
p[0]p[1]p[2]
p[9]
24
第十章 指针
a[0]a[1]a[2]a[3]a[4]
例 数组元素的引用方法
main(){ int a[5],*pa,i; for(i=0;i<5;i++)
a[i]=i+1; pa=a; for(i=0;i<5;i++)
printf("*(pa+%d):%d\n",i,*(pa+i)); for(i=0;i<5;i++)
printf("*(a+%d):%d\n",i,*(a+i)); for(i=0;i<5;i++)
printf("pa[%d]:%d\n",i,pa[i]); for(i=0;i<5;i++)
printf("a[%d]:%d\n",i,a[i]);}
12345
pa
Ch10_1.c
25
第十章 指针
例 void main() { int a []={5,8,7,6,2,7,3}; int y,*p=&a[1]; y=(*--p)++; printf(“%d ”,y); printf(“%d”,a[0]); }
输出: 5 6
pp 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
例 注意指针变量的运算
6
例 int a[]={1,2,3,4,5,6,7,8,9,10},*p=a,i; 数组元素地址的正确表示:( A ) &(a+1) ( B ) a++ ( C ) &p ( D ) &p[i]
26
第十章 指针
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);}
例 注意指针的当前值
p=a;
pp 5
8
7
6
2
7
3
0
1
2
3
4
5
6
a
p
p
p
p
p
p
指针变量可以指到数组后的内存单元
Ch9_6.c
27
第十章 指针– 数组名作函数参数
• 数组名作函数参数,是地址传递• 数组名作函数参数,实参与形参的对应关系
实参 形参数组名
指针变量数组名
指针变量
数组名数组名指针变量指针变量
28
第十章 指针
例 将数组 a 中的 n 个整数按相反顺序存放
i j
3 7 9 11 0 6 7 5 4 20 1 2 3 4 5 6 7 8 9
i ji ji jji
117 6 05 94 72 3
实参与形参均用数组
void inv(int x[], int n){ int t,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i;
t=x[i]; x[i]=x[j]; x[j]=t; }}main(){ int i,a[10]={3,7,9,11,0,6,7,5,4,2}; inv(a,10); printf("The array has been reverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); printf("\n");}
m=4
29
第十章 指针例 将数组 a 中的 n 个整数按相反顺序存放
void inv(int *x, int n){ int t,*p,*i,*j,m=(n-1)/2; i=x; j=x+n-1; p=x+m; for(;i<=p;i++,j--) { t=*i; *i=*j; *j=t; }}main(){ int i,a[10]={3,7,9,11,0,6,7,5,4,2}; inv(a,10); printf(" has been reverted:\n"); for(i=0;i<10;i++) printf("%d,",a[i]); printf("\n");}
37911067542
a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]
x
p=x+m
a 数组
60
7
11
5
9
4
7
2
3
i
j
i
j
i
j
ji
j
i
Ch9_71.c
void inv(int *x, int n){ int t,*i,*j,*p,m=(n-1)/2; i=x; j=x+n-1; p=x+m; for(;i<=p;i++,j--) { t=*i; *i=*j; *j=t; }}main(){ int i,a[10],*p=a; for(i=0;i<10;i++,p++) scanf("%d",p); p=a; inv(p,10); printf("has been reverted:\n"); for(p=a;p<a+10;p++) printf("%d",*p);}
Ch9_9.c
30
第十章 指针例 将数组 a 中的 n 个整数按相反顺序存放
void inv(int x[], int n){ int t,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; t=x[i]; x[i]=x[j]; x[j]=t; }}main(){ int i,a[10],*p=a; for(i=0;i<10;i++,p++) scanf("%d",p); p=a; inv(p,10); printf("The array has been reverted:\n"); for(p=arr;p<arr+10;p++) printf("%d ",*p);}
Ch9_72.c
31
第十章 指针
• 一级指针变量与一维数组的关系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 字节的内存区
32
第十章 指针– 指针与二维数组
• 二维数组的地址
对于一维数组 :
( 1 )数组名 array 表示数组的首地址,即 array[0] 的地址;( 2 )数组名 array 是地址常量( 3 ) array+i 是元素 array[i] 的地址( 4 ) array[i] *(array+i)
array int array[10];
33
第十章 指针
对于二维数组:( 1 ) a 是数组名, 包含三个元素 a[0],a[1],a[2]
( 2 )每个元素 a[i] 又是一个一维 数组,包含 4 个 元素
a
a+1
a+2
*(*(a+0)+1)
*(a[0]+1)int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
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
34
第十章 指针– 对二维数组 int a[3][4], 有
» a----- 二维数组的首地址,即第 0 行的首地址» 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[i] *(a+i) &a[i][0] ,表示第 i 行第 0 列元
素地址,指向列
int a[3][4];
a[0]
a[1]
a[2]
2000
2008
2016
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
a+1
a+2
35
第十章 指针
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]
二维数组元素表示形式:( 1 ) a[1][2]( 2 ) *(a[1]+2)( 3 ) *(*(a+1)+2)( 4 ) *(&a[0][0]+1*4+2)
地址表示:(1) a+1 (2) &a[1][0](3) a[1](4) *(a+1)(5)(int *) (a+1)
行指针
列指针
地址表示:(1) &a[1][2](2) a[1]+2(3) *(a+1)+2(4)&a[0][0]+1*4+2
36
第十章 指针
表示形式 含义 地址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
2000
2008
2008
2012
13
37
第十章 指针• 二维数组的指针变量
– 指向二维数组元素的指针变量例 指向二维数组元素的指针变量
main(){ static 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); }}
p=*a; p=&a[0][0]; p=*(a+0); p=a;
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]
pCh9_12.c
38
第十章 指针– 指向一维数组的指针变量
» 定义形式: 数据类型 (* 指针名 )[ 一维数组维数 ]; 例 int (*p)[4];
( ) 不能少int (*p)[4] 与 int *p[4] 不同
p 的值是一维数组的首地址, p是行指针
» 可让 p 指向二维数组某一行 如 int a[3][4], (*p)[4]=a;
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[0]+1 或 *p+1
p[1]+2 或 *(p+1)+2
*(*p+1) 或 (*p)[1]
*(*(p+1)+2)
一维数组指针变量维数和二维数组列数必须相同
39
第十章 指针例 一维数组指针变量举例
main(){ static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int i,j,(*p)[4]; for(p=a,i=0;i<3;i++,p++) for(j=0;j<4;j++) printf("%d ",*(*p+j));
printf("\n");
} p=a[0]; p=*a; p=&a[0][0]; p=&a[0];
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]
p
p
p
p[0][j]
Ch9_13.c
40
第十章 指针
例 二维数组与指针运算
main(){ int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6,7,8}}; int i; int (*p)[4]=a,*q=a[0]; for(i=0;i<3;i++) { if(i==0)
(*p)[i+i/2]=*q+1;else p++,++q;
} for(i=0;i<3;i++) printf("%d,",a[i][i]); printf("%d,%d\n",*((int *)p),*q);}
运行结果: 2,4,7,5,3
1 2 3 4
3 4 5 6
5 6 7 8
p
q
2p
q
p
q
Ch9_06.c
41
第十章 指针• 二维数组的指针作函数参数
– 用指向变量的指针变量– 用指向一维数组的指针变量– 用二维数组名
实参 形参数组名 int x[][4]
指针变量 int (*q)[4]
数组名 int x[][4]
指针变量 int (*q)[4]
数组名 a
数组名 a
指针变量 p1
指针变量 p1
若 int a[3][4]; int (*p1)[4]=a; int *p2=a[0];
指针变量 p2 指针变量 int *q
42
第十章 指针例 3 个学生各学 4 门课,计算总平均分,并输出第 n 个学生成绩
main(){ void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,79,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;p++)
sum=sum+(*p); aver=sum/n; printf("average=%5.2f\n",aver);}void search(float (*q)[4], int n){ int i; printf(" No.%d :\n",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(q+n)+i));}
列指针
行指针
函数说明
float p[][4]
65 52 79 60
80 87 90 81
90 99 100 98
q
p
q[n][i]
Ch9_14.c
43
第十章 指针
Ch9_15.c
例 3 个学生各学 4 门课,计算总平均分,并查找一门以上课 不及格学生, 输出其各门课成绩
void search(float (*p)[4], int n){ int i,j,flag; for(j=0;j<n;j++) { flag=0;
for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1;if(flag==1){ printf("No.%d is fail,his scores are:\n",j+1); for(i=0;i<4;i++)
printf("%5.1f ",*(*(p+j)+i)); printf("\n");} }
}main(){ void search(float (*p)[4], int n); float score[3][4]={{...},{...},{...}}; search(score,3); }
65 52 79 60
80 87 90 81
90 99 100 98
p
p[j][i]
44
第十章 指针» 二维数组与一维数组指针变量的关系 如 int a[5][10] 与 int (*p)[10];二维数组名是一个指向有 10 个元素的一维数组的指针常量p=a+i 使 p 指向二维数组的第 i 行*(*(p+i)+j) a[i][j] 二维数组形参实际上是一维数组指针变量, 即
int x[ ][10] int (*x)[10]变量定义 ( 不是形参)时两者不等价系统只给 p 分配能保存一个指针值的内存区 ( 一般 2 字
节);而给 a 分配 2*5*10 字节的内存区
45
第十章 指针
• 10.4 指针与字符串– 字符串表示形式
• 用字符数组实现例 main( ) { char string[]=“I love China!”; printf(“%s\n”,string); printf(“%s\n”,string+7); }
I
love
Chi
string[0]string[1]string[2]string[3]string[4]string[5]string[6]string[7]string[8]string[9]
string
string[10]string[11]string[12]string[13]
n
!a
\0
46
第十章 指针• 用字符指针实现
例 main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } }
I
love
Chi
string
n
!a
\0
字符指针初始化 :把字符串首地址赋给 string char *string; string=“I love China!”;
string
*string!=0
Ch9_17.c
47
第十章 指针– 字符串指针作函数参数
例 用函数调用实现字符串复制
( 1 )用字符数组作参数
( 2 )用字符指针变量作参数
aI
am
a
teac
eh
\0
r.
froma
by
u
ar
a
s
ut
n
de
tob
o
e
t.
\0
I
a
a
e
c
eh
\0
r.
t.
\0
m
t
a
void copy_string(char from[],char to[]){ int i=0; while(from[i]!='\0') { to[i]=from[i];
i++; } to[i]='\0';}main(){ char a[]="I am a teacher."; char b[]="You are a student."; printf("string_a=%s\n string_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b);}
void copy_string(char *from,char *to){ for(;*from!='\0';from++,to++) *to=*from; *to='\0';}main(){ char *a="I am a teacher."; char *b="You are a student."; printf("string_a=%s\nstring_b=%s\n",a,b); copy_string(a,b); printf("\nstring_a=%s\nstring_b=%s\n",a,b);}
Ch9_201.c
Ch9_20.c
48
第十章 指针– 字符指针变量与字符数组
char *cp; 与 char str[20];
• str 由若干元素组成,每个元素放一个字符;而 cp 中存放字符串首地址
• char *cp; cp=“I love China!”; ()
• char str[20]; str=“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); ()
49
第十章 指针字符串与数组关系
– 字符串用一维字符数组存放– 字符数组具有一维数组的所有特点
» 数组名是指向数组首地址的地址常量» 数组元素的引用方法可用指针法和下标法» 数组名作函数参数是地址传递等
– 区别» 存储格式:字符串结束标志» 赋值方式与初始化» 输入输出方式: %s %c
char str[]={“Hello!”}; ()char str[]=“Hello!”; ()char str[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; ()char *cp=“Hello”; ()int a[]={1,2,3,4,5}; ()int *p={1,2,3,4,5}; ()
char str[10],*cp;int a[10],*p;str=“Hello”; ()cp=“Hello!”; ()a={1,2,3,4,5}; ()p={1,2,3,4,5}; ()
scanf(“%s”,str);printf(“%s”,str);gets(str);puts(str);
50
第十章 指针
• 10.5 指针与函数– 函数指针:函数在编译时被分配的入口地址 , 用
函数名表示 max
….
..
指令 1指令 2
• 函数指针变量赋值 : 如 p=max;函数返回值的数据类型专门存放函数入口地址
可指向返回值类型相同的不同函数
– 指向函数的指针变量• 定义形式: 数据类型 (* 指针变量名 )();
如 int (*p)();
函数指针变量指向的函数必须有函数说明• 函数调用形式: c=max(a,b); c=(*p)(a,b); c=p (a,b);
• 对函数指针变量 pn, p++, p-- 无意义
( ) 不能省int (*p)() 与 int *p() 不同
51
第十章 指针例 用函数指针变量调用函数,比较两个数大小
main(){ int max(int ,int); int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c);} int max(int x,int y){ int z; if(x>y) z=x; else z=y; return(z);}
main(){ int max(int ,int), (*p)(); int a,b,c; p=max; scanf("%d,%d",&a,&b); c=(*p)(a,b); printf("a=%d,b=%d,max=%d\n",a,b,c);}int max(int x,int y){ int z; if(x>y) z=x; else z=y; return(z);}
Ch9_23.c
52
第十章 指针– 用函数指针变量作函数参数
例 用函数指针变量作参数,求最大值、最小值和两数之和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=”);printf(“max=”); return(x>y?x:y);return(x>y?x:y);}min(int x,int y){ printf(“min=”);printf(“min=”); return(x<y?x:y);return(x<y?x:y);}add(int x,int y){ printf(“sum=”); printf(“sum=”); return(x+y);return(x+y);}
Ch9_24.c
53
第十章 指针• 10.6 返回指针值的函数
– 函数定义形式: 类型标识符 * 函数名 ( 参数表 );例 int *f(int x, int y)
例 : 指针函数实现:有若干学生成绩,要求输入学生序号后, 能输出其全部成绩 .main()
{ float score[][4]={{60,70,80,90}, {56,89,67,88},{34,78,90,66}};
float *search(float (*pointer)[4],int n), *p; int i,m; printf("Enter the number of student:"); scanf("%d",&m); printf("The scores of No.%d are:\n",m); p=search(score,m); for(i=0;i<4;i++) printf("%5.2f\t",*(p+i));}float *search(float (*pointer)[4], int n){ float *pt; pt=*(pointer+n); return(pt); }
pointer
pointer+1
34 78 90 66
56 89 67 88
60 70 80 90
score 数组
p p p p
Ch9_26.c
54
第十章 指针例 写一个函数,求两个 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
23
指针变量 y
指针变量 x(f1)
20022000
COPY
变量 a
变量 b
(main)
指针变量 p**
Ch9_009.c
2002
55
第十章 指针• 10.7 指针数组和多级指针
用于处理二维数组或多个字符串– 指针数组
• 定义:数组中的元素为指针变量• 定义形式: [ 存储类型 ] 数据类型 * 数组名 [ 数组长度说
明 ] ;例 int *p[4];
指针所指向变量的数据类型指针本身的存储类型区分 int *p[4] 与 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
2
4
6
初始化 :main(){ int b[2][3],*pb[ ]={b[0],b[1]}; ……..}
int *pb[2]
pb[0]pb[1]
int b[2][3]
1
2
3
2
4
6
56
第十章 指针• 指针数组赋值与初始化
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] 0
赋值 :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; ……..}
或 :main(){ 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] 0
57
第十章 指针
char name[5][9]={“gain”,“much”,“stronger”, “point”,“bye”};
char *name[5]={“gain”,“much”,“stronger”, “point”,“bye”};
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
name[0]
name[1]
name[2]
name[3]
name[4]b y e \0
g a i n \0
s t r o n g e r \0
p o i n t \0
m u c h \0
b y e \0
• 二维数组与指针数组区别:
二维数组存储空间固定 ;字符指针数组相当于可变列长的二维数组 ;分配内存单元 = 数组维数 *2+各字符串长度 ;
指针数组元素的作用相当于二维数组的行名 ;但指针数组中元素是指针变量 ;二维数组的行名是地址常量 ;
58
第十章 指针
main(){ int b[2][3],*pb[2]; int i,j; for(i=0;i<2;i++) for(j=0;j<3;j++)
b[i][j]=(i+1)*(j+1); pb[0]=b[0]; pb[1]=b[1]; for(i=0;i<2;i++) for(j=0;j<3;j++,pb[i]++)
printf("b[%d][%d]:%2d\n",i,j,*pb[i]);}
例 : 用指针数组处理二维数组
int *pb[2]
pb[0]pb[1]
int b[2][3]b[0][0] *pb[0]
b[0][1] *(pb[0]+1)
b[0][2] *(pb[0]+2)
b[1][0] *pb[1]
b[1][1] *(pb[1]+1)
b[1][2] *(pb[1]+2)
1
2
3
2
4
6
Ch9_281.c
59
第十章 指针例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n);}void sort(char *name[],int n){ char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} }}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
jk
jj
j
i=0
Ch9_28.c
60
第十章 指针
例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n);}void sort(char *name[],int n){ char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} }}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
kjj
j
i=1
k
Ch9_28.c
61
第十章 指针
例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n);}void sort(char *name[],int n){ char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} }}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASICk
k j
j
i=2
Ch9_28.c
62
第十章 指针
例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n);}void sort(char *name[],int n){ char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} }}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
k
j
i=3
Ch9_28.c
63
第十章 指针
例 对字符串排序(简单选择排序)main(){ void sort(char *name[],int n), print(char *name[],int n); char *name[]={"Follow me","BASIC", "Great Wall","FORTRAN","Computer "}; int n=5; sort(name,n); print(name,n);}void sort(char *name[],int n){ char *temp; int i,j,k; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j; if(k!=i) { temp=name[i]; name[i]=name[k]; name[k]=temp;} }}
name[0]
name[1]
name[2]
name[3]
name[4]
name
Great Wall
FORTRAN
Computer
Follow me
BASIC
Ch9_28.c
64
第十章 指针– 多级指针
• 定义 : 指向指针的指针• 一级指针 : 指针变量中存放目标变量的地址
p1
&p2 &i 3
P2( 指针变量 ) i( 整型变量 )例 int **p1; int *p2; int i=3; p2=&i; p1=&p2; **p1=5;
• 二级指针 : 指针变量中存放一级指针变量的地址
例 int *p; int i=3; p=&i; *p=5;
&i 3
P( 指针变量 ) i( 整型变量 )
一级指针 单级间接寻址
二级指针 一级指针 目标变量
二级间接寻址
65
第十章 指针– 定义形式: [ 存储类型 ] 数据类型 ** 指针名;如 char **p;
例 int i, **p; p=&i; //p 是二级指针,不能用变量地址为其赋值
指针本身的存储类型最终目标变量的数据类型*p是 p间接指向对象的地址**p 是 p间接指向对象的值例 int i=3;
int *p1; int **p2; p1=&i; p2=&p1; **p=5;
ip1
p2
3&i
&p1
**p2, *p1
*p2
• 多级指针
例 三级指针 int ***p; 四级指针 char ****p;
66
第十章 指针例 用二级指针处理字符串
#define NULL 0void main(){ char **p; char *name[]={"hello","good","world","bye",""}; p=name+1; printf("%o : %s ", *p,*p); p+=2; while(**p!=NULL) printf("%s\n",*p++);}
name[0]
name[1]
name[2]
name[3]
name[4]
char *name[5]
world
bye
\0
hello
good
name
p
运行结果:644 : good bye
用 *p 可输出地址 (%o 或 %x), 也可用它输出字符串 (%s)
p*(p++)
Ch9_000.c
67
第十章 指针
• 二级指针与指针数组的关系int **p 与 int *q[10]
– 指针数组名是二级指针常量– p=q; p+i 是 q[i] 的地址– 指针数组作形参, int *q[ ] 与 int **q完全等价;但作为变量定
义两者不同– 系统只给 p 分配能保存一个指针值的内存区;而给 q 分配 10块
内存区,每块可保存一个指针值
68
第十章 指针– 命令行参数
• 命令行:在操作系统状态下,为执行某个程序而键入的一行字符
• 命令行一般形式:命令名 参数 1 参数 2……… 参数n
main(int argc, char *argv[]){ ………}
• 命令行参数传递
• 带参数的 main 函数形式:C:\TC> copy[.exe] source.c temp.c
有 3 个字符串参数的命令行
命令行中参数个数 元素指向命令行参数中各字符串首地址
形参名任意
命令行实参 main( 形参 )
系统自动调用main 函数时传递
第一个参数 : main 所在的可执行文件名
69
第十章 指针例 输出命令行参数
/*test.c*/main(int argc, char *argv[]){ while(argc>1) { ++argv; printf("%s\n",*argv); --argc; }}
main(int argc, char *argv[]){ while(argc-->0) printf("%s\n",*argv++);}
1. 编译、链接 test.c ,生成可执行文件 test.exe2. 在 DOS 状态下运行 (test.exe 所在路径下)
例如: C:\TC> test[.exe] hello world!
运行结果: hello world!
运行结果: test hello world!
argv[0]
argv[1]
argv[2]
char *argv[]
world
test
hello
argv
argc=3
Ch9_311.c
70
第十章 指针
定义 含义int i;
int *p;
int a[n];
int *p[n];
int (*p)[n];
int f();
int *p();
int (*p)();
int **p;
定义整型变量 i
p 为指向整型数据的指针变量
定义含 n 个元素的整型数组 a
n 个指向整型数据的指针变量组成的指针数组 p
p 为指向含 n 个元素的一维整型数组的指针变量
f 为返回整型数的函数
p 为返回指针的函数,该指针指向一个整型数据
p 为指向函数的指针变量,该函数返回整型数p 为指针变量,它指向一个指向整型数据的指针变量
指针的数据类型
71
第十章 指针
例 下列定义的含义( 1 ) int *p[3];( 2 ) int (*p)[3];( 3 ) int *p(int);( 4 ) int (*p)(int);( 5 ) int *(*p)(int);( 6 ) int (*p[3])(int);( 7 ) int *(*p[3])(int); 函数指针数组,函数返回 int 型指针
指针数组指向一维数组的指针返回指针的函数
指向函数的指针,函数返回 int 型变量指向函数的指针,函数返回 int 型指针函数指针数组,函数返回 int 型变量