浙江大学刘加海C语言课件5.docx
《浙江大学刘加海C语言课件5.docx》由会员分享,可在线阅读,更多相关《浙江大学刘加海C语言课件5.docx(19页珍藏版)》请在冰豆网上搜索。
浙江大学刘加海C语言课件5
第5章指针与函数
◆本章重点
1.函数指针的概念、定义及赋值。
2.函数指针的应用。
3.指针函数的定义。
4.命令行参数的应用。
◆本章难点
1.函数指针的概念,函数指针的赋值及调用方法。
尤其是定义及调用中的形式参数及实际参数的使用。
2.指针函数的概念以及与函数指针的区别。
3.命令行参数编程中的文件名与命令的关系。
4.命令行参数的赋值方法。
指针和函数的关系主要包括三个方面的内容:
一是指针可以指向某个函数,即函数指针;二是函数的返回值可以是指针类型的变量,即指针函数;三是指针可以作为函数的参数。
5.1指向函数的指针
大家知道,变量a被分配到某个存储区域,若指针变量p的值为该存储区域的首地址,则称p为指向变量a的指针,通过对指针p的间接引用可以访问它所指向的变量a。
函数虽然不是变量,但是它经过编译后,其目标代码在内存中是连续存放的,该代码的首地址就是函数执行时的入口地址,它可以赋给指针变量,使得指针变量指向函数。
利用指向函数的指针变量,可以代替函数名,也可以作为函数的参数传递给其他函数。
也就是说可以用指针变量指向基本类型变量、字符串、数组等,也可指向一个函数。
一个函数在编译时被分配给一个入口地址。
这个入口地址就称为函数的指针。
可以用一个指针变量指向某函数,然后通过该指针变量调用此函数。
5.1.1用函数指针变量调用函数
1.函数指针的声明
格式:
数据类型(*指针变量名)(函数形参类型标识符列表);
其中的“数据类型”是指函数返回值的类型。
例如:
char(*f1)(char*,char);
int(*f2)(int*,int);
上述语句声明两个指向函数的指针:
f1指向形参类型依次为char*、char,返回值类型为char的函数;f2指向形参类型依次为int*、int,返回值类型为int的函数。
注意
f1、f2是不同类型的指针,因为它们各自所指向的函数的返回值类型,形参个数及各形参的类型不尽相同。
2.为指向函数的指针赋值
函数名是指针常量,其值为该函数在内存中存储区域的首地址。
只能将函数的首地址赋值到指向同类型函数的指针。
函数指针赋值格式为:
函数指针=函数名;
3.函数指针的调用方式为:
(*指针名)(实在参数列表);
5.1.2用函数指针变量调用函数举例
例5.1用函数指针调用求最大值函数,求出a和b中的大者。
函数占有一段内存单元,可用函数指针变量指向其首地址,通过指针变量来访问它所指向的函数。
程序设计如下:
#include
intmax(intx,inty)
{
returnx>y?
x:
y;
}
voidmain()
{
inta,b,c;
int(*p)(int,int);/*声明函数指针*/
p=max;/*函数名代表了函数入口地址*/
scanf("%d,%d",&a,&b);
c=(*p)(a,b);/*调用方式*/
printf("a=%d,b=%d,max=%d\n",a,b,c);
}
注意
(1)int(*p)(int,int);定义p是一个指向函数的指针变量,即所指向的函数返回值为int型,形参依次为int、int类型的函数。
(2)在int(*p)(int,int);(*p)两侧的括弧不可省略,表示p先与*结合,是指针变量,然后再与后面的()结合,表示此函数指针变量所指向的函数返回值是整型。
如果写成“int*p(int,int)”,则由于()优先级高于*,它表示声明了一个函数,只不过这个函数的返回值是指向整型变量的指针。
(3)给函数指针变量赋值时只赋函数名,不准带参数。
如:
p=max;
赋值语句“p=max;”的作用是将函数max的入口地址赋给指针变量p。
和数组名代表数组的起始地址一样,函数名代表函数的入口地址。
函数调用可以通过函数名,也可通过函数指针,本例中的调用形式为c=(*p)(a,b);用函数指针调用的参数与函数调用的参数完全一致,用函数指针变量调用函数时,只需将(*p)代替函数名即可。
(4)对指向函数的指针变量不准作加减运算,如:
p++,p--,p+n都是错误的。
例5.2指向函数的指针简单应用示例。
#include
floatf1(floatx)
{
returnx*x;
}
floatf2(floatx)
{
returnx*x*x;
}
voidmain()
{
float(*p)(float),y;
p=f1;
y=(*p)
(2);
printf("%f\n",y);
p=f2;
y=(*p)
(2);
printf("%f\n",y);
}
其中,语句“p=f1;”将函数f1的首地址赋给p,表达式“(*p)
(2)”的作用是以2为实参调用函数f1,返回值为4。
同样,语句“p=f2;”将改写p的值为函数f2的首地址,表达式“(*p)
(2)”的作用是以2为实参调用函数f2,返回值为8。
思考:
有二个函数定义:
intadd(intx,inty)
{
returnx+y;
}
intsub(intx,inty)
{
returnx+y;
}
在main函数中定义一个函数指针,分别调用函数add、sub,并在main函数中输出结果。
例5.3已知契比雪夫多项式的定义如下:
x (n=1)
2x2-1(n=2)
4x3-3x(n=3)
8x4-8x+1(n=4)
试编写程序,从键盘输入整数n和实数x,并计算多项式的值。
#include
voidmain()
{
floatfn1(float),fn2(float),fn3(float),fn4(float);
float(*fp)(float);
floatx;
intn;
printf("inputx:
");
scanf("%f",&x);
printf("inputn:
");
scanf("%d",&n);
switch(n)
{
case1:
fp=fn1;break;
case2:
fp=fn2;break;
case3:
fp=fn3;break;
case4:
fp=fn4;break;
default:
printf("dataerror!
");
}
printf("result=%f",(*fp)(x));
}
floatfn1(floatx)
{
returnx;
}
floatfn2(floatx)
{
return2*x*x-1;
}
floatfn3(floatx)
{
return4*x*x*x-3*x;
}
floatfn4(floatx)
{
return4*x*x*x*x-8*x*x+1;
}
例5.4设一个函数pp,在调用它的时候,每次实现不同的功能。
输入a和b两个数,第一次调用pp时找出a和b中大者,第二次找出a和b中小者。
#include
intmax(int,int);//函数原型声明
intmin(int,int);//函数原型声明
voidpp(int,int,int(*p)(int,int));//函数原型声明
voidmain()
{
inta,b;
scanf("%d,%d",&a,&b);
printf("max=");
pp(a,b,max);
printf("min=");
pp(a,b,min);
}
intmax(intx,inty)//求x,y中的大者
{
returnx>y?
x:
y;
}
intmin(intx,inty)//求x,y中的小者
{
returnxx:
y;
}
voidpp(intx,inty,int(*p)(int,int))
{
intresult;
result=(*p)(x,y);
printf("%d\n",result);
}
运行情况如下:
enteraandb:
2,6
max=6
min=2
注意:
程序中使用的函数指针fp,将随着输入条件n的不同而指向不同的函数,从而达到在不同条件下调用不同函数的目的。
5.2返回值为指针的函数
函数被调用后,可以由函数中的return语句返回一个值到主调函数中。
函数的返回值可以是整型值、字符值、实型值等,也可以是返回指针型的数据,即地址。
返回值为指针的函数,与以前的函数概念类似,只是返回的值的类型是指针类型而已,此类函数通常称为指针函数。
指针函数定义形式:
类型名*函数名(形参表定义)
{
函数体
}
如:
int*func(intx,inty);
其中,func是函数名,其返回值类型是“指向整型的指针”,也即函数值是一个指针,指向一个整型变量,函数的形参为intx和inty。
5.2.1指针函数应用举例
例5.5由键盘输入1~12自然数,再调用指针函数输出英文名被调用程序中应定义指针函数。
#include
char*month_name(intn)
{staticchar*name[13]={
"ILLEGAL",
"JANUARY",
"FEBRUARY",
"MARCH",
"APRIL",
"MAY",
"JUNE",
"JULY",
"AUGUST",
"SEPTEMBER",
"OCTOBET",
"NOVEMBER",
"DECEMBER"};
return((n<1||n>12)?
name[0]:
name[n]);
}
voidmain()
{char*month_name(int);/*说明一个指针函数,由于函数定义在先,此语句可省略*/
intn;
scanf("%d",&n);
printf("%s",month_name(n));
}
例5.6编一函数,在一组字符串中找出按字典序最大的字符串。
#include
#include
char*find_max(char*str[],intn)//声明函数返回值为指向字符的指针
{
inti;
char*p;
p=str[0];//假设str[0]及p所指向的字符串按字典序最大
for(i=1;iif(strcmp(str[i],p)>0)
p=str[i];//使p指向按字典序最大的字符串
returnp;//返回指针值
}
voidmain()
{
char*a[5]={"ABc","abc","Abc","abcd","abca"};
printf("%s\n",find_max(a,5));
}
运行结果:
abcd
分析:
char*find_max(char*str[],intn)声明函数返回值为一个指向字符或字符串的指针。
形式参数char*str[]表示它是一个指针数组,指针数组中的每个元素相当于一个指针变量,分别指向不同字符串的首地址。
strcmp(str[i],p)表示str[i]和p指向的两个字符串从左至右逐个字符相比较(按ASCII码值大小比较),直到出现不同的字符或遇到‘\0’为止。
如果全部字符相同,则认为相等(返回0值);若出现不同的字符,则以第一个不相同的字符比较结果为准(返回其ASCII码的差值)。
例5.7有若干学生的成绩(每个学生四门课程),要求用户在输入学生序号(假设从0号开始)后,能输出该学生的全部成绩。
#include
float*search(float(*pointer)[4],intn);/*函数原型声明*/
voidmain()
{
staticfloatscore[][4]={{65,70,80,90},{76,89,67,88},{84,78,90,76}};
float*p;
inti,num;
printf("Enterthenumberofstudent:
");
scanf("%d",&num);
printf("ThescoresofNo.%dare:
\n",num);
p=search(score,m);/*在score数组中查询m号学生的成绩*/
/*p指向m号学生第0门课程*/
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
}
float*search(float(*pointer)[4],intn)
{
float*pp;/*pp是指向实数的指针,pointer是指向数组的指针*/
pp=*(pointer+n);/*pp=(float*)(pointer+n)*/
returnpp;
}
运行结果如下:
Enterthenumberofstudent:
2
ThescoresofNo.2are:
84.0078.0090.0076.00
注意
float(*pointer)[4],其中的pointer是一个指向一维数组的指针变量,数组元素个数为4(四门课程),即pointer指向一个学生的四门成绩。
main()函数调用search函数,将score数组的首地址传给pointer(注意score是一个二维数组名,所以score也是一个指向行的指针,而不是指向列元素的指针)。
输入学生序号后,使pp指向该学生第0门课程,赋值给p。
*(p+i)表示此学生第i门课程的成绩。
例5.8编一函数,在字符串中找出一个子串的首地址。
#include
#include
char*find_str(char*s1,char*s2)
{
inti,j,ls2;
ls2=strlen(s2);//求子串s2的长度ls2
for(i=0;i{
for(j=0;jif(s1[j+i]!
=s2[j])
break;
if(j==ls2)
returns1+i;
}/*查找s1+i是否为所求的地址值。
具体思想为:
从地址s1+i起的ls2个
字符与从地址s2起的ls2个字符均对应相同,则s1+i是所求地址。
*/
returnNULL;//NULL为空指针
}
voidmain()
{
char*src="Windows98Office97Microsoft",*dest="Office97",*pp;
pp=find_str(src,dest);//返回的地址值送给指针变量pp
if(pp!
=NULL)
printf("%s\n",pp);
else
printf("notfind:
%s\n",dest);
}
运行结果为:
Office97Microsoft
注意函数*find_str返回字符串s2在s1中第一次出现的首地址,当查找不到时返回空指针值NULL。
5.2.2指针函数和函数指针比较
指针函数和函数指针这两个概念都是简称。
它们的区别如表5.1所示。
表5.1函数指针和指针函数的区别
函数指针
指针函数
含义:
指向函数的入口地址的指针
含义:
调用函数后返回值为指针
定义形式:
类型(*p)(形式参数表)
定义形式:
类型*p(形式参数表)
本质:
指针
本质:
函数
5.3命令行参数
5.3.1命令行参数的概念
所谓“命令行参数”,一般是在DOS环境下所输入的命令及其参数。
执行文件的形式为:
命令名参数1参数2...参数n(回车)。
命令名和各参数之间用空格分隔。
例如,若有一命令文件名为disp,现要将两个字符串“Zhejiang”,“Hangzhou”作为参数传送给main函数。
可以写成以下形式:
dispZhejiangHangzhou
带参数的命令行文件的编辑步骤:
首先编辑一个名为disp.c(或disp.cpp)的文件,然后将之经过编译、连接后产生disp.exe文件,最后在Dos命令提示符下输入带参数的命令然后回车即可。
5.3.2命令行参数的表示方法
在编辑C语言源程序时,main()函数应带有两个参数,形式为:
voidmain(intargc,char*argv[])
{
……
}
注意
(1)形参argc是命令行中参数的个数,可执行文件名本身也算一个,以空格分隔。
(2)形参argv是一个字符指针数组,argv是一个数组名,元素个数为形参argc的值,其元素为指向命令行字符串的指针,指针数组argv[argc-1]分别指向命令行各参数的首地址。
5.3.3命令行参数使用举例
例5.9编一程序,显示命令行上的所有参数(不包括命令名),源程序名为echo.c。
#include
voidmain(intargc,char*argv[])
{
inti;
i=0;
while(argc>1)
{
++i;
printf("%s\n",argv[i]);
--argc;
}
}
编译连接后生成应用程序文件echo.exe,进入DOS环境,在命令行输入:
disp Zhejiang Hangzhou
运行结果为:
Zhejiang
Hangzhou
程序运行时,系统自动作如下赋值:
命令行参数的个数(含命令)3赋给argc,因为输入的参数为3个,分别为:
dispzhejianghangzhou。
argv[0]指向字符串"disp"的首地址;argv[1]指向字符串"Zhejiang"的首地址;argv[2]指向字符串"Hangzhou"的首地址。
思考:
如文件名前有路径,它们都保存在argv[0]中,请调试当把++i放在printf语句后,程序的运行情况如何?
例5.10用一程序实现文件的加密和解密。
约定:
c程序取名为lock.c,程序的可执行文件名为lock.exe,其用法为:
lock+|-<被处理的文件名>,其中“+”为加密,“-”为解密。
#include
voidmain(intargc,char*argv[])
{
charc;
if(argc!
=3)printf("参数个数不对!
\n");
else
{c=*argv[1];/*截取第二个实参字符串的第一个字符*/
switch(c)
{case'+':
/*执行加密*/
{/*加密程序段*/
printf("执行加密程序段。
\n");
}
break;
case'-':
/*执行解密*/
{/*解密程序段*/
printf("执行解密程序段。
\n");
}
break;
default:
printf("第二个参数错误!
\n");
}
}
}
注意
(1)形参argc的值为3,因为有三个参数”lock”、”+|-“、被处理的文件名。
(2)元素argv[0]指向第1个实参字符串“lock”,元素argv[1]指向第2个实参字符串“+|-”,元素argv[2]指向第3个实参字符串“被处理的文件名”。
(3)main函数中的形参不一定命名为argc和argv,可以是任意的名字,只是人们习惯用argc,argv而已。
习题
1.输入3个整数,按从小到大的顺序输出(用函数指针调用实现)。
2.输入3个字符串,按从小到大的顺序输出(用函数指针调用实现)。
3.用函数指针来实现:
写一个程序,输入x的值,输出相应的结果。
4.从键盘输入10名学生的成绩,显示其中的最高分、最低分及平均成绩(要求用指针来编写程序)。
5.编制函数,在字符串数组中查找与另一个字符串相等的字符串,函数的返回值为字符串的地址或NULL值(当找不到时)。
6.以下程序输出的结果是()。
#include
intfuna(inta,intb)
{
returna+b;
}
intfunb(inta,intb)
{
returna-b;
}
intsub(int(*t)(int,int),intx,inty)
{
return((*t)(x,y));
}
voidmain()
{
intx,(*p)(int,int);
p=funa;
x=sub(p,9,3);
x+=sub(funb,8,3);
printf("%d\n",x);
}
7.阅读下面程序。
#include
voidmain(inta,char**ar)
{
a--;
printf("%d",a);
if(a==0)exit(0);
main(a,ar);
}
如文件名为pro.c编译后在命令行输入C:
\>proaaaaaabbbbbccccddd,写出程序运行的结果。
8.编一程序,输入星期的序号0~6,输出中文的星期名。
9.编写一个带命令行参数的程序prog.c。
当运行progfilename时将文本文件filename按原样在标准输出(终端)上输出;当运行prog-nfilename时将文本文件按原样在标准输出(终端)上输出,并在各行开头显示当前行行号(从1:
开始,如1:
XXXXXXXX);其他运行方式均显示出错信息。