第八讲C语言函数_第1页
第八讲C语言函数_第2页
第八讲C语言函数_第3页
第八讲C语言函数_第4页
第八讲C语言函数_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、一、函数概述一、函数概述首先,什么是函数?函数(function)是用于完成特定任务的程序代码的自包含单元。尽管C中的函数和其他语言中的函数、子程序或子过程等扮演着相同的角色,但是在细节上会有所不同。某些函数会导致执行某些动作,比如printf()可使数据呈现在屏幕上;还有一些函数能返回一个值以供程序使用,如strlen()将指定字符串的长度传递给程序。一般来讲,一个函数可同时具备以上两种功能。为什么使用函数?第一,函数的使用可以省去重复代码的编写。如果程序中需要多次使用某种特定的功能,那么只需要编写一个合适的函数即可。程序可以在任何需要的地方调用该函数,并且同一个函数可以在不同的程序中调用,

2、就像在许多程序中需要使用putchar()一样。第二,即使某种功能在程序中只使用一次,将其以函数的形式实现也是有必要的,因为函数使得程序更加模块化,从而有利于程序的阅读,修改和完善。示例程序示例程序我们需要掌握如何正确定义函数、如何调用函数和如何建立函数间的通信。为了让您对这些有一个清晰的思路,我们首先来看一个非常简单的例子:#include#define NAME No.83 Middle School#define ADDRESS 151 Xianning zhonglu#define PLACE Xian Shannxi#define WIDTH 50void starbar(void)

3、;int main(void)starbar();printf(%sn,NAME);printf(%sn,ADDRESS);printf(%sn,PLACE);starbar();return 0;void starbar(void)int i;for(i=1;i=WIDTH;i+)putchar(*);putchar(n);程序输出如下:*No.83 Middle School151 Xianning zhongluXian Shannxi*程序分析程序分析l Starbar标识符在不同位置被使用了3次:函数原型(function prototype)告知编译器starbar()的函数类型,

4、函数调用(function call)导致该函数的执行,函数定义(function definition)则确切指定了该函数的具体功能。l 函数同变量一样具有多种类型。任何程序在使用函数之前都需要声明该函数的类型。因此,在main()函数的定义之前出现了如下的函数原型void starbar(void);圆括号表明starbar是一个函数名。第一个void指的是函数类型;它的意思是该函数没有返回值。第二个void(位于圆括号内)表明该函数不接受任何参数。分号的作用是表示该语句是进行函数的声明而不是函数定义。也就是说,这一行声明了程序将使用一个名为starbar()且函数类型为void的函数,同

5、事通知编译器需要在其他位置找到该函数的定义。l 程序把starbar()原型置于main()之前;也可以将其置于main()之内,可以放置在变量声明的任何位置。这两种方法都正确。l 程序在main()中通过使用函数名后跟圆括号和分号的格式调用函数starbar(),语句如下:starbar();l 这是void类型函数的一般调用形式。当计算机执行到starbar();语句时,它找到starbar()函数并执行其中的指令。执行完starbar()中的代码后,计算机返回到调用函数(calling function)的下一行继续执行。在本例中,调用函数式main()。如下图所示。l 程序中starb

6、ar()和main()具有相同的定义格式,即首先以类型、名称和圆括号开始,接着是开始花括号、变量声明、函数语句定义以及结束花括号。注意此处的starbar()后没有分号,这告诉编译器是在定义函数starbar(),而不是调用它或声明它的原型。l starbar()中的变量i是一个局部(Local)变量。这意味着该变量只是在starbar()中可用。即使你在其他函数(包括main()函数)中使用名称i,也不会出现任何冲突,你将得到具有统一名称的多个单独的、互不相关的变量。程序分析程序分析二、函数参数二、函数参数在上例中,如果文字居中显示,那么信头就会更漂亮。可以通过在打印文字之前打印一定数目的空

7、格来达到此目的。这和starbar()函数类似。在starbar()中打印的是一定数量的星号,而现在要打印的是一定数目的空格。我们应不应该为这个任务单独编写一个函数呢?答案是否定的,我们应该编写一个可以同时胜任这两个任务的更为通用的函数。新函数将命名为show_n_char()(意思是显示n个某种字符)。唯一的改变是要显示的字符和要现实的次数将被作为参数传递给函数show_n_char(),而不是把它们置于函数内部。具体一点儿说,假如一行是50个字符宽。50个星号恰好填满一行,调用函数show_n_char(*,50)可以同starbar()函数一样实现该功能。而将No.83 Middle S

8、chool.居中需要多少个空格?因为No.83 Middle School.是20个字符宽,为使其居中,必须先输出15个空格,这样该短语两边就会分别有15个空格。所以可以调用show_n_char( ,15)输出15个空格。除了使用参数外,在其他方面show_n_char()和函数starbar()非常相似。两者的一个不同之处是show_n_char()不像starbar()那样输出换行符,因为在同一行中可能还需要输出其他字符。程序清单如下:#include#include#define NAME No.83 Middle School#define ADDRESS 151 Xianning

9、zhonglu#define PLACE Xian Shannxi#define WIDTH 50#define SPACE void show_n_char(char ch,int num);int main(void)int spaces;show_n_char(*,WIDTH);putchar(n);show_n_char(SPACE,15);printf(%sn,NAME);spaces=(WIDTH-strlen(ADDRESS)/2;show_n_char(SPACE,spaces);printf(%sn,ADDRESS);show_n_char(SPACE, (WIDTH-str

10、len(PLACE)/2);printf(%sn,PLACE);show_n_char(*,WIDTH);putchar(n);return 0;void show_n_char(char ch,int num)int i;for(i=1;i=num;i+)putchar(ch);三、定义带有参数的函数:形式参量三、定义带有参数的函数:形式参量函数定义一下面的函数头开始:void show_n_char(char ch,int num)这行代码通知编译器show_n_char()使用名为ch和num的两个参数,并且这两个参数的类型分别是char和int。变量ch和num被称为形式参量形式参量(

11、formal parameter)。形式参量是局部变量,它们是定义它们的函数所私有的。这意味着可以在其他函数中使用相同的变量名。每当调用函数时,这些变量就会被赋值。注意:不能像通常的变量声明那样使用变量列表来声明同一类型的变量,如下所示:void a(int x,y,z) /*不正确*/void a(int x,int y,int z) /*正确*/另外,如果表示一个函数确实不使用参数,需要在括号内加入void关键字。六、带参函数的原型声明六、带参函数的原型声明使用函数之前需要用原型声明该函数void show_n_char(char ch,int num);当函数接受参数时,函数原型通过使用

12、一个逗号分隔的类型列表指明参数的个数和类型。在函数原型中可以根据自己的喜好省略变量名:void show_n_char(char,int);因为在原型中并没有实际的创建变量。这只是说明char代表一个char类型的变量,num代表了一个int类型的变量。四、调用带有参数的函数:实际参量四、调用带有参数的函数:实际参量函数调用中,通过使用实际参数(actual argument)对ch和num赋值。例如上例中show_n_char()的第一次使用:show_n_char(SPACE,12);实际参数是空格字符和12。这两个数值被赋给show_n_char()中相应的形式参量:变量ch和num。换

13、句话说,形式参灵是被调函数中的变量,而实际参数是调用函数分配给被调用函数变量的特定数值。如上例中所示,实际参数可以是常量、变量或一个复杂的表达式。但是无论何种形式的实际参数,执行时首先要计算其值,然后将该值赋值给被调用函数中相应的形式参量。以上例中最后一次使用show_n_char()的语句为例:show_n_char(SPACE, (WIDTH-strlen(PLACE)/2);求得构成第二个实际参数的表达式的值为18。然后把数值18赋给变量num。被调函数不知道也不必知道这个数值来自与常量、变量或是更一般地表达式。再次,实际参数是赋给被称为形式参量的函数变量的具体值。因为被调函数使用的值是

14、从调用函数中复制而来的,所以不管在被调函数中对复制数值进行什么操作,调用函数中的原数值不会受到任何影响。五、使用五、使用return从函数中返回一个值从函数中返回一个值前面讨论了从调用函数到被调函数的通信方法。需要沿相反方向传递信息时,可以使用函数返回值。为了进一步说明,我们将构建一个比较两个参数大小并将较小数值返回的函数。#includeint imin(int,int);int main(void)int a,b;printf(Enter a pair of integers(q to quit): n);while(scanf(%d%d,&a,&b)=2)printf(T

15、he lesser of %d and %d is %dn,a,b,imin(a,b);printf(Enter a pair of integers(q to quit): n);printf(Bye!);return 0;int imin(int m,int n)int min;if(nm)min=n;elsemin=m;return min;程序分析程序分析关键字return指明了其后的表达式的数值即是该函数的返回值。在本例中,函数返回变量min的值。因为min的类型是int,所以函数imin()的类型也是int。变量min是imin()私有的,但是return语句把min的数值返回给了

16、调用函数。下面这个语句的作用相当于把min的值赋给lesser:lesser=imin(a,b);但是不能用下面这个语句代替上句。imin(a,b);lesser=min;因为调用函数并不知道min的存在。imin()中的变量是该函数的局部变量。函数调用imin(a,b)只是复制了两个变量的数值。返回值不仅可以被赋给一个变量,也可以被用作表达式的一部分。例如,可以使用下列语句:answer=2*imin(a,b)+25;printf(”%dn”,imin(a,b);程序分析程序分析返回值可以由任何表达式计算得出,而不仅仅来自于变量。例如可以使用以下代码来简化示例程序:int imin(int

17、n,int m)return (nm)? n:m;该条件表达式的值是n和m中的较小者,并且该数值被返回给调用函数。return语句的另一个作用是终止执行函数,并把控制返回给调用函数的下一个语句。即使return语句不是函数的最后一个语句,其执行结果也是如此。因此可以用下面的方式编写imin()函数:int imin(int n,int m)if(nm)return n;elsereturn m;大多数C程序员还是更倾向于只在函数结尾使用一次return语句,因为这样做更有利于阅读程序的人明白函数的执行流程。但是,以上这种小函数中多次使用return语句并没有问题。六、函数类型六、函数类型函数应

18、该进行类型声明。同时其类型应和返回值类型形同。而无返回值的函数应被声明为void类型。在早期C语言中,如果函数没有进行类型声明,则该函数具有默认的函数类型int。使用这种默认类型的原因是早期大多数C语言函数都是int类型的。但是新的标准不再支持函数的int类型的默认设置。类型声明是函数定义的一部分。但需要注意的是该类型指的是返回值类型,而不是函数参数类型。例如,以下函数头表示函数使用两个int类型的参数而返回值的类型是double。double aaa(int a,int b)为正确使用函数,程序在首次调用之前需要知道该函数的类型。途径之一是在第一次调用之前进行完整的函数定义。但是,这种方式会

19、使得程序难于阅读。而且,需要的函数可能在C库中或其他文件中。因此,通常的做法是预先对函数进行声明,以便将函数的信息通知给编译器。在C标准库中,函数被分为几个系列每个系列都有各自的头文件。这些头文件包含了本系列函数的声明部分。例如:头文件stdio.h包含了标准I/O库函数的声明,像printf()、scanf()。而头文件math.h是对各种数学函数进行声明。例如它使用以下代码通知编译器函数sqrt()返回double类型的数值:doubel sqrt(double);但是不要把函数声明和函数定义混淆。函数声明只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。引用math.h头文

20、件只向编译器说明了sqrt()的返回值类型为double,但是sqrt()的实现代码则位于另一个库函数文件中。七、改变调用函数中的变量七、改变调用函数中的变量有时我们需要用一个函数改变另一个函数中的变量。例如,排序问题中的一个常见任务是交换两个变量的数值。假设要交换两个变量x和y的数值:temp=x;a=y;y=temp;现在这段代码可以实现数值交换的功能,可以将其编写成函数。#includevoid interchange(int,int);int main(void)int x=5,y=10;printf(Originally x=%d and y=%d.n,x,y);interchang

21、e(x,y);printf(Now x=%d and y=%d.n,x,y);return 0;void interchange(int a,int b)int temp;temp=a;a=b;b=temp;七、改变调用函数中的变量七、改变调用函数中的变量运行程序之后我们发现数值并没有发生交换。我们在interchange()中加入一些打印语句来检查错误#includevoid interchange(int,int);int main(void)int x=5,y=10;printf(Originally x=%d and y=%d.n,x,y);interchange(x,y);print

22、f(Now x=%d and y=%d.n,x,y);return 0;void interchange(int a,int b)int temp;printf(Originally x=%d and y=%d.n,a,b);temp=a;a=b;b=temp;printf(Now x=%d and y=%d.n,a,b);七、改变调用函数中的变量七、改变调用函数中的变量我们发现函数interchange()并没有错误,a和b的值确实得到了交换。问题出现在把执行结果传递给main()的时候,正如我们之前强调过的那样,interchange()使用的变量独立于函数main(),因此交换a和b的数

23、值对x和y的数值没有任何影响!有人可能要说,使用return语句可以吗?不要忘了,return语句只能把一个数值传递给调用函数,但是现在我们需要传递两个数值。这时我们就需要用到指针指针了。八、指针简介八、指针简介究竟什么叫做指针?一般来讲,指针是一个其数值为地址的变量。就好比char类型的变量用字符作为其数值,而int类型变量的数值是整数,指针变量的数值表示的是地址。指针在C中有很多用途,这里我们暂时只研究他作为函数参数的方法和理由。如果将某个指针变量命名为ptr,就可以使用如下语句:ptr=&a; /把a的地址赋给ptr对于这个语句,我们称ptr“指向”a。这里需要注意的一点是,pt

24、r和&a的区别在于前者是一个变量,而后者是一个常量。当然,ptr也可以指向其他任何地方:ptr=∑ /令ptr指向sum而不是a这时ptr的值是sum的地址。要创建一个指针变量,首先要声明其类型。假设您想把ptr声明为可以存放一个int数值的地址,就需要使用下面的新运算符。假定ptr指向sum,如下所示:ptr=∑这时就可以使用间接(indirection)运算符*(也称作取值(dereferecing)运算符)来取sum中存放的数值(不要把这种一元运算符和表示乘法的二元运算符相混淆)。val=*ptr; /得到ptr指向的值语句ptr=&sum

25、;以及语句val=*ptr;放在一起等同于下面的语句:val=sum;由此看出,使用地址运算符和间接运算符可以间接完成上述语句的功能,这也正是“间接运算符”名称的由来。八、指针简介八、指针简介那我们究竟要如何来声明指针变量呢?可能有人会猜是不是这样定义:pointer ptr;这是不对的,为什么不能这样声明呢?因为这对于生命一个变量为指针是不够的,还需要说明指针所指向变量的类型。因为不同类型的变量占用的存储空间的大小不同,而有些指针操作需要知道变量类型所占用的存储空间。同时,程序也需要了解地址中存储的是何种数据。例如,long和float两种类型的数值可能使用相同大小的存储空间,但是它们的数据

26、存储方式完全不同。所以,指针的声明形式如下:int *ptr1; /ptr1是指向一个整数变量的指针char *ptr2; /ptr2是指向一个字符变量的指针float *ptr3,ptr4; /ptr3和ptr4是指向浮点变量的指针类型标识符表明了被指向变量的类型,而星号(*)表示该变量为一指针。声明int *ptr1;的意思是ptr1是一个指针,而且*ptr1是int类型的。ptr2所指向的值(*ptr2)是char类型的。而ptr2本身又是什么类型?我们称它为“指向char的指针”类型。C专门为指针提供了%p输出格式。八、指针简介八、指针简介在下面的程序清单中,函数interchange

27、()使用了指针参数:#includevoid interchange(int *,int *);int main(void)int x=5,y=10;printf(Originally x=%d and y=%d.n,x,y);interchange(&x,&y);printf(Now x=%d and y=%d.n,x,y);return 0;void interchange(int *a,int *b)int temp;temp=*a;*a=*b;*b=temp;八、指针简介八、指针简介这一次程序能否正常运行?答案是肯定的。下面我们来详细分析一下程序的运行情况。首先函数调用语句如下:interchange(&x,&y);可以看出,函数传递的是x和y的地址而不是它们的值。这就意味着i

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论