c语言中的函数

这个是我c语言讲义的第三章,有些同学催促过几次,刚写完,现发上来,下一章讲述基本算法,感兴趣请关注。

1.什么是函数

说到函数 , 大家自然会联想到数学中的函数 , 但是我们程序中的函数和数学中的函数有很大区别 , C 语言可以编写数学函数 , 完成数学计算的功能 , 但函数远远不是计算那么简单 .
简单的说函数就是把一段代码封装起来 , 起一个名字 , 当我们需要使用这段代码的时候 , 调用这个函数就可以了 , 这个看似简单的东西 , 却是 C 语言代码组织的关键 , 其重要性不亚于房屋的主结构 , 因此在讲述了存储之后 , 直接进入了函数。函数不但对于 C 语言重要 , 对于其他的编程语言一样重要。 C 语言的代码除了声明和定义的语句可以在函数外面,其他的代码都是存在于函数里,代码里调用函数也占了相当的一部分,所以写程序就是写函数。正是有了函数,一个复杂庞大的系统,经过层层分解,生成了大量的函数,每一个函数都很简单,由一个函数开始,但经过层层调用却完成了异常复杂的功能,就像一台神奇的机器,你组装好了每一个零件,按下一个开关,机器就开始运转起来。
开始写函数,就是开始写程序了,写程序前必须要说一件事情,就是什么是好程序,好代码的标准是什么,在你写代码之前这个标准一定要明确,而且一直要向这个标准看齐.在我看来,好代码的标准有以下几点,按重要性列出:
1 .代码没有重复,雷同,尽可能的复用写好的代码。复用是指调用,不是 Copy Paste ,这个是重中之重,软件开发的很多技术都是以复用为目的的,函数就是代码复用的最常用最简单的方法,所以一定要养成多写函数的习惯。
2 .代码易读性要好,写代码不是展现你语法水平有多高,尽量使用易读的语句,看起来容易,找错误也容易。在我的课程里只用最简单的语句写程序,可能应付考试不行,但写一个软件还是够用。
3 .代码要有一个好的格式。这个比较容易,我这有个简单的格式,“ { ”和“ } ”占一行,“ { ”下一行要缩进,   对应的“ { ”和“ } ”要对齐,我在一本书上看到的,很好用,记住了。
4 .注释要加好。注释主要是关于函数的说明,函数的算法说明,作者信息,编写时间,修改记录等,有写好的函数注释模板,大家可以上网找一个,有的开发工具能生成。
下面我就先来写第一函数,这个函数是给游戏里所有的格子初始化。
int init()
{
      Ball ba[9][9];
   int num;
   ba[0][0].x=0;
   ba[0][0].y=0;
ba[0][0].isHaveBall=false;
ba[0][0].ballColor=0;
      ba[0][1].x=0;
   ba[0][1].y=1;
ba[0][1].isHaveBall=false;
ba[0][1].ballColor=0;
   ……
ba[8][8].x=8;
   ba[8][8].y=8;
ba[8][8].isHaveBall=false;
ba[8][8].ballColor=0;
num=81;
return num;//return 81    都可以
}
我们来看函数格式 :
int init()
{
}
int  是返回值类型, init 是函数的名字,小括号里可以放函数参数,没有的话可以空着,大括号里就是函数的代码。
函数调用 函数写好了,不一定就会运行,只有在调用的时候,才能运行,调用的时候写函数的名字加括号就可以了,如果希望保存返回值,可以把函数放在等号的右侧,左侧放一个开好的空间,就可以把返回值保存起来。看代码:
int a;
a=init();      // 函数调用  
要调用一个函数,必须在调用函数的代码之前定义函数或声明函数。
定义函数 我们上面写的函数包括了函数名字和返回类型,和函数体,就是函数的定义。
声明函数 只写出函数名字,返回类型和参数(后面讲),不写函数体,叫做函数的声明,写法如下:
int init() // 注意没有 {} ,有;
为了函数调用方便,不用考虑前后关系,通常把函数的声明放在程序的最前面。只要有函数的声明,即使没有函数定义,调用该函数,编译也不会报错,但是在连接的时候,要加上含有该函数定义的 obj 或者 LIB, 连接才可以成功。
 
返回值 int 是函数的返回值类型 , 函数的返回值是函数在运行结束前返回到函数外边的值 , 是把函数里面的数据传到到函数外面的主要方法 . 例如 :
函数的返回值类型是 int , 所以开个 int 空间来存储。注意函数最后一句话 return 后面的数就是返回的值; return 不一定写在函数的最后面,但是运行到 return ,函数就结束运行,返回到调用函数的下一句。函数可以没有返回值,可以用 void 来修饰 , 如:
void init();
没有返回值,就可以不写 return 语句,如果需要结束函数,可直接调用 return , 后面不加任何内容,但是如果有返回值必须有 return 语句,而且 return 的数据类型必须和指定的返回值类型一致。
 
全局变量和局部变量
变量就是我们上节课讲的空间,在这个函数里我们开了如下空间:
Ball ba[9][9];
int num;
这两个变量就是局部变量。在函数里开的空间叫做局部变量,局部变量的特点是 :
1. 在函数执行期间,局部变量存在,函数结束后不再存在,如果第二次去调用函数,第一次调用时的空间里的内容都已经不存在了,因此需要把函数里变量的内容传到函数外面,一定要在函数结束前进行处理。
2. 局部变量有作用域,只有同一作用域的局部变量可以互相访问。通常一对 {} 里就是一个作用域,函数体本身在一对 {} 里,就是一个作用域,代码里面可能还包含多层 {} ,外层作用域的变量内层可以访问,内层作用域的变量外层不可以访问。
我们现在把 81 Ball 类型的空间放在函数里就是错误的,因为当函数运行完以后,这些空间就不能再进行访问,我们需在这些空间在其他的函数里也可以访问,必须把他们放在一个别的函数都可以访问的地方才可以。
全局变量 全局变量顾名思仪,在全部地方都可以访问的变量,这个变量定义在函数的外边,在所有的函数里都可以访问,当然,变量的声明要放在访问之前。我们存棋盘的数据用全局变量是最合适的,我们把 Ball 类型的二维数组改成全局变量。代码如下:
Ball ba[9][9];
int init()
{
      int num;
   ba[0][0].x=0;
   ba[0][0].y=0;
ba[0][0].isHaveBall=false;
ba[0][0].ballColor=0;
      ……
 
       for  循环语句 刚才的代码里给 81 个空间赋值,我们写了 81 遍,显然是不可取的,我们可以采用循环语句来把重复的代码去掉,注意我开始讲的,代码不能有重复,采用循环是消除重复的一个好办法。循环语句可一把同一条语句执行多遍,看代码:
       for(int i=0 i<9 i++)
{
      ba[0][0].x=0;
   ba[0][0].y=0;
ba[0][0].isHaveBall=false;
ba[0][0].ballColor=0;
}
看一下 for 语句的格式, for 是关键字,后面跟一对(),小括号后面是要重复的语句,只有一句话的时候,可以不加 {} ;多条语句的话一定要加 {} {} 后面不用加;,小括号里的内容分 3 段,用 2 个;隔开。
第一段,可以写一条语句,在循环之前被执行,只执行一次,这里我们开了空间 i, 并赋值为 0
第二段 是一个表达式,表达式就是我们在数学里常看到算式,有 + - * / 等算术运算符,还有 >,<,>=,<= 关系运算符 , 还有 &&,||,! 等逻辑运算符,这些运算符可以在一起使用,正常是从左往右计算,但不同的运算符的优先级不一样,像乘法高于加法,先计算优先级高的算式,函数的优先级有张表,记不清的时候可以查一下。算术表达式的结果是一个数字,这个数字用在逻辑运算时,数字为 0 就是 false, 数字不为 0 就是 true 。在这里,当表达式的结果为真,或者说不等于 0 ,表示条件成立,进入循环语句,继续执行,否则退出循环,循环开始之前和每次循环结束之后,都会进行这种判断。
第三段,又是一条语句,当循环的内容执行完之后,就要执行这条语句,我们通常在这里对循环变量进行修改,比如 i=i+1, ( 还可以写成 i++), 这样每循环一次 i 的值就增加 1 ,当 i 的值等于 9 ,正好循环了 9 遍,此时表达式的结果为 false ,循环结束。我们同常用 i ,做数组的下标,那么每次循环我们就可以访问不同的数组元素。
刚才的语句只是把同一个空间修改了 9 次,要给二维数组赋值,通常需要一个二重循环看代码:
for(int i=0 i<9 i++)
{    
       for(int j=0 j<9 j++)
       {
             ba[i][j].x=i;
          ba[i][j].y=j;
ba[i][j].isHaveBall=false;
ba[i][j].ballColor=0;
              }
}
注意里面的 i,j ,每循环一次, i j 的值就会变,每次循环执行的内容就会变.
参数   我们再回过头来看 init 函数,我们会发现一个问题,这个函数只能给全局变量 ba 代表的二维数组进行初始化,如果我还有一个同样的全局变量,或者一个同样类型的局部变量,这个函数就完全用不了了,我们就必须把类似的代码重写好多遍,这又违背了不重复的规定,如果我们的函数事先不指定二维数组,而是在调用的时候告诉函数一个指定的二维数组,那么这个函数就可以为这个指定的二维数组服务,如何把信息传递给函数呢,那就要用到参数,函数的参数就是给函数传递信息的主要途径。有了参数,函数的功能就会变的复杂,智能,能够适应更多场合,也就减少了更多的重复。
参数的定义 参数就是函数名后面小括号里的声明的变量 . 看个简单函数 :
int add(int a,int b);
{
       int c;
       c=a+b;
       return c;//return a+b; 这么写也可以 , 这里主要是为了说明参数的用法
}
a b 就是参数 . 那么参数如何使用 , 有什么特性 , 我总结了三句话 , 当你感到参数不会使用的时候 , 想想这三句话 , 可能就会有思路了 , 哪三句话呢 , 请看 :
1 .参数是函数内部的变量
2 .调用函数时 , 要按照参数的顺序给参数赋值
3 .传递数组的只传递数组的首地址 
解释一下 :
看代码,一个求 int 数组最大值的函数:
int getMax(int a[],int n)
{
       int k=a[0];
int i;
              for(i=0;i<n;i++)
              {
                     if(k<a[i])
                     {
                 k=a[i]
}
}
return k;
}
调用代码:
void main()
{
       int m[]={34,23,67,78,32,12};
       int k=0;
           k=getMax(m,6);
}
第一句 , 参数在使用起来 , 和使用函数内的局部变量是一样的 , 参数可以和局部变量互相访问 , 他们具有相同的作用域 . 可以参考上面的程序代码,数组 a int 空间 n 就是参数,在函数里可以
第二句说明了参数在调用函数时的传递过程,同时也说明了参数的重要作用 , 通过给参数赋值达到向函数内部传递信息。来看一段代码调用上面 add 函数的代码:
int m;
int n=3;
m=add(n,5);
在调用函数的时候, n 的值和常数 5 ,分别赋给了函数内的两个变量 a,b, 并开始执行函数内的代码。
第三句话,看函数 getMax 的定义,第一个参数是一个数组,调用时我们传入一个变量 m, 以前讲过, m 的含义是 &m[0], 既首元素的地址,我们在传递数组的时候,实际上传入了一个数组元素的地址,所以传递数组的时候还可以写成:
getMax(int *a,int n)
这种写法和刚才定义的函数功能是一样。
这时候,刚才的函数可以改成这样:
int init(Ball ba[9][9])
{
for(int i=0 i<9 i++)
    for(int j=0 j<9 j++)
    {
          ba[i][j].x=i;
       ba[i][j].y=j;
ba[i][j].isHaveBall=false;
ba[i][j].ballColor=0;
           }
}
}
   这个函数不再是对全局变量进行初始化,而是对传入的变量进行初始化,言外之义,就是能够应用到更多的场合,减少了代码的重复。
 
为了巩固上面三句话的效果,我特意准备了一些函数,让大家试着调用一下 , 把可能的调用形式都写出来。
void f1(char a);
void f2(char *a);
void f3(char a[]);
void f4(char *a[]);
void f5(char **a);
void f6(char (*a)[5]);
void f7(char a[][])
我先把一些空间定义出来,供大家调用时使用。
char m[5];
char *n[5];
char s[5][5];
n[0]=&m[0];
n[1]=&m[1];
n[2]=&m[2];
n[3]=&m[3];
n[4]=&m[4];
 
大家可以自己先练习一下,在调用的时候,不但参数要传对,还有去理解传递的过程,我把答案公布一下。
f1: f1(m[0]),f1(*n[0])
f2: f2(n[0])),f2(&m[0]),f2(m)
f3: f3(n[0])),f3(&m[0]),f3(m)
f4: f4(n),f4(&n[0])
f5: f5(n),f5(&n[0])
f6: f6(s),f6(&m)
f7: f7(s)
其中 f2 f3 f4 f5 是完全相同的。 f6 f7 是关于**数组的使用,大家可以参考一下
评论 68
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值