什么是函数?
函数是用于完成特定任务的程序代码的自包含单元
为什么使用函数?
第一:可以省去重复代码的编写。如果程序中需要多次使用某种特定的功能,只要编写一个合适的函数即可,也就是函数可以一次编写,多次调用。第二:函数额可以是程序模块化,更容易阅读和修改。
我们可以把函数看做一个“黑盒子”,即对应一定的输入会产生特定的记过或返回某个值,而黑盒子的内部行为并不需要考虑(除非是函数的编写者)。以这样方式看待函数的功能有助于把精力投入到程序整体设计而不是其实现细节。
一个简单的程序:
1 /*打一串星号*/
2 #include <stdio.h>
3 #define NAME "Owen_beta"
4 #define WIDTH 40
5
6 void starbar(void); /*声明函数原型(function prototype) [注意这里有逗号]*/
7
8 int main(void) /*main()函数*/
9 {
10 starbar(); /*函数调用(calling function*/
11 printf("%s\n",NAME);
12 starbar();
13 }
14
15 void starbar(void) /*函数定义(function definition*/
16 {
17 int count; /*因为函数是一个“黑盒子”,所以count是一个局部变量
18 在main()函数中是不可见的*/
19 for(count=1;count<=WIDTH;count++)
20 {
21 putchar('*'); /*putchar函数(字符输出函数)的作用是向终端输出一个字符*/
22 }
23 putchar('\n');
24 }
函数分析:
1。starbar标识符在不同位置被使用了3次:函数原型告知编译器starbar()的函数类型;函数调用导致函数的执行;函数定义则指定了“黑盒子”的具体实现
2。函数同变量一样有多种类型。任何程序在使用函数之前都要声明该函数的类型
在上面的例子中:
void starbar(void)
圆括号表明starbar是一个函数名。第一个void指的是函数类型:函数没有返回值。第二个位于圆括号内的void表明该函数不接受任何参数。分号的作用是表示该语句是进行函数声明而不是函数定义。也就是说,这一行声明了程序将使用一个名为starbar()且函数类型为void的函数,同时通知编译器需要在其他位置找到该函数的定义。
3.建议函数类型放在main()函数之前,这样便于阅读
4.程序在main()中通过使用函数名后跟圆括号和分号的形式调用starbar()函数
1 starbar(); /*函数调用(calling function)*/
5.程序中starbar()和mian()具有相同的定义格式,即首先以类型、名称和圆括号开始,接着是开始花括号、变量声明、函数语句定义以及结束花括号
如下列程序所示
1 #include <stdio.h> //预处理指令
2 #define WIDTH 40 //预处理指令
3
4 void starbar(void) //函数名
5 {
6 int count; //声明语句
7 for(count=1;count<=WIDTH;count++) //循环控制语句
8 {
9 putchar('*'); //函数
10 }
11 putchar('\n');
12 }
如果不想打星号,想打一行空格?应该怎么办?我们肯定想到编写一个函数来打空格。
事实上,这样做很浪费时间,可以编写一个
通用函数来打出空格
1 #include <stdio.h>
2 #define NAME "Owen_beta"
3 #define WIDTH 40
4 #define SPACE ' '
5
6 void show_n_char(char ch, int num); //定义函数原型,记得要有逗号
7
8 int main(void)
9 {
10 int spaces;
11 show_n_char('*',WIDTH);
12 putchar('\n');
13 spaces=(WIDTH-strlen(NAME)/2)/2; //算出NAME前面的空格数
14 show_n_char(SPACE,spaces); //打出空格
15 printf("%s",NAME);
16 putchar('\n');
17 show_n_char('*',WIDTH);
18 }
19
20 void show_n_char(char ch,int num)
21 {
22 int count;
23 for(count=1;count<=num;count++)
24 {
25 putchar(ch);
26 }
27 }
上面的这一个程序用到了“
形式参量”的概念,如下
void show_n_char(char ch,int num) //函数定义
{
....
}
这行代码通知编译器show_n_char()使用名为ch和num的两个参数,并且这两个参数的类型分别为char和int。变量ch和num为形式参数,形式参量是局部变量,他们是函数私有
的。
我们注意到,在main()函数中有一段代码
1 show_n_char(SPACE,spaces); //打出空格
这里的SPACE和spaces是实际参数。实际参数是付给被成为形式参量的函数变量的具体值。因为被调函数中使用的值是从调用函数中复制而来的,所以不管在被调函数中对复制数值进行什么操作,调用函数中的原数值不会收到任何影响。
总结下实际参数和形式参量:
实际参量是函数调用是出现在圆括号中的表达式,而形式参量则是函数定义中在函数头部声明的变量。
前面说了从调用函数到被调用函数的通信方法。当需要沿相反方向传递信息时,可以使用函数返回值。我们可以
使用return来从函数中返回值。
1 /*找出两个整数的最小值*/
2 #include <stdio.h>
3 int imin(int,int); /*在函数原型中,我们可以根据喜好来省略变量名*/
4
5 int main(void)
6 {
7 int evil1,evil2;
8 printf("Enter a pair of integer(q to quit): \n");
9 while(scanf("%d%d",&evil1,&evil2)==2)
10 {
11 printf("The lesser of %d and %d is %d\n",evil1,evil2,imin(evil1,evil2)); /*函数调用*/
12 printf("Enter a pair of integer(q to quit): \n");
13 }
14 printf("Bye\n");
15 getch();
16 return 0;
17 }
18
19 int imin(int m,int n) /*函数定义*/
20 {
21 int min;
22 if(m<n)
23 min=m;
24 else
25 min=n;
26 }
关键字return指明了其后的表达式即是该函数的返回值。
变量min是imin()私有的,但是return语句把min的数值返回给了调用函数。下面这个语句相当于把min的值赋给lesser
lesser=imin(m,n);
1 imin(m,n);
2 lesser=min;
答案是否定的!!因为调用函数不知道min变量的存在。imin()中的变量是该函数的局部变量。我们前面说了,函数是一个“黑盒子”,里面的东西对main()函数来说是不可见的。
我们还可以使用下面的代码来简化程序
1 int imin(int m,int n) /*函数定义*/
2 {
3 return (m<n)?m:n;
4 }
return语句的另一个作用是终止执行函数,并把控制返回给调用函数的下一个语句。既是return语句不是函数的最后一个句子,其执行结果也是如此。因此我们可以这样辨析imin()函数
1 int imin(int m,int n) /*函数定义*/
2 {
3 int min;
4 if(m<n)
5 return m;
6 else
7 return n;
8 }
return;
这个语句会终止执行函数并把控制返回给调用函数。因为return后没有任何表达式,所以没有返回值,这种形式只能用于void类型的函数之中
为了进一步了解函数的“黑盒子”性质,程序如下
1 #include <stdio.h>
2 void mikado(int); /*声明函数原型*/
3
4 int main(void)
5 {
6 int pooh=2,bah=5;
7 printf("In mian(), pooh=%d and &pooh=%p\n",pooh,&pooh);
8 printf("In main(), bah=%d and &bah=%p\n",bah,&bah);
9 mikado(pooh);
10 return 0;
11 }
12
13 void mikado(int bah) /*函数定义*/
14 {
15 int pooh=10;
16 printf("In pikado(), pooh=%d and &pooh=%p\n",pooh,&pooh);
17 printf("In mikado(), bah=%d and &bah=%p\n",bah,&bah);
18 }
output:
In mian(), pooh=2 and &pooh=0028FF1C
In main(), bah=5 and &bah=0028FF18
In pikado(), pooh=10 and &pooh=0028FEEC
In mikado(), bah=2 and &bah=0028FF00
上述的output说明了几个问题:
首先,两个pooh变量具有不同的地址,两个bah变量也是这样,证明了函数的“黑盒子”性质;第二,main()函数调用mikado(pooh)把实际参数(main()中的pooh值2)传递给了mikado(int bah)中的形式参数。注意:这种传递只是进行了数值传递,两个变量(main()中的pooh和mikado()中的bah)仍分别保持原来的特性,再一次证明了函数的“黑盒子”性质。
改变调用函数中的变量
如果我们要交换变量怎么办?也许我们会使用下面的程序
1 #include <stdio.h>
2 void interchange(int u,int v); /*声明函数原型*/
3 int main(void)
4 {
5 int x=5,y=10;
6 printf("Originally x=%d and y=%d.\n",x,y);
7 interchange(x,y);
8 printf("Now x=%d and y=%d.\n",x,y);
9 return 0;
10 }
11
12 void interchange(int u,int v) /*函数定义*/
13 {
14 int temp;
15
16 temp=u;
17 u=v;
18 v=temp;
19 }
output:
Originally x=5 and y=10.
Now x=5 and y=10.
但是这是错误的程序,调用函数中的变量没有发生改变。void interchange(int u,int v)中的temp,u,v都是局部变量,它们都处于“黑盒子”中,对main()来说是不可见的。在函数中对变量的改变不会影响main()中的变量。我们可以通过下面的程序来验证:
1 #include <stdio.h>
2 void interchange(int u,int v); /*声明函数原型*/
3 int main(void)
4 {
5 int x=5,y=10;
6 printf("Originally x=%d and y=%d.\n",x,y);
7 interchange(x,y);
8 printf("Now x=%d and y=%d.\n",x,y);
9 return 0;
10 }
11
12 void interchange(int u,int v) /*函数定义*/
13 {
14 int temp;
15
16 printf("Originally u=%d and v=%d\n",u,v);
17 temp=u;
18 u=v;
19 v=temp;
20 printf("Now u=%d and v=%d.\n",u,v);
21 }
output:
Originally x=5 and y=10.
Originally u=5 and v=10.
Now u=10 and v=5.
Now x=5 and y=10.
这样我们又一次证明了“黑盒子”性质
也许我们可以用return来返回值,进行数值调换。但是return语句只能把一个数值传递给调用函数,而数值交换需要传递2个的数值给调用函数。
可以用指针来实现!
1 #include <stdio.h>
2 void interchange(int * u,int * v); /*声明函数原型*/
3 int main(void)
4 {
5 int x=5,y=10;
6 printf("Originally x=%d and y=%d.\n",x,y);
7 interchange(&x,&y); /*向函数传递地址*/
8 printf("Now x=%d and y=%d.\n",x,y);
9 return 0;
10 }
11
12 void interchange(int * u, int * v) /*函数定义*/
13 {
14 int temp;
15 temp=*u;
16 *u=*v;
17 *v=temp;
18 }
这样,就进行了数值交换
完成。