1
第 8章 指 针
C 语言程序设计北京科技大学 计算机系
2009-7-29
2
8.1 指针和指针变量
8.2 指针与数组
8.3 指针与函数第 8章 指针
2009-7-29
38.1 指针和指针变量
main()
{float x;
int y;
…
}
8.1.1 指针和指针变量的概念变量的两个物理意义 2000
2001
2002
2003
2004
2005
…
x
y
变量的内容变量的地址
2009-7-29
48.1 指针和指针变量
main()
{int a,b,c;
a=5;
b=3;
c=a+b;
…
}
8.1.1 指针和指针变量的概念
2000
2001
2002
2003
2004
2005
a
b
c
5
直接将整数 5存入变量 a 3
直接将整数 3存入变量 b 8
直接 将变量 a,b的值取出,相加后存入变量 c
利用变量名存取数据的方式称为,直接存取
”方式。
2009-7-29
5
C 语言还提供了对内存单元的
间接存取?
方式
p
2000
2001
2002
2003
2004
2005
2006
2007
a
b
c
5
3
2004
a和 b相加的结果 (*p=a+b)存放到变量 p所指向的 内存单元 中去。
此时 c称为指针变量 p的 目标变量 。
p
2004
c
p=&c
8
main()
{int a=5,b=3,c,*p;
p = &c;
*p = a + b;
…
}
8.1 指针和指针变量
8.1.1 指针和指针变量的概念
● 变量的地址称为变量的指针
● 存放地址的变量称为指针变量
2009-7-29
6
main()
{ int a,*p=&a;
float x,*q=&x;
…
}
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
a
p
x
q
2004
8.1 指针和指针变量
8.1.2 指针变量的定义
2000main()
{int a,*p;
float x,*q;
p = &a;
q = &x;
…
}
指针变量定义的形式 指针变量初始化
2009-7-29
7
main()
{ int a,*p=&a;
float x,*q=&x;
*p = 5;
printf("a+3=%d\n",*p+3);
…
}
间接存取运算
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
a
p
x
q
2004
8.1 指针和指针变量
8.1.3 指针的基本运算
2000
*p = &a;×
main()
{int a,*p;
float x,*q;
p = &a;
q = &x;
…
}
赋值运算
5
2009-7-29
8
方法 1:目标变量值不变,改变指针变量的指向求解。
main( )
{ int a,b,*p1,*p2,*p;
p1=&a;p2=&b;
scanf(″ %d%d″,p1,p2);
if(*p1<*p2)
{ p=p1; p1=p2; p2=p;}
printf(″ a=%d,b=%d\n″,a,b);
printf(″ max=%d,min=%d\n″,*p1,*p2);
}
a b
p1 p2
p
6 8
a=6,b=8
max=8,min=6
8.1 指针和指针变量
【例 8.1】输入 a,b两个整数,
使用指针变量按大小顺序输出这两个整数。
2009-7-29
9
方法 2:利用指针变量直接改变目标变量的值求解。
main()
{ int a,b,t,*p1,*p2;
p1=&a;p2=&b;
scanf(″ %d%d″,p1,p2);
if(*p1<*p2)
{ t=*p1; *p1=*p2; *p2=t;}
printf(″ a=%d,b=%d\n″,a,b);
printf(″ max=%d,min=%d\n″,*p1,*p2);
}
a,*p1 b,*p2
p1 p2
t
6 8
a=8,b=6
max=8,min=6
8.1 指针和指针变量
6
8 6
2009-7-29
10
指针的加减运算
8.1 指针和指针变量
8.1.3 指针的基本运算
main()
{int a[]={10,20,30,40,50},*p1,*p2;
p1=p2=a;
printf(″p1 =%u,*p1=%d\n″,p1,*p1);
p2+=3;
printf(″p2 =%u,*p2=%d\n″,p2,*p2);
}
P1=404,*p1=10
P2=410,*p2=40
404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2
指针加减运算要点:
① 只有当指针变量指向数组时指针的加减运算才有意义。
② 指针变量可加减一个整型表达式。如:
p1++,p2+3,p2--,p2-2。
③ 指针的加减运算是以基类型为单位 (即
sizeof( 类型) )的 。
④ 两个指针变量不能作加法运算,只有当两个指针变量指向同一数组时,进行指针变量相减才有实际意义。如,p2-p1。
2009-7-29
11
指针的关系运算
8.1 指针和指针变量
8.1.3 指针的基本运算 404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2
① 指向同一数组的两个指针可以进行关系运算,表明它们所指向元素的相互位置关系 。
如,p2 > p1,p2 == p1。
② 指针与一个整型数据进行比较是没有意义的 。
③ 不同类型指针变量之间比较是非法的 。
④ NULL可以与任何类型指针进行 ==,!=
的关系运算,用于判断指针是否为空指针 。
2009-7-29
12
⑴ 数组名是该数组的指针
a是 数组的首地址(即 a[0]的地址),是一个指针常量。
a = &a[0],a+1 = &a[1],…,a+9 = &a[9]
数组元素的下标表示法:
a[0],a[1],…,a[i],…,a[9]
数组元素的指针表示法:
*(a+0),*(a+1),…,*(a+i),…,*(a+9)
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针例如,int a[10],*p;
a[0]
a[9]
p
a
2009-7-29
13
当 p指向 a[0]时,用 p表示数组元素下标法:
p[0],p[1],…,p[i],…,p[9]
指针法:
*(p+0),*(p+1),…,*(p+i),…,*(p+9)
a[0]
a[9]
p
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针
⑵ 指向一维数组元素的指针变量由于数组元素也是一个内存变量,所以此类指针变量的定义和使用与指向变量的指针变量相同。例如,int a[10],*p;
p = a;(或 p = &a[0];)
a
2009-7-29
14
a[0]
a[9]
p
a
【例 8.3】用指针法输出数组元素。
main( )
{ int a[10],i,*p;
for (i=0; i<10; i++)
scanf("%d",a+i);
for (i=0; i<10; i++)
printf("%4d ",*(a+i));
printf("\n");
for (p=a,i=0; i<10; i++)
printf("%4d ",*(p+i));
printf("\n");
}
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针 1
2
3
4
5
6
7
8
9
10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
2009-7-29
15
a[0]
a[9]
p
a
【例 8.3】用指针法输出数组元素。
main( )
{ int a[10],i,*p;
for (p=a; p<a+10; p++)
scanf("%d",p);
for (p=a; p<a+10; p++)
printf("%d ",*p);
printf("\n");
}
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针 1
2
3
4
5
6
7
8
9
10
2009-7-29
16
a[0]
a[9]
p
a[1]
.
.
.
.
.
.
1
2
3
4
5
6
7
8
9
10
a
数组名 是地址,指向数组的 指针变量 存放的也是地址。
通过指针变量也可以引用数组元素。 p=&a[0] 等效于 p=a。数组名 和指向数组的 指针变量 的区别:
指针变量 p是 变量 可以赋值,
数组名 a是地址 常量 不能赋值。
8.2 指针与数组
8.2.1 指向数组的指针用指针变量引用数组元素,
必须关注其当前值。例如:
p = p + 3
*(p-1),p[-1]等价于 a[2]
2009-7-29
17
【 例 8.4】输入五个整数,使用指针变量将这五个数按从小到大排序后输出。
main( )
{ int a[5],*pp,*p,*q,t;
for (p=a; p<a+5;p++)
scanf("%d",p);
for (p=a; p<a+4; p++)
{ pp=p;
for (q=p+1; q<a+5; q++)
if (*pp>*q) pp=q;
if (pp!=p)
{ t=*p;*p=*pp;*pp=t; }
}
for (p=a; p<a+5; p++)
printf("%d ",*p);
printf("\n");
}
8.2 指针与数组输入 a数组
for (p=a; p<a+4; p++)
pp=p
for (q=p+1; q<a+5; q++)
T *pp>*q F
pp=q
T pp!=p F
*pp?*p
输出 a数组图 8.9 选择法排序 N-S图
2009-7-29
18
0
10
20
1
11
21
2
12
22
3
13
23
a
⑴ 二维数组的地址例如,int a[3][4];
① 二维数组名 a是数组的 首地址 。
② 二维数组 a包含三个行元素,a[0],a[1],a[2] 。
a[0]
a[1]
a[2]
a
三个行元素的地址分别是,a,a+1,a+2。
而 a[0],a[1],a[2]也是地址量,是一维数组名,
即 *(a+0),*(a+1),*(a+2)是一维数组首个元素地址。
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a+1
a+2
2009-7-29
19
⑵ 二维数组元素的地址
a[0],a[1],a[2]是一维数组名,
所以 a[i]+j是 数组元素的地址。
数组元素 a[i][j]的地址可以表示为下列形式:
&a[i][j],a[i]+j,*(a+i)+j
0
10
20
1
11
21
2
12
22
3
13
23
a[0]数组
a[1]数组
a[2]数组
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a[2]+1 a[2]+2 a[2]+3
a[0]+1 a[0]+2 a[0]+3
2009-7-29
20
⑶ 二维数组元素的表示法数组元素可用下列形式表示:
a[i][j],*(a[i]+j),*(*(a+i)+j)
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a是 二 维数组,根据 C的地址计算方法,a经过两次 *操作才能访问到数组元素。所以
*a 是 a[0],**a 才 是 a[0][0]。
a[0]是 a[0][0]的地址,*a[0]是 a[0][0]。
0
10
20
1
11
21
2
12
22
3
13
23
a[0]
a[1]
a[2]
a
2009-7-29
21
⑷ 指向二维数组元素的指针变量
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
【例 8.5】用指向数组元素的指针变量输出数组元素。
main( )
{ int a[3][4]={{0,1,2,3},{10,11,12,13},{20,21,22,23}},i,j,*p;
for (p=a[0],i=0; i< 3; i++)
{ for (j=0; j< 4; j++)
printf("%4d",*(p+i*4+j)); /* 元素的相对位置为 i*4+j */
printf("\n");
}
}
这种指针变量的定义及使用与指向一维数组元素的指针变量是相同的,用它存放二维数组元素的地址。
0
10
20
1
11
21
2
12
22
3
13
23
ap
2009-7-29
22
⑸ 指向一维数组的指针变量指向一维数组指针变量的定义形式:
数据类型标识符 (*变量名 )[元素个数 ]
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
*?表示其后的 变量名 为指针类型,[ 元素个数 ] 表示目标变量是一维数组,并说明一维数组元素的个数。由于
*?比? [ ]?的运算级别低,? *变量名?作为一个说明部分,两边必须加括号。数据类型标识符?是定义一维数组元素的类型。
2009-7-29
23
【 例 8.6】 用指向一维数组的指针变量输出数组元素 。
main( )
{ int a[3][4]={{0,1,2,3},{10,11,12,13},{20,21,22,23}};
int (*lp)[4],i,j;
for (lp=a,i=0; i<3; i++)
{ for (j=0; j<4; j++)
printf("%4d",*(*(lp+i)+j));
printf("\n");
}
}
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
0
10
20
1
11
21
2
12
22
3
13
23
alp
lp+1
lp+2
0 1 2 3
10 11 12 13
20 21 22 23
2009-7-29
24
【 例 8.6】 用指向一维数组的指针变量输出数组元素 。
main( )
{ int a[ ][4]={0,1,2,3,10,11,12,13,20,21,22,23};
int (*lp)[4],j;
for (lp=a; lp<a+3; lp++)
{ for (j=0; j<4; j++)
printf("%4d",*(*lp+j));
printf("\n");
}
}
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
0
10
20
1
11
21
2
12
22
3
13
23
alp
lp
lp
lp
0 1 2 3
10 11 12 13
20 21 22 23
2009-7-29
25
【 例 8.7】 通过初始化使指针指向一个字符串 。
main( )
{ char str1[ ]="Good morning!";
char *str2="Good night!";
printf("%s\n",str1);
printf("%s\n",str2);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
1,指向字符串的指针变量的定义及初始化字符串的两种表示方式:
字符数组表示方式,字符串存放在一维数组中,
引用时用数组名。
字符指针变量表示方式,字符指针变量存放字符串的首地址,引用时用指针变量名。
Good morning!
Good night!
2009-7-29
26
【 例 8.8】 通过赋值运算使字符指针变量指向一个字符串 。
#include<string.h>
main( )
{ char c[80],*str;
strcpy(c,"How are you?");
str="Fine,thanks.";
printf("%s\n%s\n",c,str);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
2,字符串的赋值运算
将一个字符串赋给一个字符数组只能使用 strcpy函数
将字符串常量的首地址赋给指针变量,可使用赋值运算符,=”
How are you?
Fine,thanks.
2009-7-29
27
【 例 8.9】 利用指针变量输入输出字符串 。
#include<stdio.h>
main( )
{ char c[80],*str;
str=c;
gets(str);
puts(str);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
3,字符串的输入输出
使用字符串输入输出函数 gets和 puts;
在 scanf和 printf函数中使用 %s格式实现。
输入字符串时,函数参数:数组名、存有数组名的指针变量;
输出字符串时,函数参数:数组名、存有字符串首地址的指针变量。
I love China!
I love China!
2009-7-29
28
【 例 8.11】已知字符串 str,从中截取一子串。要求该子串是从 str的第 m个字符开始,由 n个字符组成。
思路:
定义字符数组 c 存放子串,字符指针变量 p 用于复制子串,利用循环语句从字符串 str截取 n个字符。
考虑到几种特殊情况:
① m位置后的字符数有可能不足 n个,所以在循环读取字符时,
若读到 ‘ \0’ 停止截取,利用 break语句跳出循环。
② 输入的截取位置 m大于字符串的长度,则子串为空。
③ 要求输入的截取位置和字符个数均大于 0,否则子串为空。
8.2 指针与数组
8.2.2 指向字符串的指针变量
2009-7-29
29
main( )
{ char c[80],*p,*str="This is a string.";
int i,m,n;
printf("m,n=");
scanf("%d,%d",&m,&n);
if (m>strlen(str) || n<=0 || m<=0)
printf("NULL\n");
else
{ for (p=str+m-1,i=0; i<n; i++)
if(*p) c[i]=*p++;
else break; /* 如读取到 '\0' 则停止循环 */
c[i]='\0'; /* 在 c数组中加上子串结束标志 */
printf("%s\n",c);
}
}
8.2 指针与数组从 m位置读取 n个字符送到 c数组要求位置 m和长度 n大于 0
2009-7-29
30
指针数组定义的一般形式:
数据类型标识符 *数组名 [ 元素个数 ] ;
在这个定义中由于,[ ],比,*” 的优先级高,所以数组名先与,[ 元素个数 ],结合,形成数组的定义形式,,*” 表示数组中每个元素是指针类型,,数据类型标识符,说明指针的目标变量的数据类型 。 例如:
int *ip[10];
char *cp[5];
8.2 指针与数组
8.2.3 指针数组
1,指针数组的定义
指针数组就是数组中的每个元素均为指针类型
2009-7-29
31
例如:
char
c[4][8]={"Fortran","COBOL","BASIC","Pascal"};
char *cp[4]={c[0],c[1],c[2],c[3]};
char *str[5]={"int","long","char","float","double"};
int a[2][3];
int *p[2]={a[0],a[1]};
8.2 指针与数组
8.2.3 指针数组
2,指针数组初始化
cp C 数 组
cp[0] F o r t r a n \0
cp[1] C O B O L \0
cp[2] B A S I C \0
cp[3] P a s c a l \0
例如:
c ar
c[4][8]={ ortra,,I,ascal };
c ar *c [4]={c[0],c[1],c[2],c[3]};
c r str[ ]={ i t,l,c r,fl t,le };
i t a[ ][ ];
i t [ ]={ [ ],[ ]};
str[0] i n t \0
str 1 l o n g \0
str 2 c h a r \0
str[3] f l o a t \0
str 4 d o u b l e \0
2009-7-29
32
【 例 8.13】 用 0~ 6分别代表星期日至星期六,当输入其中任意一个数字时,输出相应英文单词 。
main( )
{ char *weekname[7]={"Sunday","Monday","Tuesday",
"Wednesday","Thursday","Friday","Saturday"};
int week;
printf("Enter week No.,");
scanf("%d",&week);
if (week>=0 && week<7)
printf("week No.%d —— %s\n",week,weekname[week]);
}
8.2 指针与数组
8.2.3 指针数组
3,利用字符指针数组处理多个字符串
利用字符指针数组处理长度不等的字符串,可节省存储空间。
2009-7-29
338.2 指针与数组
8.2.4 多级指针
1,二级指针变量的定义整型值整型变量整型变量地址一级指针变量一级指针变量地址二级指针变量如果一个指针的目标变量是一个指针类型变量,则此指针为 指向指针 的指针变量,也称为多级指针变量。
二级指针变量定义的一般形式:
数据类型标识符 **指针变量名 ;
其中,**指针变量名,相当于 *(*指针变量名 ),在括号中定义了一个指针变量,括号外的,*”,说明指针变量 ( 即 二级指针 ) 的目标变量是一个指针类型数据,
,数据类型标识符,是目标变量 ( 即 一级指针 ) 所指向的数据的类型,也就是最终目标变量的类型 。 例如:
int a,*p,**pp;
2009-7-29
348.2 指针与数组
8.2.4 多级指针
2,二级指针变量初始化
a的内容
a
&a
p
&p
pp
例如:
int a,*p=&a,**pp=&p;
,*p,**pp,*pp
2009-7-29
358.2 指针与数组
8.2.4 多级指针
【 例 8.16】 利用二级指针输出字符串 。
main( )
{ static char *name[ ]={"Zhang","Wang","Li",""};
char **pp=name;
while (**pp!='\0')
printf("%s\n",*pp++);
} name
Z h a n g \0
W a n g \0
L i \0
\0
pp
pp
pp
pp
*(*pp+1)=h,*(*p+2)=a
Zhang
Wang
Li
2009-7-29
368.3 指针与函数
8.3.1 指针变量作函数参数
1,变量的指针作函数参数【 例 8.17】 输入 3个整数,按从小到大顺序输出 。
void swap(int *x1,int *x2)
{ int t;
t=*x1;*x1=*x2;*x2=t;
return;
}
void main( )
{ int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if (a>b) swap(&a,&b);
if (a>c) swap(&a,&c);
if (b>c) swap(&b,&c);
printf("%d,%d,%d\n",a,b,c);
}
7 5 9
5,7,9
7
a
5
b
9
c
&a
x1
&b
x2主调函数与被调函数之间数据传递的方法:① 实参与形参之间的数据传递;
② 被调函数通过 return语句把函数值返回到主调函数;
③ 通过全局变量交换数据;
④ 利用指针型参数在主调函数和被调函数之间传递数据。
7
t
75
形参 x1和 x2得到 main函数中 a
和 b的地址,这样 x1和 x2的目标变量就是 main函数的变量 a和 b。
在 swap函数中交换 *x1和 *x2的内容,就是交换 a和 b的内容,所以当函数调用结束后,尽管 x1和
x2已经释放,但操作结果仍保留在 main函数的变量 a和 b中,这就是 通过指针形参指向域扩展到主调函数的方法,达到主调函数与被调函数间交换多个数据的目的 。
2009-7-29
378.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑴ 一维数组的指针作函数参数例如被调函数 abc的定义形式有以下三种:
void abc(int b[10]) { …… }
void abc(int b[ ]) { …… }
void abc(int *b) { …… }
在主调函数有如下定义:
int a[10],*p=a;
则调用 abc函数时可用
abc(a) 或 abc(p)
当函数之间需要传递数组时,可以通过传递数组的首地址(即通过指针参数指向域的扩展),完成存取主调函数中数组元素的操作。
2009-7-29
388.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
【 例 8.20】 字符串复制函数 。
void copystr(char *t,char *s)
/* 字符指针变量 t和 s作形参 */
{ while ((*t++=*s++)!='\0'); }
main( )
{ char c[80],*p="I am a student.";
copystr(c,p); /* 数组名 c和字符指针变量 p作实参 */
printf("%s\n",c);
}
2009-7-29
398.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑵ 二维数组的指针作函数参数由于指向二维数组的指针分为行指针和元素指针,所以他们作为函数参数的形式也不同。
① 二维数组的行指针作函数参数例如被调函数 abc的定义形式有以下三种:
void abc(int b[2][4]) { …… }
void abc(int b[ ][4]) { …… }
void abc(int (*b)[4]) { …… }
在主调函数有如下定义:
int a[2][4],(*p)[4]=a;
则调用 abc函数时可用
abc(a) 或 abc(p)
2009-7-29
40
【 例 8.22】 用函数输入输出整型二维数组,函数的形参为行指针 。
void inarr(int (*p1)[4],int m)
/* 可改写为 void inarr(int p1[ ][4],int m) */
{ int i,j;
for (i=0; i<m; i++)
for (j=0; j<4; j++)
scanf("%d",*(p1+i)+j);
}
void outarr(int (*p2)[4],int m)
/* 可改写为 void outarr(int p2[3][4],int m) */
{ int i,j;
for (i=0; i<m; i++)
{ for (j=0; j<4; j++)
printf("%6d",p2[i][j]);
printf("\n");
}
}
main( )
{ int a[3][4],(*p)[4];
p=a;
inarr(a,3);
outa r(p,3);
}
8.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
2009-7-29
418.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑵ 二维数组的指针作函数参数
② 指向二维数组元素的指针作函数参数指向二维数组元素的指针作函数参数时,是利用二维数组元素按行连续存储的的特点,访问数组的每一个元素。 被调函数的形参必须定义为指向二维数组元素的指针变量。
例如被调函数 abc的定义形式如下:
void abc(int *b) { …… }
在主调函数有如下定义:
int a[2][4],*p=a[0];
则调用 abc函数时可用
abc(a[0]),abc(&a[0][0]) 或 abc(p)
2009-7-29
42
【 例 8.23】 用函数输入输出整型二维数组,函数的形参为指向二维数组元素的指针 。
void inarr(int *p1,int m,int n)
{ int i,j;
for (i=0; i<m; i++)
for (j=0; j<n; j++)
scanf("%d",p1+n*i+j);
}
8.3 指针与函数
8.3.1 指针变量作函数参数
void outarr(int *p1,int m,int n)
{ int i,j;
for (i=0; i<m; i++)
{ for (j=0; j<n; j++)
printf("%6d",*(p1+n*i+j));
printf("\n");
}
}
2,数组的指针作函数参数
main( )
{ int a[3][4],*p;
p=&a[0][0];
inarr(a[0],3,4);
outarr(p,3,4);
}
2009-7-29
438.3 指针与函数
8.3.2 带参数的主函数
1,带参数的主函数的定义
main(int argc,char *argv[ ])
{
……
}? main函数只能有两个形参,并且这两个形参的类型也是固定的。第一个形参必须是整型变量,
第二个形参可以定义为字符型指针数组,也可以定义为二级字符指针变量,因此也可以写成
main(int argc,char **argv)
2009-7-29
448.3 指针与函数
8.3.2 带参数的主函数
2,带参数的主函数的调用带参数的主函数调用形式,
可执行文件名 参数 1 参数 2 …… 参数 n
在 DOS系统提示符下键入的这一行字符称为 命令行 。 可执行文件名称为 命令名,其后的参数称为 命令行参数,命令名与各参数之间用空格进行分隔 。
要调用带参数的主函数 必须在操作系统环境下进行。假设 C语言源程序文件 file1.c,经过编译、
连接生成一个可执行文件 file1.exe。 在 DOS系统提示符后键入 可执行文件名 file1,即可执行该程序 。
2009-7-29
458.3 指针与函数
8.3.2 带参数的主函数
3,主函数参数的作用
argc 称作参数计数器,它的值是包括命令名在内的参数个数 。
argv 指针数组的作用是存放命令行中命令名和每个参数字符串的首地址 。
C:\>file1 one two three
argv[0] f i l e 1 \0
argv[1] o n e \0
argv[2] t w o \0
argv[3] t h r e e \0
argv
argc 4
2009-7-29
46
C:\>
8.3 指针与函数
8.3.2 带参数的主函数
【 例 8.24】 显示命令行参数程序 。
void main(int argc,char *argv[ ])
{ int i=0;
while (--argc>0)
printf("%s\n",argv[++i]);
}
file1 one two three
one
two
three
注意,命令行参数所传送的数据全部都是字符串。即便传送的是数值,也是按字符串方式传送给主函数。程序中使用这些参数时,还需要将数字字符串转换成数值型数据。 C语言标准库函数提供了一些相关的数据类型转换函数 。
2009-7-29
478.3 指针与函数
8.3.3 指针型函数
1,指针型函数的定义指针型函数定义的一般形式:
数据类型标识符 *函数名 ( 形式参数表 )
{
……
}
其中函数名前的,*” 表示 函数的返回值 是一个指针类型,,数据类型标识符,是指针所指向的目标变量的类型 。
如果一个函数的返回值是指针,则称此函数为指针型函数。
2009-7-29
48
char *subcut(char *a,int s,int len)
{ static char substr[SIZE+1]; /* substr用于存放子串 */
int n;
char *ps;
ps=substr;
if(s<1 || s>strlen(a) || len<1)
printf("data error\n"); /* 数据错,子串为空 */
else
for (n=1,a+=s-1; n<=len && *a!= '\0'; n++)
*ps++=*a++;
*ps='\0'; /* 复制结束加入字符串结束标志 */
return(substr);
}
8.3 指针与函数
8.3.3 指针型函数
【 例 8.26】 编制一个函数,其功能是从已知字符串中指定位置 s开始截取一个长度为 len的子串 。
思路:
在截取子串函数 sutcut中需要从主调函数传送 3个数据,
源字符串,截取位置 s,长度 len。 在形参中定义一个 指针变量 a接收源字符串的首地址,在函数中再定义一个存储类型为 static的 字符数组 substr用于存放子串,指针变量 ps
指向 substr数组 。
在截取子串的过程中,如果起始位置 s小于 1或 大于 源串则 子串为空,如果截取 长度 len小于 1子串也为空 ;否则循环复制子串,直到复制了 len个字符或从源串读到 '\0' 结束 。
最后函数将返回 substr字符数组的首地址 。
若起始位置 s小于 1或大于源串或截取长度 len小于 1
从第 s个字符开始,复制 len个字符
2009-7-29
498.3 指针与函数
8.3.3 指针型函数
2,指针型函数定义时应注意的问题
① 指针函数中 return的返回值 必须 是与函数 类型一致 的指针 。
② 返回值必须是外部或静态存储类别的变量指针或数组指针,以保证主调函数能正确使用数据 。
2009-7-29
508.3 指针与函数
8.3.4 指向函数的指针
1,函数指针变量的定义函数指针变量定义的一般形式:
数据类型标识符 (*函数指针变量名 )( );
其中,*函数指针变量名,必须用圆括号括起来,否则就成为声明一个指针型函数了 。 在定义中,(*函数指针变量名 )” 右侧的括号,( )” 表示指针变量所 指向的目标是一个函数,,数据类型标识符,是定义指针变量所指向的函数的类型 。 例如:
int (*p)( ); /*p是一个指向整型函数的指针变量 */
float (*q)( ); /*q是 一个指向单精度实型函数的指针变量 */
在 C语言中,函数名具有与数组名类似的特性,数组名代表数组的首地址,函数名代表函数的起始地址 (即该函数的程序代码段在内存中所占用的存储空间的首地址,也称函数入口)。因此也可以 把函数名赋给一个函数指针变量,使其成为 指向该函数的指针变量 。 函数名 则可以看成 是函数指针常量 。
2009-7-29
518.3 指针与函数
8.3.4 指向函数的指针
2,用函数指针变量调用函数用函数指针变量调用函数的一般形式:
(*函数指针变量名 )(实参表 );
由于优先级不同,*函数指针变量名,必须用圆括号括起来,表示 间接调用 指针变量所指向的 函数 ;右侧括号中为传递到被调函数的实参 。
函数指针 与 变量指针 的共同之处是都可以做间接访问。 变量指针 指向内存的数据存储区,通过间接存取运算访问目标变量; 函数指针 指向内存的程序代码存储区,通过间接存取运算 使程序流程转移到指针所指向的函数入口,取出函数的机器指令并执行函数,完成函数的调用。
2009-7-29
528.3 指针与函数
8.3.4 指向函数的指针
main()
{int max(),a,b,c; /*声明被调用的目标函数 max */
int (*p)(); /*定义 p为指向整型函数的指针变量 */
p=max; /* 用指针变量存储函数入口地址 */
scanf(“%d%d”,&a,&b);
c=(*p)(a,b); /* 用指针变量调用函数 */
printf(“max=%d”,c);
}
max(int x,int y) /* 函数名是函数的入口地址 */
{if(x>y) return(x);
else return(y);
}
由于优先级的问题,
()是必须的目标函数必须要事先声明,
即使是整型函数也要声明
(*p)中 ( )是必须的,实参表应与函数的形参表一一对应
2009-7-29
53
将 函数指针 作为参数,可在主调函数和被调函数之间将第三个函数作为参数传递,实现在被函数中调用不同函数的目的,使被调函数成为通用函数。
函数指针 作函数参数时,形参 一定要定义为指向函数的指针变量,实参 则可以是函数名或指向函数的指针变量。
【 例 8.28】 编制一个用弦截法求方程根的函数,求解下列方程 。
x3-5x2+6x-30=0
2x3-4x2+3x-6=0 Y f(x)(x2,f(x2))
0 x1 x x0 x2
X
(x1,f(x1))
图 8.21 弦截法求方程的根示意图
8.3 指针与函数
8.3.4 指向函数的指针
3,函数指针作函数的参数
)1()2(
)1(2)2(1
xfxf
xfxxfxx
由直线方程推出 公式:
2009-7-29
548.3 指针与函数
float root(float (*fun)(float))
{ float x,x1,x2,y,y1,y2;
do /*选定一个单调变化的区间 */
{ ……
}while (y1*y2>=0);
do
{ x=(x1*y2-x2*y1)/(y2-y1);
y=(*fun)(x);
if (y*y1<0)
{x2=x; y2=y;}
else
{x1=x; y1=y;}
}while (fabs(y)>=0.0001);
return x;
}
选定一个单调变化的区间 [x1,x2]
按公式计算 x=(x1*y2-x2*y1)/(y2-y1)
y=f(x)
y*y1<0
T F
x2=x x1=x
y2=y y1=y
while(|y|>=1e-4)
x为方程的根图 8.22 弦截法求方程的根 N-S图
Y
f(x)(x2,f(x2))
0 x1 x x0 x2
X
(x1,f(x1))
图 8.21 弦截法求方程的根示意图被积函数如何解决 root函数适用于不同的被积函数?
通过指向函数的指针变量调用函数
f(x);
不可用固定函数名
2009-7-29
558.3 指针与函数
#include <math.h>
void main( )
{ float f1(float),f2(float),root(float (*)(float));
float x1,x2;
x1=root(f1); /* 函数名 f1作实参 */
printf("A root of the first equation:%8.4f\n",x1);
x2=root(f2); /* 函数名 f2作实参 */
printf("A root of the second equation:%8.4f\n",x2);
}
float f1(float x)
{ return(x*x*x-5*x*x+6*x-30); }
float f2(float x)
{ return(2*x*x*x-4*x*x+3*x-6); }
对被调函数的声明
第 8章 指 针
C 语言程序设计北京科技大学 计算机系
2009-7-29
2
8.1 指针和指针变量
8.2 指针与数组
8.3 指针与函数第 8章 指针
2009-7-29
38.1 指针和指针变量
main()
{float x;
int y;
…
}
8.1.1 指针和指针变量的概念变量的两个物理意义 2000
2001
2002
2003
2004
2005
…
x
y
变量的内容变量的地址
2009-7-29
48.1 指针和指针变量
main()
{int a,b,c;
a=5;
b=3;
c=a+b;
…
}
8.1.1 指针和指针变量的概念
2000
2001
2002
2003
2004
2005
a
b
c
5
直接将整数 5存入变量 a 3
直接将整数 3存入变量 b 8
直接 将变量 a,b的值取出,相加后存入变量 c
利用变量名存取数据的方式称为,直接存取
”方式。
2009-7-29
5
C 语言还提供了对内存单元的
间接存取?
方式
p
2000
2001
2002
2003
2004
2005
2006
2007
a
b
c
5
3
2004
a和 b相加的结果 (*p=a+b)存放到变量 p所指向的 内存单元 中去。
此时 c称为指针变量 p的 目标变量 。
p
2004
c
p=&c
8
main()
{int a=5,b=3,c,*p;
p = &c;
*p = a + b;
…
}
8.1 指针和指针变量
8.1.1 指针和指针变量的概念
● 变量的地址称为变量的指针
● 存放地址的变量称为指针变量
2009-7-29
6
main()
{ int a,*p=&a;
float x,*q=&x;
…
}
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
a
p
x
q
2004
8.1 指针和指针变量
8.1.2 指针变量的定义
2000main()
{int a,*p;
float x,*q;
p = &a;
q = &x;
…
}
指针变量定义的形式 指针变量初始化
2009-7-29
7
main()
{ int a,*p=&a;
float x,*q=&x;
*p = 5;
printf("a+3=%d\n",*p+3);
…
}
间接存取运算
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
a
p
x
q
2004
8.1 指针和指针变量
8.1.3 指针的基本运算
2000
*p = &a;×
main()
{int a,*p;
float x,*q;
p = &a;
q = &x;
…
}
赋值运算
5
2009-7-29
8
方法 1:目标变量值不变,改变指针变量的指向求解。
main( )
{ int a,b,*p1,*p2,*p;
p1=&a;p2=&b;
scanf(″ %d%d″,p1,p2);
if(*p1<*p2)
{ p=p1; p1=p2; p2=p;}
printf(″ a=%d,b=%d\n″,a,b);
printf(″ max=%d,min=%d\n″,*p1,*p2);
}
a b
p1 p2
p
6 8
a=6,b=8
max=8,min=6
8.1 指针和指针变量
【例 8.1】输入 a,b两个整数,
使用指针变量按大小顺序输出这两个整数。
2009-7-29
9
方法 2:利用指针变量直接改变目标变量的值求解。
main()
{ int a,b,t,*p1,*p2;
p1=&a;p2=&b;
scanf(″ %d%d″,p1,p2);
if(*p1<*p2)
{ t=*p1; *p1=*p2; *p2=t;}
printf(″ a=%d,b=%d\n″,a,b);
printf(″ max=%d,min=%d\n″,*p1,*p2);
}
a,*p1 b,*p2
p1 p2
t
6 8
a=8,b=6
max=8,min=6
8.1 指针和指针变量
6
8 6
2009-7-29
10
指针的加减运算
8.1 指针和指针变量
8.1.3 指针的基本运算
main()
{int a[]={10,20,30,40,50},*p1,*p2;
p1=p2=a;
printf(″p1 =%u,*p1=%d\n″,p1,*p1);
p2+=3;
printf(″p2 =%u,*p2=%d\n″,p2,*p2);
}
P1=404,*p1=10
P2=410,*p2=40
404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2
指针加减运算要点:
① 只有当指针变量指向数组时指针的加减运算才有意义。
② 指针变量可加减一个整型表达式。如:
p1++,p2+3,p2--,p2-2。
③ 指针的加减运算是以基类型为单位 (即
sizeof( 类型) )的 。
④ 两个指针变量不能作加法运算,只有当两个指针变量指向同一数组时,进行指针变量相减才有实际意义。如,p2-p1。
2009-7-29
11
指针的关系运算
8.1 指针和指针变量
8.1.3 指针的基本运算 404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2
① 指向同一数组的两个指针可以进行关系运算,表明它们所指向元素的相互位置关系 。
如,p2 > p1,p2 == p1。
② 指针与一个整型数据进行比较是没有意义的 。
③ 不同类型指针变量之间比较是非法的 。
④ NULL可以与任何类型指针进行 ==,!=
的关系运算,用于判断指针是否为空指针 。
2009-7-29
12
⑴ 数组名是该数组的指针
a是 数组的首地址(即 a[0]的地址),是一个指针常量。
a = &a[0],a+1 = &a[1],…,a+9 = &a[9]
数组元素的下标表示法:
a[0],a[1],…,a[i],…,a[9]
数组元素的指针表示法:
*(a+0),*(a+1),…,*(a+i),…,*(a+9)
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针例如,int a[10],*p;
a[0]
a[9]
p
a
2009-7-29
13
当 p指向 a[0]时,用 p表示数组元素下标法:
p[0],p[1],…,p[i],…,p[9]
指针法:
*(p+0),*(p+1),…,*(p+i),…,*(p+9)
a[0]
a[9]
p
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针
⑵ 指向一维数组元素的指针变量由于数组元素也是一个内存变量,所以此类指针变量的定义和使用与指向变量的指针变量相同。例如,int a[10],*p;
p = a;(或 p = &a[0];)
a
2009-7-29
14
a[0]
a[9]
p
a
【例 8.3】用指针法输出数组元素。
main( )
{ int a[10],i,*p;
for (i=0; i<10; i++)
scanf("%d",a+i);
for (i=0; i<10; i++)
printf("%4d ",*(a+i));
printf("\n");
for (p=a,i=0; i<10; i++)
printf("%4d ",*(p+i));
printf("\n");
}
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针 1
2
3
4
5
6
7
8
9
10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
2009-7-29
15
a[0]
a[9]
p
a
【例 8.3】用指针法输出数组元素。
main( )
{ int a[10],i,*p;
for (p=a; p<a+10; p++)
scanf("%d",p);
for (p=a; p<a+10; p++)
printf("%d ",*p);
printf("\n");
}
8.2 指针与数组
8.2.1 指向数组的指针
1,一维数组的指针 1
2
3
4
5
6
7
8
9
10
2009-7-29
16
a[0]
a[9]
p
a[1]
.
.
.
.
.
.
1
2
3
4
5
6
7
8
9
10
a
数组名 是地址,指向数组的 指针变量 存放的也是地址。
通过指针变量也可以引用数组元素。 p=&a[0] 等效于 p=a。数组名 和指向数组的 指针变量 的区别:
指针变量 p是 变量 可以赋值,
数组名 a是地址 常量 不能赋值。
8.2 指针与数组
8.2.1 指向数组的指针用指针变量引用数组元素,
必须关注其当前值。例如:
p = p + 3
*(p-1),p[-1]等价于 a[2]
2009-7-29
17
【 例 8.4】输入五个整数,使用指针变量将这五个数按从小到大排序后输出。
main( )
{ int a[5],*pp,*p,*q,t;
for (p=a; p<a+5;p++)
scanf("%d",p);
for (p=a; p<a+4; p++)
{ pp=p;
for (q=p+1; q<a+5; q++)
if (*pp>*q) pp=q;
if (pp!=p)
{ t=*p;*p=*pp;*pp=t; }
}
for (p=a; p<a+5; p++)
printf("%d ",*p);
printf("\n");
}
8.2 指针与数组输入 a数组
for (p=a; p<a+4; p++)
pp=p
for (q=p+1; q<a+5; q++)
T *pp>*q F
pp=q
T pp!=p F
*pp?*p
输出 a数组图 8.9 选择法排序 N-S图
2009-7-29
18
0
10
20
1
11
21
2
12
22
3
13
23
a
⑴ 二维数组的地址例如,int a[3][4];
① 二维数组名 a是数组的 首地址 。
② 二维数组 a包含三个行元素,a[0],a[1],a[2] 。
a[0]
a[1]
a[2]
a
三个行元素的地址分别是,a,a+1,a+2。
而 a[0],a[1],a[2]也是地址量,是一维数组名,
即 *(a+0),*(a+1),*(a+2)是一维数组首个元素地址。
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a+1
a+2
2009-7-29
19
⑵ 二维数组元素的地址
a[0],a[1],a[2]是一维数组名,
所以 a[i]+j是 数组元素的地址。
数组元素 a[i][j]的地址可以表示为下列形式:
&a[i][j],a[i]+j,*(a+i)+j
0
10
20
1
11
21
2
12
22
3
13
23
a[0]数组
a[1]数组
a[2]数组
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a[2]+1 a[2]+2 a[2]+3
a[0]+1 a[0]+2 a[0]+3
2009-7-29
20
⑶ 二维数组元素的表示法数组元素可用下列形式表示:
a[i][j],*(a[i]+j),*(*(a+i)+j)
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
a是 二 维数组,根据 C的地址计算方法,a经过两次 *操作才能访问到数组元素。所以
*a 是 a[0],**a 才 是 a[0][0]。
a[0]是 a[0][0]的地址,*a[0]是 a[0][0]。
0
10
20
1
11
21
2
12
22
3
13
23
a[0]
a[1]
a[2]
a
2009-7-29
21
⑷ 指向二维数组元素的指针变量
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
【例 8.5】用指向数组元素的指针变量输出数组元素。
main( )
{ int a[3][4]={{0,1,2,3},{10,11,12,13},{20,21,22,23}},i,j,*p;
for (p=a[0],i=0; i< 3; i++)
{ for (j=0; j< 4; j++)
printf("%4d",*(p+i*4+j)); /* 元素的相对位置为 i*4+j */
printf("\n");
}
}
这种指针变量的定义及使用与指向一维数组元素的指针变量是相同的,用它存放二维数组元素的地址。
0
10
20
1
11
21
2
12
22
3
13
23
ap
2009-7-29
22
⑸ 指向一维数组的指针变量指向一维数组指针变量的定义形式:
数据类型标识符 (*变量名 )[元素个数 ]
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
*?表示其后的 变量名 为指针类型,[ 元素个数 ] 表示目标变量是一维数组,并说明一维数组元素的个数。由于
*?比? [ ]?的运算级别低,? *变量名?作为一个说明部分,两边必须加括号。数据类型标识符?是定义一维数组元素的类型。
2009-7-29
23
【 例 8.6】 用指向一维数组的指针变量输出数组元素 。
main( )
{ int a[3][4]={{0,1,2,3},{10,11,12,13},{20,21,22,23}};
int (*lp)[4],i,j;
for (lp=a,i=0; i<3; i++)
{ for (j=0; j<4; j++)
printf("%4d",*(*(lp+i)+j));
printf("\n");
}
}
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
0
10
20
1
11
21
2
12
22
3
13
23
alp
lp+1
lp+2
0 1 2 3
10 11 12 13
20 21 22 23
2009-7-29
24
【 例 8.6】 用指向一维数组的指针变量输出数组元素 。
main( )
{ int a[ ][4]={0,1,2,3,10,11,12,13,20,21,22,23};
int (*lp)[4],j;
for (lp=a; lp<a+3; lp++)
{ for (j=0; j<4; j++)
printf("%4d",*(*lp+j));
printf("\n");
}
}
8.2 指针与数组
8.2.1 指向数组的指针
2,二维数组的指针
0
10
20
1
11
21
2
12
22
3
13
23
alp
lp
lp
lp
0 1 2 3
10 11 12 13
20 21 22 23
2009-7-29
25
【 例 8.7】 通过初始化使指针指向一个字符串 。
main( )
{ char str1[ ]="Good morning!";
char *str2="Good night!";
printf("%s\n",str1);
printf("%s\n",str2);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
1,指向字符串的指针变量的定义及初始化字符串的两种表示方式:
字符数组表示方式,字符串存放在一维数组中,
引用时用数组名。
字符指针变量表示方式,字符指针变量存放字符串的首地址,引用时用指针变量名。
Good morning!
Good night!
2009-7-29
26
【 例 8.8】 通过赋值运算使字符指针变量指向一个字符串 。
#include<string.h>
main( )
{ char c[80],*str;
strcpy(c,"How are you?");
str="Fine,thanks.";
printf("%s\n%s\n",c,str);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
2,字符串的赋值运算
将一个字符串赋给一个字符数组只能使用 strcpy函数
将字符串常量的首地址赋给指针变量,可使用赋值运算符,=”
How are you?
Fine,thanks.
2009-7-29
27
【 例 8.9】 利用指针变量输入输出字符串 。
#include<stdio.h>
main( )
{ char c[80],*str;
str=c;
gets(str);
puts(str);
}
8.2 指针与数组
8.2.2 指向字符串的指针变量
3,字符串的输入输出
使用字符串输入输出函数 gets和 puts;
在 scanf和 printf函数中使用 %s格式实现。
输入字符串时,函数参数:数组名、存有数组名的指针变量;
输出字符串时,函数参数:数组名、存有字符串首地址的指针变量。
I love China!
I love China!
2009-7-29
28
【 例 8.11】已知字符串 str,从中截取一子串。要求该子串是从 str的第 m个字符开始,由 n个字符组成。
思路:
定义字符数组 c 存放子串,字符指针变量 p 用于复制子串,利用循环语句从字符串 str截取 n个字符。
考虑到几种特殊情况:
① m位置后的字符数有可能不足 n个,所以在循环读取字符时,
若读到 ‘ \0’ 停止截取,利用 break语句跳出循环。
② 输入的截取位置 m大于字符串的长度,则子串为空。
③ 要求输入的截取位置和字符个数均大于 0,否则子串为空。
8.2 指针与数组
8.2.2 指向字符串的指针变量
2009-7-29
29
main( )
{ char c[80],*p,*str="This is a string.";
int i,m,n;
printf("m,n=");
scanf("%d,%d",&m,&n);
if (m>strlen(str) || n<=0 || m<=0)
printf("NULL\n");
else
{ for (p=str+m-1,i=0; i<n; i++)
if(*p) c[i]=*p++;
else break; /* 如读取到 '\0' 则停止循环 */
c[i]='\0'; /* 在 c数组中加上子串结束标志 */
printf("%s\n",c);
}
}
8.2 指针与数组从 m位置读取 n个字符送到 c数组要求位置 m和长度 n大于 0
2009-7-29
30
指针数组定义的一般形式:
数据类型标识符 *数组名 [ 元素个数 ] ;
在这个定义中由于,[ ],比,*” 的优先级高,所以数组名先与,[ 元素个数 ],结合,形成数组的定义形式,,*” 表示数组中每个元素是指针类型,,数据类型标识符,说明指针的目标变量的数据类型 。 例如:
int *ip[10];
char *cp[5];
8.2 指针与数组
8.2.3 指针数组
1,指针数组的定义
指针数组就是数组中的每个元素均为指针类型
2009-7-29
31
例如:
char
c[4][8]={"Fortran","COBOL","BASIC","Pascal"};
char *cp[4]={c[0],c[1],c[2],c[3]};
char *str[5]={"int","long","char","float","double"};
int a[2][3];
int *p[2]={a[0],a[1]};
8.2 指针与数组
8.2.3 指针数组
2,指针数组初始化
cp C 数 组
cp[0] F o r t r a n \0
cp[1] C O B O L \0
cp[2] B A S I C \0
cp[3] P a s c a l \0
例如:
c ar
c[4][8]={ ortra,,I,ascal };
c ar *c [4]={c[0],c[1],c[2],c[3]};
c r str[ ]={ i t,l,c r,fl t,le };
i t a[ ][ ];
i t [ ]={ [ ],[ ]};
str[0] i n t \0
str 1 l o n g \0
str 2 c h a r \0
str[3] f l o a t \0
str 4 d o u b l e \0
2009-7-29
32
【 例 8.13】 用 0~ 6分别代表星期日至星期六,当输入其中任意一个数字时,输出相应英文单词 。
main( )
{ char *weekname[7]={"Sunday","Monday","Tuesday",
"Wednesday","Thursday","Friday","Saturday"};
int week;
printf("Enter week No.,");
scanf("%d",&week);
if (week>=0 && week<7)
printf("week No.%d —— %s\n",week,weekname[week]);
}
8.2 指针与数组
8.2.3 指针数组
3,利用字符指针数组处理多个字符串
利用字符指针数组处理长度不等的字符串,可节省存储空间。
2009-7-29
338.2 指针与数组
8.2.4 多级指针
1,二级指针变量的定义整型值整型变量整型变量地址一级指针变量一级指针变量地址二级指针变量如果一个指针的目标变量是一个指针类型变量,则此指针为 指向指针 的指针变量,也称为多级指针变量。
二级指针变量定义的一般形式:
数据类型标识符 **指针变量名 ;
其中,**指针变量名,相当于 *(*指针变量名 ),在括号中定义了一个指针变量,括号外的,*”,说明指针变量 ( 即 二级指针 ) 的目标变量是一个指针类型数据,
,数据类型标识符,是目标变量 ( 即 一级指针 ) 所指向的数据的类型,也就是最终目标变量的类型 。 例如:
int a,*p,**pp;
2009-7-29
348.2 指针与数组
8.2.4 多级指针
2,二级指针变量初始化
a的内容
a
&a
p
&p
pp
例如:
int a,*p=&a,**pp=&p;
,*p,**pp,*pp
2009-7-29
358.2 指针与数组
8.2.4 多级指针
【 例 8.16】 利用二级指针输出字符串 。
main( )
{ static char *name[ ]={"Zhang","Wang","Li",""};
char **pp=name;
while (**pp!='\0')
printf("%s\n",*pp++);
} name
Z h a n g \0
W a n g \0
L i \0
\0
pp
pp
pp
pp
*(*pp+1)=h,*(*p+2)=a
Zhang
Wang
Li
2009-7-29
368.3 指针与函数
8.3.1 指针变量作函数参数
1,变量的指针作函数参数【 例 8.17】 输入 3个整数,按从小到大顺序输出 。
void swap(int *x1,int *x2)
{ int t;
t=*x1;*x1=*x2;*x2=t;
return;
}
void main( )
{ int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if (a>b) swap(&a,&b);
if (a>c) swap(&a,&c);
if (b>c) swap(&b,&c);
printf("%d,%d,%d\n",a,b,c);
}
7 5 9
5,7,9
7
a
5
b
9
c
&a
x1
&b
x2主调函数与被调函数之间数据传递的方法:① 实参与形参之间的数据传递;
② 被调函数通过 return语句把函数值返回到主调函数;
③ 通过全局变量交换数据;
④ 利用指针型参数在主调函数和被调函数之间传递数据。
7
t
75
形参 x1和 x2得到 main函数中 a
和 b的地址,这样 x1和 x2的目标变量就是 main函数的变量 a和 b。
在 swap函数中交换 *x1和 *x2的内容,就是交换 a和 b的内容,所以当函数调用结束后,尽管 x1和
x2已经释放,但操作结果仍保留在 main函数的变量 a和 b中,这就是 通过指针形参指向域扩展到主调函数的方法,达到主调函数与被调函数间交换多个数据的目的 。
2009-7-29
378.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑴ 一维数组的指针作函数参数例如被调函数 abc的定义形式有以下三种:
void abc(int b[10]) { …… }
void abc(int b[ ]) { …… }
void abc(int *b) { …… }
在主调函数有如下定义:
int a[10],*p=a;
则调用 abc函数时可用
abc(a) 或 abc(p)
当函数之间需要传递数组时,可以通过传递数组的首地址(即通过指针参数指向域的扩展),完成存取主调函数中数组元素的操作。
2009-7-29
388.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
【 例 8.20】 字符串复制函数 。
void copystr(char *t,char *s)
/* 字符指针变量 t和 s作形参 */
{ while ((*t++=*s++)!='\0'); }
main( )
{ char c[80],*p="I am a student.";
copystr(c,p); /* 数组名 c和字符指针变量 p作实参 */
printf("%s\n",c);
}
2009-7-29
398.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑵ 二维数组的指针作函数参数由于指向二维数组的指针分为行指针和元素指针,所以他们作为函数参数的形式也不同。
① 二维数组的行指针作函数参数例如被调函数 abc的定义形式有以下三种:
void abc(int b[2][4]) { …… }
void abc(int b[ ][4]) { …… }
void abc(int (*b)[4]) { …… }
在主调函数有如下定义:
int a[2][4],(*p)[4]=a;
则调用 abc函数时可用
abc(a) 或 abc(p)
2009-7-29
40
【 例 8.22】 用函数输入输出整型二维数组,函数的形参为行指针 。
void inarr(int (*p1)[4],int m)
/* 可改写为 void inarr(int p1[ ][4],int m) */
{ int i,j;
for (i=0; i<m; i++)
for (j=0; j<4; j++)
scanf("%d",*(p1+i)+j);
}
void outarr(int (*p2)[4],int m)
/* 可改写为 void outarr(int p2[3][4],int m) */
{ int i,j;
for (i=0; i<m; i++)
{ for (j=0; j<4; j++)
printf("%6d",p2[i][j]);
printf("\n");
}
}
main( )
{ int a[3][4],(*p)[4];
p=a;
inarr(a,3);
outa r(p,3);
}
8.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
2009-7-29
418.3 指针与函数
8.3.1 指针变量作函数参数
2,数组的指针作函数参数
⑵ 二维数组的指针作函数参数
② 指向二维数组元素的指针作函数参数指向二维数组元素的指针作函数参数时,是利用二维数组元素按行连续存储的的特点,访问数组的每一个元素。 被调函数的形参必须定义为指向二维数组元素的指针变量。
例如被调函数 abc的定义形式如下:
void abc(int *b) { …… }
在主调函数有如下定义:
int a[2][4],*p=a[0];
则调用 abc函数时可用
abc(a[0]),abc(&a[0][0]) 或 abc(p)
2009-7-29
42
【 例 8.23】 用函数输入输出整型二维数组,函数的形参为指向二维数组元素的指针 。
void inarr(int *p1,int m,int n)
{ int i,j;
for (i=0; i<m; i++)
for (j=0; j<n; j++)
scanf("%d",p1+n*i+j);
}
8.3 指针与函数
8.3.1 指针变量作函数参数
void outarr(int *p1,int m,int n)
{ int i,j;
for (i=0; i<m; i++)
{ for (j=0; j<n; j++)
printf("%6d",*(p1+n*i+j));
printf("\n");
}
}
2,数组的指针作函数参数
main( )
{ int a[3][4],*p;
p=&a[0][0];
inarr(a[0],3,4);
outarr(p,3,4);
}
2009-7-29
438.3 指针与函数
8.3.2 带参数的主函数
1,带参数的主函数的定义
main(int argc,char *argv[ ])
{
……
}? main函数只能有两个形参,并且这两个形参的类型也是固定的。第一个形参必须是整型变量,
第二个形参可以定义为字符型指针数组,也可以定义为二级字符指针变量,因此也可以写成
main(int argc,char **argv)
2009-7-29
448.3 指针与函数
8.3.2 带参数的主函数
2,带参数的主函数的调用带参数的主函数调用形式,
可执行文件名 参数 1 参数 2 …… 参数 n
在 DOS系统提示符下键入的这一行字符称为 命令行 。 可执行文件名称为 命令名,其后的参数称为 命令行参数,命令名与各参数之间用空格进行分隔 。
要调用带参数的主函数 必须在操作系统环境下进行。假设 C语言源程序文件 file1.c,经过编译、
连接生成一个可执行文件 file1.exe。 在 DOS系统提示符后键入 可执行文件名 file1,即可执行该程序 。
2009-7-29
458.3 指针与函数
8.3.2 带参数的主函数
3,主函数参数的作用
argc 称作参数计数器,它的值是包括命令名在内的参数个数 。
argv 指针数组的作用是存放命令行中命令名和每个参数字符串的首地址 。
C:\>file1 one two three
argv[0] f i l e 1 \0
argv[1] o n e \0
argv[2] t w o \0
argv[3] t h r e e \0
argv
argc 4
2009-7-29
46
C:\>
8.3 指针与函数
8.3.2 带参数的主函数
【 例 8.24】 显示命令行参数程序 。
void main(int argc,char *argv[ ])
{ int i=0;
while (--argc>0)
printf("%s\n",argv[++i]);
}
file1 one two three
one
two
three
注意,命令行参数所传送的数据全部都是字符串。即便传送的是数值,也是按字符串方式传送给主函数。程序中使用这些参数时,还需要将数字字符串转换成数值型数据。 C语言标准库函数提供了一些相关的数据类型转换函数 。
2009-7-29
478.3 指针与函数
8.3.3 指针型函数
1,指针型函数的定义指针型函数定义的一般形式:
数据类型标识符 *函数名 ( 形式参数表 )
{
……
}
其中函数名前的,*” 表示 函数的返回值 是一个指针类型,,数据类型标识符,是指针所指向的目标变量的类型 。
如果一个函数的返回值是指针,则称此函数为指针型函数。
2009-7-29
48
char *subcut(char *a,int s,int len)
{ static char substr[SIZE+1]; /* substr用于存放子串 */
int n;
char *ps;
ps=substr;
if(s<1 || s>strlen(a) || len<1)
printf("data error\n"); /* 数据错,子串为空 */
else
for (n=1,a+=s-1; n<=len && *a!= '\0'; n++)
*ps++=*a++;
*ps='\0'; /* 复制结束加入字符串结束标志 */
return(substr);
}
8.3 指针与函数
8.3.3 指针型函数
【 例 8.26】 编制一个函数,其功能是从已知字符串中指定位置 s开始截取一个长度为 len的子串 。
思路:
在截取子串函数 sutcut中需要从主调函数传送 3个数据,
源字符串,截取位置 s,长度 len。 在形参中定义一个 指针变量 a接收源字符串的首地址,在函数中再定义一个存储类型为 static的 字符数组 substr用于存放子串,指针变量 ps
指向 substr数组 。
在截取子串的过程中,如果起始位置 s小于 1或 大于 源串则 子串为空,如果截取 长度 len小于 1子串也为空 ;否则循环复制子串,直到复制了 len个字符或从源串读到 '\0' 结束 。
最后函数将返回 substr字符数组的首地址 。
若起始位置 s小于 1或大于源串或截取长度 len小于 1
从第 s个字符开始,复制 len个字符
2009-7-29
498.3 指针与函数
8.3.3 指针型函数
2,指针型函数定义时应注意的问题
① 指针函数中 return的返回值 必须 是与函数 类型一致 的指针 。
② 返回值必须是外部或静态存储类别的变量指针或数组指针,以保证主调函数能正确使用数据 。
2009-7-29
508.3 指针与函数
8.3.4 指向函数的指针
1,函数指针变量的定义函数指针变量定义的一般形式:
数据类型标识符 (*函数指针变量名 )( );
其中,*函数指针变量名,必须用圆括号括起来,否则就成为声明一个指针型函数了 。 在定义中,(*函数指针变量名 )” 右侧的括号,( )” 表示指针变量所 指向的目标是一个函数,,数据类型标识符,是定义指针变量所指向的函数的类型 。 例如:
int (*p)( ); /*p是一个指向整型函数的指针变量 */
float (*q)( ); /*q是 一个指向单精度实型函数的指针变量 */
在 C语言中,函数名具有与数组名类似的特性,数组名代表数组的首地址,函数名代表函数的起始地址 (即该函数的程序代码段在内存中所占用的存储空间的首地址,也称函数入口)。因此也可以 把函数名赋给一个函数指针变量,使其成为 指向该函数的指针变量 。 函数名 则可以看成 是函数指针常量 。
2009-7-29
518.3 指针与函数
8.3.4 指向函数的指针
2,用函数指针变量调用函数用函数指针变量调用函数的一般形式:
(*函数指针变量名 )(实参表 );
由于优先级不同,*函数指针变量名,必须用圆括号括起来,表示 间接调用 指针变量所指向的 函数 ;右侧括号中为传递到被调函数的实参 。
函数指针 与 变量指针 的共同之处是都可以做间接访问。 变量指针 指向内存的数据存储区,通过间接存取运算访问目标变量; 函数指针 指向内存的程序代码存储区,通过间接存取运算 使程序流程转移到指针所指向的函数入口,取出函数的机器指令并执行函数,完成函数的调用。
2009-7-29
528.3 指针与函数
8.3.4 指向函数的指针
main()
{int max(),a,b,c; /*声明被调用的目标函数 max */
int (*p)(); /*定义 p为指向整型函数的指针变量 */
p=max; /* 用指针变量存储函数入口地址 */
scanf(“%d%d”,&a,&b);
c=(*p)(a,b); /* 用指针变量调用函数 */
printf(“max=%d”,c);
}
max(int x,int y) /* 函数名是函数的入口地址 */
{if(x>y) return(x);
else return(y);
}
由于优先级的问题,
()是必须的目标函数必须要事先声明,
即使是整型函数也要声明
(*p)中 ( )是必须的,实参表应与函数的形参表一一对应
2009-7-29
53
将 函数指针 作为参数,可在主调函数和被调函数之间将第三个函数作为参数传递,实现在被函数中调用不同函数的目的,使被调函数成为通用函数。
函数指针 作函数参数时,形参 一定要定义为指向函数的指针变量,实参 则可以是函数名或指向函数的指针变量。
【 例 8.28】 编制一个用弦截法求方程根的函数,求解下列方程 。
x3-5x2+6x-30=0
2x3-4x2+3x-6=0 Y f(x)(x2,f(x2))
0 x1 x x0 x2
X
(x1,f(x1))
图 8.21 弦截法求方程的根示意图
8.3 指针与函数
8.3.4 指向函数的指针
3,函数指针作函数的参数
)1()2(
)1(2)2(1
xfxf
xfxxfxx
由直线方程推出 公式:
2009-7-29
548.3 指针与函数
float root(float (*fun)(float))
{ float x,x1,x2,y,y1,y2;
do /*选定一个单调变化的区间 */
{ ……
}while (y1*y2>=0);
do
{ x=(x1*y2-x2*y1)/(y2-y1);
y=(*fun)(x);
if (y*y1<0)
{x2=x; y2=y;}
else
{x1=x; y1=y;}
}while (fabs(y)>=0.0001);
return x;
}
选定一个单调变化的区间 [x1,x2]
按公式计算 x=(x1*y2-x2*y1)/(y2-y1)
y=f(x)
y*y1<0
T F
x2=x x1=x
y2=y y1=y
while(|y|>=1e-4)
x为方程的根图 8.22 弦截法求方程的根 N-S图
Y
f(x)(x2,f(x2))
0 x1 x x0 x2
X
(x1,f(x1))
图 8.21 弦截法求方程的根示意图被积函数如何解决 root函数适用于不同的被积函数?
通过指向函数的指针变量调用函数
f(x);
不可用固定函数名
2009-7-29
558.3 指针与函数
#include <math.h>
void main( )
{ float f1(float),f2(float),root(float (*)(float));
float x1,x2;
x1=root(f1); /* 函数名 f1作实参 */
printf("A root of the first equation:%8.4f\n",x1);
x2=root(f2); /* 函数名 f2作实参 */
printf("A root of the second equation:%8.4f\n",x2);
}
float f1(float x)
{ return(x*x*x-5*x*x+6*x-30); }
float f2(float x)
{ return(2*x*x*x-4*x*x+3*x-6); }
对被调函数的声明