PowerPoint 演示文稿

第十章
指 针
信息管理系
章
节
总
览
指针是C语言中的一个重要的概念,也是C
语言中的一个重要特色。正确灵活的使用指针:
(1) 可以有效的表示复杂的数据结构;
(2) 能动态分配内存;
(3) 能方便使用字符串;
(4) 有效而方便的使用数组;
•
•
•
•
现实生活中的指针及其特点:
钟表上的指针
1、根据指向确定值
2、不同的指针单位不同
10.1地址和指针的概念(1)
一、地址:
1.概述:如果在程序中定义了一个变量,在编译时就给这个变量分
配内存单元,该内存单元有相应的地址;同时,该内存单元可以存
放变量的值,又有相应的内容。
2.定义:内存的每一个字节有一个编号,这就是地址。
3.根据内存单元的地址访问内存单元的内容的方式
(1)直接访问: 按变量地址存取变量值的方式称为
“直接访问”方式。
另一个变量
(2)间接访问: 变量的地址存放在另一个变量中
的方式称为“间接访问”方式。
如:i_pointer=&i;(见图10.2)
指针变量
i_pointer
i
10
2000
2000 2001
地址和指针的概念
(2)
二、指针:
1.指针:一个变量的地址称为该变量的“指针”。
2.指针变量:如果有一个变量专门用来存放另一变量
的地 址(即指针),则它称为“指针变量”。
3.指针和指针变量的关系:
指针变量的值(指针变量中存 放的值)是指针
(地址)。
4.指向:所谓“指向”就是通过地址来实现
的。
i_pointer
i
10
2000
2000
例:假定程序定义了以下变量,指出各个变量的指针各是多少?
(假定这些变量在内存中连续存放,且x所占的单元从2000开始)
int x=1,y=5;
char z=‘B’;
float f=9.8;
long int m=87;
double df=12.4;
p
2000
变量x
1
2000
变量y
5
2002
变量z
66
2004
若变量p里面存放着变
变量f 9.8
量x的地址(指针)2000,
则变量p就是变量x的指 变量m 87
针变量,我们就可以说
变量df 12.4
变量p指向变量x
2005
2009
2013
10.2变量的指针和指向变量的指针变量(1)
一、指针变量的定义和赋值:
1、概述:定义的一般形式:基类型
*指针变量名
(1)指针变量不同于整型变量和其他类型变量,它是用来
专门存放地址的。必须将它定义为“指针类型”。
我们用*来标记这一变量为指针变量。
(2)指针变量的基类型用来指定指针变量所指向的变量
的类型,不可缺少。 例如: int i , j ;
int *pointer_1 ,
*pointer_2 ;
pointer_1,pointer_2用来指向整型变量,但不可以用来
指向其他类型的变量。
注意:定义时, “*”表示该变量的类型为指针类型;
而指针变量的名称为:pointer_1,pointer_2
变量的指针和指向变量的指针变量(2)
3、赋值:一个指针变量指向另一个变量:
如:pointer_1=&i;
pointer_2=&j;
注意:指针变量中只能存放地址(指针),不能将任
何其他非 地址类型的数据赋给一个指针变量。
变量i , j 的地址被分别存放到指针变量pointer_1和
pointer_2中,这样,pointer_1指向了变量i , pointer_2
指向了变量j。
pointer_1=10.0; 是非法的。
示例
如果在定义变量时变量名字前面带*,则所定义
的变量为指针变量
左面的三条语句定义了四个
例:int x,y,*p1; 一般变量x,y,z,w,也定义了
四个指针变量p1,p2,p3,p4,
float z, *p2;
其中p1只能用来存放整形变
char *p3,w, * p4; 量的地址,p2只能用来存放
浮点型变量的地址,p3,p4只
根据上面的定
能用来存放字符型变量的地
义,说明下面
址
代码的对错:
p1=2000;p1=&y;p2=&w;p2=&z;p3=&w;p4=p3;
变量的指针和指向变量的指针变量(3)
二、指针变量的使用:
1、 两个有关的运算符:
(1)& 取地址运算符
(2)* 指针运算符(或称“间接访问”运算符),非定义
时出现,用于求指针(地址)所指向的变量。
例如: int a , * p; p=&a; *p=3;
& i 为变量 i 的地址
* p 为指针变量p所指向的变量a
例:分析以下程序,并回答问题
假定程序运行时,从键盘输入了25,78.12,B 程序的结
果如何?(程序P10_1)
main()
{int a,*pa=&a;float b, *pb;char c, *pc;
pb=&b;pc=&c;
scanf(“%d,%f,%c”,&a,&b,&c);
printf(\n a=%d,b=%.0f,c=%c”,a,b,c);
printf(\n a=%d,b=%.0f,c=%c”, *pa, *pb, *pc);}
1、可否把int 语句中a和*pa=&a的位置互换?
2、scanf(“%d,%f,%c”,&a,&b,&c);语句可否换成
scanf(“%d,%f,%c”,pa,pb,pc);
3、两个printf语句可否等价?
指针变量的引用
根据&和*的含义回答以下问题,并弄清楚原因
1、若只执行了int *pa,a;语句,你能确定指针
变量pa指向了哪个变量吗?
2、若再执行pa=&a;语句,你能确定pa的指向
吗?你能确定pa所指向单元的值吗?
3、此时的 *&a与*pa和a是否等价?
4、此时&*pa与pa和&a是否等价?
5、(*pa)++与*pa++的意义如何?是否等价?
注意: *与++的级别相同,但结合方向自右向
左,即*p++等价于*(p++), (*p)++与a++等价,
*(p++)是先用p的值,然后p再加1,即p指向了a
后面的一个变量(++作用在p上)。
指
针
变
量
举
例(2)
例2:利用指针,用两种方法,输入变量a,b的值,按
先大后小的顺序输出。
方法1:让指针变量pa指向a,指针变量pb指向b,若a<b,
就交换a和b的值,见程序P10_2.c
a
b
main()
10
5
10
5
{int a,b,t,*pa, *pb;
pa=&a;pb=&b;
pa
pb
scanf(“%d,%d”,pa,pb);
&a
&b
if (*pa<*pb)
t
{t=*pa; *pa=*pb; *pb=t;}
5
printf(“\n a=%d,b=%d”,a,b);
printf(“\n max=%d,min=%d”,*pa,*pb);
}
方法2:让指针变量pa指向a,指针变量pb指向b,若a<b,
就交换pa和pb的值,见程序P10_3.c
a
b
main()
5
9
{int a,b, *t,*pa, *pb;
pa
pb
pa=&a;pb=&b;
&b
&a
&b
&a
scanf(“%d,%d”,pa,pb);
t &a
if (*pa<*pb)
{t=pa; pa=pb; pb=t;}
printf(“\n a=%d,b=%d”,a,b);
printf(“\n max=%d,min=%d”,*pa,*pb);
}
注意:方法1和方法2中的变量t有什么区别?为什么
要如此定义?方法1和方法2中各自都交换了什么?
作业:
1、模仿例题2的两种方法,从键盘输入三个
整数,按由小到大的顺序输出。
继续
指针变量(5)
关于& 和 * 的运算(& 和 * 的优先级相同) int a, *pointer_1;
(1)若有&*pointer_1
&a
pointer_1= &a;
先进行*pointer_i的运算,即为变量a;
再进行&运算,即为&a;表达式的值最终为 a 的地址
(2)若有*&a
*pointer_i
a
先进行 & a 运算,得到 a 的地址,即pointer_1或 &a;
再进行 * 运算,得到 & a 所指向的变量
a+ +
(3)(*pointer_1) + +
注意:括号是必要的。否则就成为*(pointer_i + +)此时,
先按poiter_i的原值进行 * 运算得到a的值,然后
pointer_i的值增加1 , 即pointer_指向a 以后的存储单元,
pointer_i不再指向a。
指
针
变
量(6)
三、指针变量作为函数参数:
(1) 函数的形参的类型不仅可以是整型、实型、字
符型等数据,还可以是指针类型。
作用:是将一个变量的地址传送到函数中。
( 2 )函数的类型(返回值的类型)也可以是指针类型
例1、编写函数,交换两个数的值:
main()
{int a=4,b=15;
viod swap(int x,int y);
swap(a,b);
printf(“a=%d,b=%d\n”,a,b);
}
void swap(int x,int y)
{int t;
t=x;x=y;y=t;
printf(“x=%d,y=%d\n”,x,y);
}
a
b
4
15
x
y
154
15
4
t
4
例1、编写函数,交换两个数的值:
main()
{int a=4,b=15, *p,*q;
void swap(int *p,int *q);
p=&a;q=&b;
swap(p,q);
printf(“a=%d,b=%d\n”,a,b);
}
void swap(int *m,int *n)
{ intint
t; *t;
t=*m;*m=*n;*n=t;
t=m;m=n;n=t;
printf(“*x=%d,*y=%d\n”,*m,*n)
;
}
a
b
a
15
4
4
b
15
4
15
p
p
&a
&a
q
t
t
4
&a
q
&b
&b
例2、利用子函数和指针参数,对输入的两个整
数按大小顺序输出。
算法如下:编写一个子函数,接受两个整形数
据的地址,并负责交换这两个地址里面的内容。
主函数输入两个整数,当两个整数不满足要求
时,调用子函数做交换。最后主函数输出结果
代码如下:*见程序P10_4.c
main()
{void swap(int *p1,int *p2);
int a,b, *pa, *pb;
pa=&a;pb=&b;
scanf(“%d,%d”,&a,&b);
if(a<b) swap(pa,pb);
printf(“\n a=%d,b=%d”,a,b);
}
void swap(int *p1,int *p2)
{int temp;
temp=*p1;
*p1=*p2;
*p2=temp;}
根据程序回答:
1、可否把a<b换
成*p<*pb?
2、可否把scanf
语句中的&a,&b
换成pa,pb?
3、可否把输出
语句中的a,b换
成*pa和*pb?
4、可否不使用
pa,pb,而把
swap(pa,pb)换
成swap(&a,&b)?
5、子函数中可否只交换p1与p2的值?即子函数改为如下形式:
void swap(int
*p1,int *p2)
{int *temp;
temp=p1;
p1=p2;
p2=temp;}
6、可否把代
码改为左图
内容?这些
代码交换了
什么?
main()
{void swap(int x,int y);
int a,b,;
scanf(“%d,%d”,&a,&b);
if(a<b) swap(a, b);
printf(“\n a=%d,b=%d”,a,b);
}
void swap(int x,int y)
{int temp;
temp=x;
x=y;
y=temp;}
指
针
变
量
举
例(3)
例3、阅读P10_5.c程序代码,并说明程序的功能
swap(int *p1,int *p2) exchange(int *q1,int *q2,int *q3)
{ int temp;
{ if(*q1<*q2) swap(q1,q2);
temp=*p1;
if(*q1<*q3)
swap(q1,q3);
*p1=*p2;
if(*q2<*q3)
swap(q2,q3); }
*p2=temp; }
结果:
main( )
9,0,10
{int a,b,c;
a=10,b=9,c=0
int *pointer_1,*pointer_2,*pointer_3;
scanf("%d,%d,%d",&a,&b,&c);
pointer_1=&a; pointer_2=&b;
pointer_3=&c;
exchange ( pointer_1, pointer_2, pointer_3);
printf("\na=%d,b=%d,c=%d\n",a,b,c);
}
指针与指针变量
复习巩固
复习:
1、什么是指针?
2、什么是指针变量?
3、指向是什么意思?
4、怎样定义指针变量?
5、怎样给指针变量赋值?
如果有以下定义:
int a=3, b, *p, *q, *t, m;
a
Char c=‘A’,*pc;
0000 0011
则说出下面表示的含义: 2000 2001 2002
p
p=&a; pc=&c;
p++
&a
2002
q=&b; *q=10;
c
*p, &a, *&a &*p
10001
(*p)++; *p++; pc++;
4501
4502
4500
t=p;
pc
pc++
&c
4501
m=*p;*p=*q;*q=m;
t=p;p=q;q=t;
2003
4503
判断下面写法的对错:
1、int
2、int
3、int
4、int
5、int
a,*p; p=&a;
a,*p=&a;
a, *p; *p=a;
a, *p=a;
a; printf(“%d”,*a);
练习:
1、下列程序的输出结果是(6)
int b=2;
int func(int *a)
{b+=*a; return(b);}
main()
{int a=2,res=2;
res+=func(&a);
printf(“%d”,res);
}
练习:
2、选出正确的程序段(C)
A)int p;
scanf(“%d”,p);
D)int *s, k;
B)int *s, k;
char *p,e;
*s=100;
s=&k;
C)int *s, k;
p=&e;
char *p, c;
s=p;
s=&k;
p=&c;
*s=1;
*p=‘a’;
练习:
3、有如下的程序段:
int *p,a=10, b=1;
p=&a;a=*p+b;
执行该段程序后,a的值为(11)
4、下列程序的输出结果是(2,0)
void fun(int *n)
{while((*n)--)
printf(“%d”,--(*n)); }
main()
{int a=4;
fun(&a);
}
练习:
5、以下程序段的输出结果是(2,1,4,3)
void fun(int *x,int *y)
{printf(“%d %d “,*x,*y); *x=3;*y=4;}
main()
{int x=1;y=2;
fun(&y,&x);
printf(“%d %d “,x,y;)
}
练习:
6、下列程序的运行结果是(3 6)
void fun(int *a,int *b)
{int *k;
k=a;a=b;b=k;
}
main()
{int a=3,b=6,*x=&a,*y=&b;
fun(x,y);
printf(“%d %d “,a,b);
}
练习:
7、设有以下程序:
main()
{int a,b,k=4,m=6,*p1=&k,*p2=&m;
a=k; p1=&m;
b=(*p1)/(*p2)+7;
printf(“a=%d”,a);
printf(“b=%d”,b);
}
程序执行后,a的值为(4),b的值为(8)。
10.3数组的指针和指向数组的指针变量(1)
概述:
1. 数组的指针:数组的起始地址。
2. 数组元素的指针:数组元素的地址。
一、指向数组元素的指针:
1.定义:定义一个指向数组元素的指针变量的方法,与以
前介绍的指向变量的指针变量相同。
例如:
int a[10];
int *p;
p=&a[0]; /* 指针 p就 指向了元素a[0] */
10.3数组的指针和指向数组的指针变量(1)
int a[10];
数组的名字a表示数组的首地址;即:a==&a[0];
也就是说a本身就是一个地址。
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
a
p
5
9
7
6
4
3
8
2
0
1
2000
2000
地址可以存到指针变量中。
如果有 int *p;
p=a;
因为a就是&a[0],所以也可以写成 p=&a[0];
数组的指针和指向数组的指针变量
(2)
2、C语言规定数组名代表数组的首地址,即第0个元素
的地址。 故p=&a[0]; 与p=a;等价。
注意:
数组a不代表整个数组,上述“p=a;”的作用是“把a
数组的首地址(恰好是 a[0] 的地址 )赋给指针变量p”,
而不是“把数组a各元素的值赋给p”。
3、 在定义指针变量时可以赋给初值:
int *p=&a[0];
等价于:int *p;
p=&a[0]; 或 int *p=a; /*注意:不是*p=&a[0];*/
10.3数组的指针和指向数组的指针变量(1)
int a[10],*p,*q;
若有p=a;是把数组的首地址(也就是a[0]的地址给了p,
此时,*p与a[0]等价。
若有q=&a[5];是把数组a中第5个元素的地址送给q,此
时*q与a[5]等价。
*(a+4)
*(p+9)
* (p+8)
*p *(p+1)
*(p+4
a[0] a[1] a[2] a[3]) a[4] a[5] a[6] a[7] a[8] a[9]
9
7
6
4
3
8
2
0
1
a 5
p
2000200120022003
a
&a[0]
2000
q
&a[5]
2010
若p=a; p++; p指向谁?
若p=a;p=p+4;那么a[4]又可以怎样表示?
既然p=a,那么a[4]还可以表示成什么形式?
那么*(p+5)与*p+5有什么不同?
假定执行了int a[20], *p;回答下列问题:
1、如果再执行p=&a[19]; *p=100;则数组的哪个元素被
赋值,为什么?
2、如果想让p指向数组的第0个元素,该怎么操作?可否
用p=a;为什么?
3、假定如果在定义指针变量p时,直接把a的值送给p,
即int a[20],p=a;能否让p指向数组的开始位置,即指
向a[0]?
4、假定有p=&a[1];如果再执行p++;则p指向了谁?如果
再执行p=p+5;p又指向了谁?
5、如果有p=a;再执行*p=90;这又是在给谁赋值?
6、可否用p来操作所有数组元素?
注意:如果一个指针变量指向了数组的首地址,
则这个指针变量可看作是数组的指针变量。
数组的指针和指向数组的指针变量
(3)
二、通过指针引用数组元素:
1. 概述:
(1)已有
int a[10] , *p ;
p=a;
*p=1;
表示:对 p 当前所指向的数组元素赋予一个值1 ;
(2)按C的规定,如果指针变量p已指向数组中的一个元
素,则p+1指向同一数组中的下一个元素(而不是将p的
值简单地加1, 如对于 int 和 char ,p的值变化就不同)。
2. 引用一个数组元素的方法:
(1)下标法,如a[i]形式;或者p[i]
(2)指针法,如*(a+i)或者*(p+i)。
其中a是数组名,p是指向数组的指针变量,其初值p = a
a[i], p[i] *(a+i),*(p+i)都是用来表示数组的第i+1个元素。
即下标为i的元素。
例:判断下面的代码可否完成数组的输入输出操作?见程序
D10_7.c
main()
{int a[10], *p=a,i;
for (i=0;i<10;i++,p++)
scanf(“%d”,p);
p=a;
for (i=0;i<10;i++,p++)
printf(“ %d “,*p);
}
回答问题:
1、可否去掉p=a;语句
为什么?
2、可否把
for(i=0;i<10;i++,p++)
循环中的,p++去掉,
而把输入语句换成
scanf(“%d”,p++);把输
出语句换成
printf(“ %d “,*p++);
3、可否把两个循环中的p换成数组名a?
数组的指针和指向数组的指针变量
(4)
main()
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for(i=0;i<10;i++)
printf("%d",a[i]);}
main()
{
int a[10];
int i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for(i=0;i<10;i++)
printf("%d",*(a+i));}
结果:
1 2 3 4 5 6 7 8 9 0(回车)
1234567890
main()
{
int a[10] , *p =a ;
int i;
for(i=0;i<10;i++)
scanf("%d", p+i);
printf("\n");
for(i=0;i<10;i++)
printf("%d",*p++);}
3.注意:
(1)要注意指针变量的当前值。
(2)指针变量可以指到数组以后的内存单元。
A数组
(3)注意指针变量的运算。
main( )
{ int a[10],i,*p ;
p=a;
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n");
for(i=0;i<10;i++,p++)
printf("%d",*p);
}
p, ( 2000)
p+1,a+1, p[1]
p+2,a+2, p[2]
p+3,a+3, p[3]
1
2
3
4
p+i,a+i, p[i]
p+7,a+7, p[7]
p+8,a+8, p[8]
p+9,a+9, p[9]
a[0]
a[1]
a[2]
a[3]
a[ i]
8
9
0
a[7]
a[8]
a[9]
2000
p
通过上面的分析可见数组的指针和数组名之间
存在以下关系(假定有int a[20],*p=a;):
1、p的值可以改变,但a的值不能改变,如
p++;p=p+5;p--;--p等都是正确的,但下列语句
a++;a=a+5;a--;--a等都是错误的。即数组名永远代表
数组的首地址,不能被更改,但数组的指针可以任意
改变指向。
2、除了上面的区别,在使用上数组名和数组的指针几
乎没有区别。即当指针变量p指向数组a的首个元素时,
p+i,a+i,&a[i],&p[i]都代表第i个元素的地址,
*(p+i),*(a+i), p[i], a[i]都代表第i个元素。p与a在
这点上是等价的。但若p指向的不是首地址,则上面就
不等价。
例:判断下面的代码可否完成数组的输入输出操作?见程序
D10_8.c 和D10_9.c及D10_10.c
main()
{int a[10], *p=a,i;
for (i=0;i<10;i++)
scanf(“%d”,p++);
for (i=10;i>=1; i--)
printf(“ %d “,*(p-i));
}
main()
{int a[10], *p=a,i;
for (i=0;i<10;i++)
scanf(“%d”,&p[i]);
for (p=a;p<(a+10);p++)
printf(“ %d “,*p);
}
main(){int a[10], *p=a,i;
for (i=0;i<10;i++) scanf(“%d”,p++);
for (i=0;i<10;i++)printf(“ %d “,*(a+i));}
练习:
1、若有下面的定义:int a[9],*p=a;并在以后的语句中未
改变p的值,不能表示a[3]地址的是()
A)p+1
B)a+1 C)p++ D)a++
2、若有如下图所示的5个连续的int类型的存储单元并赋
值如下图,a[0]的地址小于a[4]的地址。p和s是基类型为
int的指针变量,请对以下问题填空。
a[0]
a[1]
a[2]
a[3]
a[4]
22
33
44
55
66
1、若p已指向存储单元a[1],通过指针p给s赋值,使
s指向最后一个存储单元a[4]的语句是()
2、若指针s指向存储单元a[2],p指向存储单元a[0],
表达式s-p的值是()
练习:
3、若有下面的定义:int w[5]={23,54,10,30,98},*p=w;
则不移动指针p,且通过指针p引用值为98的数组元素的
表达式是()
4、如有如下说明:int a[10]={1,2,3,4,5,6,7,8,9,10},*p=a;
则数值为9的表达式是()
A)*p+9
B)*(p+8)
C)*p+=9
D)p+8
5、以下程序的输出结果是()
main()
{int arr[ ]={30,25,20,15,10,5},*p=arr;
p++;
printf(“%d”,*(p+3));
}
练习:
6、以下程序的输出结果是()
main()
{int a [10 ]={9,8,7,6,5,4,3,2,1,0},*p=a+5;
printf(“%d”,*--p);
}
7、下面程序的输出结果是()
main()
{int a [10 ]={1,2,3,4,5,6,7,8,9,10},*p;
p=a;
printf(“%d”,*p+9);
}
指针与指针变量
复
习
•
•
•
•
•
指针:地址
指针变量:存放指针(地址)的变量
指向:指针变量指向所存地址对应的变量。
数组的指针和指向数组的指针变量
如果有以下定义: int a[10],*p; p=a;
或者: int a[10],*p=a;
说出下列表示的含义:
a,p,&a[0],
&a[i],a+i,p+i,&p[i]
a[i],*(a+i),*(p+i),p[i];
用指针实现数组的操作
复
习
• 数组的输入和输出:
main()
{int a[10], *p=a,i;
for (i=0;i<10;i++)
scanf(“%d”, &p[i]
&a[i])
p+i
a+i ;
for (i=0;I<10; i++)
printf(“ %d “, *(p+i)
*(a+i)
p[i] );
a[i]
}
指针p不移动,始终指向
数组的首地址。
main()
{int a[10], *p=a,i;
for (i=0;i<10;i++)
scanf(“%d”,p++);
for (p=a;p<(a+10);p++)
printf(“ %d “,*p);
}
每做一次循环,指针p都
要向后移动一个位置,循
环结束后,指针p指向数
组的最后一个元素。
数组名作为函数参数
• 数组名作为函数参数,实参向形参传递的是
数组的首地址。
• 我们知道,指针变量又可以存放地址。
所以:
1、当实参为数组名时,形参可以为数组,
也可以为指针变量
2、当形参为数组时,实参可以为数组名,
也可以为指针变量
3、实参与形参的数据类型必须一致。
数组名作为函数参数
判
断
下
列
参
数
传
递
是
否
正
确
zhs(int y[])
{…}
main()
{int x[20];
…..
zhs(x);}
zhs(int y[])
{…}
main()
{int x[20];
…..
zhs(&x[5]);}
zhs(int y[])
{…}
main()
{int x[20],*p=x;
…..
zhs(p);}
zhs(int y[])
{…}
main()
{int x[20],*p=&x[0];
…..
zhs(p);}
数组名作为函数参数
判
断
下
列
参
数
传
递
是
否
正
确
zhs(int *y)
{…}
main()
{int x[20],*p=x;
…..
zhs(p);}
zhs(int *y)
{…}
main()
{int x[20];
…..
zhs(x);}
zhs(int *y)
{…}
main()
{int x[20];
…..
zhs(&x[5]);}
zhs(int *y)
{…}
main()
{int x[20],*p=x;
…..
zhs(p+5);}
数组名作为函数参数(例题)
例:用子函数和指针从10个浮点型数据中找出
最大值和最小值。(见程序P_12.c)
要求:主函数负责输入输出,子函数负责查找。
回答问题:
1、max和min两个变量为什么要放到所有函数
之外?它们是什么属性的变量?
2、如果把max和min的定义放在程序最后可否?
3、子函数的array变量可否改为int类型?为
什么?
4、程序中的实参与形参各是什么,是指针变
量还是数组名?
5、试着把参数换成各种允许的形式,验证程
序的正确性
数组名作为函数参数(练习)
• 练:用子函数和指针,将数组a中的n个整数反序存放。
子函数fxcf负责对接受到的起始地址开始的n个元素
进行反序存放,主函数负责输入输出,见程序
x[0]
P10_11.c,并回答以下问题:
void fxcf(int x[],int n)
x[1]
{函数的代码完成对
x[0]到x[n-1]之间的元
素反序存放
}
main()
{负责输入数组
调用子函数
输出交换后的数组}
x[2]
x[n-3]
x[n-2]
x[n-1]
数组名作为函数参数
思
1、子函数中的形参数组x可否换成指针变量x?
既子函数的定义为void fxcf(int *x,int n)
考
:
2、主函数中的实参数组名a可否换成&a[0]或者
一个相应的指针变量?即调用子函数时用
fxcf(&a[0] ,20);或fxcf(p);其中p为整形指针
变量且p=a;
3、如果主函数在调用子函数时采用
fxcf(a,10); ,程序的功能如何?
4、可否把子函数中的x[i]和x[j]都改为*(x+i)
和*(x+j),而不管形参是数组还是指针?
数组名作为函数参数
课 作业:用子函数和指针完成对一个整形数
后 组的排序(用冒泡法完成),并在主函数
练 中调用它完成一组数据的排序。
习
:
对于一维字符数组(字符串)
复
习
:
• 对于一个字符串,可以存放到一个字符数
组中:
• 例如:
main( )
{
char s []="I love China!”;
printf(“%s\n”,s);
}
/* string是数组名,代表字符数组的首地址 */
• 字符串的输入与输出
scanf(“%s”,s);
gets(s);
例如有如下定义:char
s[20];
printf(“%s”,s);
puts(s);
指向字符串的指针
指 • 因为字符串一般存放到一维字符数组中,
故指向字符串的指针可以看成是指向一维
向
字符数组的指针
一
• 例如:
维 main( )
字 {char s []="I love China!”;
符
char *p; p=s;
数 printf(“%s\n”, s );
组 }
的
用指针指向一个字符串以后,可以用指针
指
对字符串进行操作。
针
指向字符串的指针
指
向
一
维
字
符
数
组
的
指
针
• 字符串赋值与输出:
main( )
{
char s []="I love China!”;
printf(“%s\n”, s );
}
main( )
{
char s[20] , *p;
p=s ;
p="I love China!”;
printf(“%s\n”, p );
}
法2与法3有什么不同?
main( )
{
char *p="I love China!”;
printf(“%s\n”, p );
}
main( )
{
char s[20] , *p;
p=s ;
gets(s); gets(p );
puts(s); puts(p );
}
指向字符串的指针
例
题
:
• 例:从键盘输入一个字符串,输出整个字
符串,并输出这个字符串中下标为3的元素。
• 例:自己编写一个函数,完成字符串的复
制(不使用库函数)
字符数组与字符指针变量的讨论
• 字符数组由若干个元素组成,每个元素中放
一个字符
• 字符指针变量中存放的是地址(字符串第一
个元素的地址),只是指向这个字符串,而
决不是将字符串放到字符指针变量中。
• 赋值方式:对字符数组只能对各个元素赋值,
下面的方法是错的:
char s[20]; s=“I love China.”
对字符指针变量,char *p; p=“I love China.”
但注意,赋给p的不是字符,而是字符串第一个
元素的地址。
字符数组与字符指针变量的讨论
•
•
•
•
•
•
判断下列赋值方式的对错:
char s[20] =“I love China.”;
char s[20] ;s[20]=“I love China.”;
char *p =“I love China.”;
char *p; p =“I love China.”;
指针变量的值是可以改变的,但数组的名字
的值不能改变
字符数组与字符指针变量的讨论
• 用指针变量指向一个字符串使用更加方便。
• 例:char *format; format=“\na=%d,b=%d”;
printf(format, a,b);等价于
printf(“\na=%d,b=%d”,a,b);
也可以:
• char format[]=“\na=%d,b=%d”;
printf(format, a,b);等价于
printf(“\na=%d,b=%d”,a,b);
• 但是:
• char format[];
• format=“\na=%d,b=%d”;
printf(format, a,b);
练习
1.以下程序的输出结果是( )。
#include <stdio.h>
#include <string.h>
main( )
{char *p="abcde\Ofghjik\0";
printf("%d\n",strlen(p));
}
A)12
B)15
C)6
D)5
练习:
2.如下程序的输出结果是( )。
#include <stdio.h>
main( )
{char s[]="ABCD", *p;
for(p=s+1; p<s+4; p++) printf ("%s\n",p);
}
D)BC
C)B
A)ABCD
B)A
D
C
B
BCD
D
CD
C
CD
D
D
D
3.有以下程序:
#include <stdio.h>
void ss(char *s,char t)
{ while(*s)
{ if(*s==t)*s=t-′a′+′A′;
s++;}}
main( )
{ char str1[100]="abcddfefdbd",c=′d′;
ss(str1,c); printf("%s\n",str1);}
程序运行后的输出结果是( )。
A)ABCDDEFEDBD
B)abcDDfefDbD
C)abcAAfefAbA
D)Abcddfefdbd
4.下面程序的输出结果是______。
#include <stdio.h>
main( )
{ static char a[]="language",b[]="program";
char *ptr1=a, *ptr2=b;
int k;
for (k=0;k<7;k++)
if(*(ptr1+k)==*(ptr2+k))
printf("%c",*(ptr1+k));
}
5.下面程序的输出结果是______。
#include <stdio.h>
main( )
{ static char a[]="language";
char *ptr=a;
while (*ptr)
{ printf("%c",*ptr-32);
ptr++;
}
}
6.下面程序的输出结果是______。
#include <stdio.h>
char *p="abcdefghijklmnopq";
main( )
{ while (*p++!=′e′);
printf("%c\n",*p);}
7、当运行以下程序时,从键盘输入Happy!<CR>(<CR>
表示回车),则下面程序的运行结果是______。
#include <stdio.h>
#include <string.h>
main( )
{ char str[10],*p=str;
gets(p);
printf("%d\n",stre (p));
}
stre (char str[])
{ int num=0;
while (*(str+num)!=′\0′)num++;
return(num);
}
8.下面程序的运行结果是______。
#include <stdio.h>
#include <string.h>
main( )
{ char *p1,*p2,str[50]="abc";
p1="abc";p2="abc";
strcpy(str+1,strcat(p1,p2));
printf("%s\n",str);}
二维数组的地址
• 设有如下定义:int a[3][4];
• 则:
a[0][0]
a[0][1]
a[0][2]
a[0][3]
a[0]
a[1][0]
a[1][1]
a[1][2]
a[1][3]
a[1]
a[2][0]
a[2][1]
a[2][2]
a[2][3]
a[2]
由于二维数组可以看作是由一维数组构成的,所以数
组a可以看作有3个一维数组a[0]、a[1]、a[2]构成,
而它们又分别由4个元素构成
也就是说:二维数组a由三个元素构成,这三个元素
都是一维数组。
二维数组的地址
对于普通的一维数组a来说:
元素地址: a+0
元素值:*(a+0)
a
a[0]
a+1
*(a+1)
a+2
a+3
*(a+2) *(a+3)
a[1]
a[2]
a[3]
同样对于一维数组a[0]来说:
元素地址:a[0]+0
a[0]+1 a[0]+2 a[0]+3
元素值: *(a[0]+0) *(a[0]+1) *(a[0]+2) *(a[0]+3)
a[0]
a[0][0]
a[0][1]
a[0][2] a[0][3]
二维数组的地址
同样对于一维数组a[1]和a[2]来说:
a[0]+0
a
a+1
a[0]+1
a[0]+2 a[0]+3
a[0][3]
a[1]+3
a[0]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2]+0 a[2]+1 a[2]+2 a[2]+3
a[1]
a[0][0] a[0][1] a[0][2]
a[1]+0 a[1]+1 a[1]+2
a[2]
a[2][0] a[2][1] a[2][2] a[2][3]
a+2
思考:
1、二维数组a有三个元素:数组a[0]、a[1]和a[2],那么a
应该存放的是首地址:即&a[0],a++就是移动一个元素的
位置,即移动一行。我们说a是指向行的。
2、一维数组a[i]有4个元素:即a[i][0]、a[i][1]、a[i][2]和
a[i][3]。那么a[i]应该存放的是首地址:即&a[i][0],
a[0]++就是移动一个元素的位置,即移动一个元素。我们
说a[i]是指向列的。
二维数组的地址
a[0]+0
a
a[0]+1
a[0]+2 a[0]+3
a[0][0] a[0][1] a[0][2]
a[1]+0 a[1]+1 a[1]+2
a[0][3]
a[1]+3
a+1
a[1][0] a[1][1] a[1][2] a[1][3]
a[2]+0 a[2]+1 a[2]+2 a[2]+3
a+2
a[2][0]
a[2][1]
a[2][2]
a[2][3]
那么:
a[0][2]可以表示为*(a[0]+2)
a[2][3]可以表示为*(a[2]+2)
第i行第j列的元素a[i][j]应该如何表示?
*(a[i]+j)
a[0]
a[1]
a[2]
二维数组的地址
第i行第j列的元素a[i][j]前
面有 i*4+j个元素,故a[i][j]
的位置为:
&a[0][0]+(i+*4+j)
a(2000)
*a[0]
a[0][1] *(a[0]+1)
a[0][2]
a[i][j]也可以写成:
*(a[0]+(i*4+j))
a[0][3]
a+1
(2008)
a[0] ---a[0][0] a[0][1] a[0][2] a[0][3]
a
a[0][0]
a[1] ---a[1][0] a[1][1] a[1][2] a[1][3]
a[1][0]
a[1]
二维数组的地址
a[0]+0
a
a[0]+1
a[0]+2 a[0]+3
a[0][0] a[0][1] a[0][2]
a[1]+0 a[1]+1 a[1]+2
a[0][3]
a[1]+3
a+1
a[1][0] a[1][1] a[1][2] a[1][3]
a[2]+0 a[2]+1 a[2]+2 a[2]+3
a+2
a[2][0]
a[2][1]
a[2][2]
a[2][3]
a[0]
a[1]
a[2]
设&a[0][0]为2000,那么,a[0]里面放的是a[0][0]这个
元素的首地址:a[0]=&a[0][0]=2000;*(a[0])=a[0][0]。
a里面放的是a[0]这个元素的首地址,即a=&a[0]=2000 。
所以*a=a[0]=&a[0][0]=2000;也就是说:*可以把行地址变
为列地址,相反地,&运算可以把列地址转换为行地址。
故:元素a[i][j]的地址又可写为:*(a+i)+j
元素a[i][j] 又可写为:*(*(a+i)+j)
二维数组的地址
说出下列表示的含义:设&a[0][0]=2000
表示形式
a
&a[0]
a[0],*(a+0),*a
a+1,&a[1]
a[1],*(a+1)
a[1]+2,*(a+1)+2,&a[1][2]
*(a[1]+2), *(*(a+1)+2), a[1][2]
含义
值
2000
第0行首地址
第0行第0列的 2000
地址
第1行首地址 2008
第1行第0列的 2008
地址
第1行第2列的 2012
地址
第1行第2列的
元素
思考:a与*a,a[1]与&a[1]的值相同,但他们有什么不同?
指向二维数组的指针变量
• 设有如下的定义:int a[3][4],*p;
• 请问可以这样赋值么?
• 1、 p=&a[0][0];
• 2、 p=a[0];
• 3、 p=*a;
• 4、 p=a;
• 为什么?
如果令p=&a[0][0],那么元素a[i][j]应该如何表示?
指向二维数组的指针变量
若定义int a[3][4], *p;
a(2000)
p=&a[0][0];
元素a[i][j]的地址用p表示
为:p+i*4+j
元素a[i][j]用p可表示为:
*(p+i*4+j)
*a[0]
a[0][1] *(a[0]+1)
第i行第j列的元素a[i][j]前
面有 i*4+j个元素,故a[i][j]
的位置为:
&a[0][0]+(i+*4+j)
a[i][j]也可以写成:
*(a[0]+(i*4+j))
a[0][0]
a[0][2]
a[0][3]
a+1
(2008)
a[1][0]
a[1]
指向二维数组的指针变量
•
•
•
•
•
•
a
如何令一个指针指向一行?
定义 指向数组的指针变量:
定义格式:类型 (*指针名)[个数]
如:int (*p)[4];
若有定义int a[3][4],(*p)[4];
则可以p=a;
那么元素a[i][j]的地址又可以怎样表示?
p
元素a[i][j]又可以怎样表示?
a[0]
a[0][0]
a[0][1]
a[0][2]
a[0][3]
a+1
a[1][0]
a[1][1]
a[1][2]
a[1][3]
a[1]
a+2
a[2][0]
a[2][1]
a[2][2]
a[2][3]
a[2]
p+1
p+2
指向二维数组的指针变量
练
习
:
1、下列程序的执行结果是()
main()
{int a[3][3],*p,I;
p=&a[0][0];
for(I=0;I<9;I++) p[I]=I+1;
printf(“%d”,a[1][2]);
}
2、下列程序的输出结果是()
main()
{int a[3][3]={1,2,3,4,5,6,7,8,9},(*p)[3],I;
p=a;
printf(“%d”,*(*(p+2)+1));
}
指针数组
如果一个数组的元素都是指针变量,则这个数
组称为指针数组。
定义方式:类型 *p[数组大小];
例如:char *p[3];
定义了一个指针数组,包含三个指针元素。
注意与指向数组的指针变量的区别。
指针数组常用于处理多个字符串。
指针数组
• 例如:用指针数组处理多个字符串。
main()
{char *name[]=
{“China”,”English”,”Japan”,”Americ”};
int I;
for(I=0;I<3;I<++)
printf(“\n%s”,name[I]);
}
多级指针
• 指向指针的指针
定义方式:
类型 **指针变量名;
例如:int **p, *q, I;
I=5;
q=&I;
p=&q;
printf(“%d”,**p);
}
返回值为指针的函数
• 当一个函数的返回值为地址时,称函数的返
回值为指针,该函数成为指针型函数。
• 指针函数的定义如下:
• 数据类型 *函数名(形参列表)
{……
return(指针变量);
}
指向函数的指针
• 函数名代表函数的首地址。
• 数据类型 (*指针变量名)( );
1.如下程序的输出结果是( )。
#include <stdio.h>
main( )
{char ch[2][5]={"6937","8254"},*p[2];
int i,j,s=0;
for(i=0;i<2;i++)p[i]=ch[i];
for(i=0;i<2;i++)
for(j=0;p[i][j]>′\0′;j+=2)
s=10*s+p[i][j]-′0′;
printf("%d\n",s);
}
A)69825
B)63825
C)6385
D)693825
2.在说明语句:int *f( );中,标识符f代表的是( )。
A)一个用于指向整型数据的指针变量
B)一个用于指向一维数组的行指针
C)一个用于指向函数的指针变量
D)一个返回值为指针型的函数名
3.设有以下语句,则( )不是对a数组元素的正确
引用,其中0≤i<10。
int a[10]={0,1,2,3,4,5,6,7,8,9}, *p=a;
A)a[p-a]
B)*(&a[i])
C)p[i]
D)*(*(a+i))
4.以下程序的输出结果为( )。
#include <stdio.h>
main( )
{char *alpha[6]
={"ABCDEFGH","IJKL","MNOP","QRST","UVWX"};
Char
; int i;
p=alpha;
for(i=0;i<4;i ++ ) printf("%s",p[i]); printf("\n"); }
A)ABCDEFGHIJKL
B)ABCD
C)ABCDEFGHIJKLMNOPQRST
D)AEIM
5.以下程序通过函数指针p调用函数fun( ),请
在空格处写出定义变量p的语句。
void fun(int *x,int *y)
{…}
main( )
{
int a=10,b=20;
______;/* 定义变量p */
p=fun; p(&a,&b);
…
}