一篇入门级别的C语言函数,值得一看。
上次写了 C 语言入门级别的指针,这篇写函数,指针和函数在一起使用,更有意思,这也才能最大程度的发挥指针的作用。
基本概念
C 语言是一个函数语言,函数相当于”工厂“,具有”来料加工“的能力。
1sqrt(2.0);
2//sqrt()函数,接收 2.0 这个来料,并把它加工成 1.414...这样的结果。
1、
函数的分类
A、按 main() 和其它函数:主函数、其它函数
B、按作者分:系统函数(库函数)、用户自定义函数(我们自己写的);无论是库函数,还是用户自定义函数,其产生过程和调用,都应遵循相同的编程原则;
C、按函数调用关系分:主调函数、被调函数;
D、函数按参数个数划分:定参函数、不定参函数。
定参函数,参数个数固定;不定参函数,参数个数不确定;定参函数又分为有参函数和无参函数。
1printf("%d%d%d", a, b, c); //4 个参数
2printf("Hello\n"); //1 个参数
3//所以 printf() 是一个不定参函数
4//若函数无参,则可以用 void 加以明确声明
5void main(void){} //名称为 main 的函数,无返回值,无参数
2、
函数的产生和使用过程(步骤)
A、函数声明
B、函数定义
C、函数调用
函数声明:
(1)、确定的函数名称,函数名称是用以区分其它函数的标志,是这个函数的”入口“地址常量。
(2)、说明函数的返回值类型,sqrt(2.0) 的计算结果为 1.414,这个 1.414 就是所谓的返回值;
函数可以没有返回值,此时该函数返回值类型必须明确的写成:void;
返回值是由被调函数返回给主调函数的数值;函数若有返回值,最多只能有一个返回值;
(3)、说明函数的参数,参数个数以及每个参数的数据类型;函数在声明时,可以只说明参数类型,而缺省相关变量。
1int fun(int n);
2int fun(int);
//以上函数声明都是正确的
函数定义:
就是函数的编程,实现函数的功能,主要包含函数首部和函数体。
函数首部包括:函数返回值类型、函数名称、以及形参;
函数体是由 {} 括起来的语句组成。
1int fun(int m){}
注意:
(1)、若函数明确声明不是 void,则该函数必须以 {return 表达式;} 语句结束,并且,表达式类型必须与函数的返回值类型完全一致!
若函数返回值为 void,则可以写 return; 语句结束,也可以缺省这条语句。
特别强调:return (表达式); 这样的写法,在概念上是一种坏习惯,在 C 语言中,xxx() 出现,代表的是对 xxx() 函数的调用,上面的写法好像就是在说 return 是函数名称,而实际上,return 是关键字!
(2)、关于 return
A、它是函数运行结束的语句,在这条语句后出现的任何其它语句,是绝对不会运行到的。
1if (条件) {
2 return ;
3}
4语句1;
5//明显,只有条件不成立,才能执行语句1
6}
B、所谓的函数运行结束的 return(返回),是“返回”到调用它的函数的下一条语句继续执行。
C、所谓的 return 表达式; 意思是,返回到主调函数,并用“表达式”的值,替代主调函数中,关于这个函数的调用部分内容。
函数调用:
1#include <stdio.h>
2
3int add (int a, int b); //函数声明
4int add (int a, int b) { //函数定义
5 return a + b;
6}
7int main (void) {
8 int a;
9 int b;
10 int sum;
11
12 scanf("%d%d", &a, &b);
13 sum = add(a, b); //函数调用
14
15 return 0;
16}
形参与实参
1、
形参与实参的定义
A、形参出现在函数定义的“函数首部”的括号中;
B、实参出现在函数调用的括号中;
C、实参有三种形式:常量、变量、表达式;
D、形参只有一种形式:变量。
1int fun (int num);
2int fun (int num) { //这个 num 就是形参 这块是函数定义
3 ...
4}
5
6int main (void) {
7 int a;
8
9 a = fun(5); //这个 5 就是实参 这块是函数调用
10}
形参只有变量这一种形式,不可能出现常量,甚至不可能出现数组!
2、
(1)、形参与实参之间的关系
A、个数一致;
B、按位置关系必须对应;
C、数据类型必须一致;
D、值传递
将实参表达式的值,复制一份,赋值给形参变量!
(2)、私有变量
在一个函数的函数体中定义的变量,以及该函数的所有形参变量,都是这个函数的私有变量。
一个函数只能使用、调用这个函数的私有变量和“公有变量”,绝对不能直接使用其它函数的私有变量(包括数组)。
私有变量是在定义这个变量的函数被调用时,才申请存储空间;私有变量(尤其是形参变量)的空间,与其它函数的私有变量的空间,是没有任何关系的,即,其空间是独立申请的。
私有变量将随着定义它的函数的运行结束,而释放其空间。
(3)、形参不能修改实参的值
被调函数对形参变量所进行的任何修改,绝对不会更改与其对应的实参的值,这就是“值传递”的必然结果!!!
对形参变量所进行的任何修改,绝对不会更改实参表达式的值 !
C 语言中,“指针”存在的理由:
最核心的原因:C 语言是一个函数语言,没有过程这个概念,就需要指针传变量的地址,才能对变量进行修改!
函数特点
1、
C 语言函数的重要特点
A、高度的独立性:任何函数只能引用属于自己的私有变量和全局变量,绝对不能直接引用其它函数的私有变量;这意味着,不能在被调函数中,直接修改主调函数的私有变量的值;
B、函数返回值最多只有一个,若需要通过被调函数同时得到多个结果,那是行不通的;
C、函数的参数(实参与形参)的关系,在 C 语言函数中,只有一种:值传递(传递的是值,只不过这个值可能是普通的值,也可能是地址值),且这种传递方式所产生的必然结果是:对被调函数的形参变量所进行的任何更改,绝对不会影响主调函数的实参的值!
上述三个特点,将对 C 语言函数的功能产生巨大的束缚:在上述情况存在的前提下,C 函数是无法实现:“同时更改主调函数中多个私有变量的值”的功能!
2、
指针的概念,就是为了解决上述 C 语言“致命”问题而提出来的!
指针解决上述问题的具体方法:
A、将需要更改的变量(空间)的首地址作为实参;
B、被调函数对应的形参变量必须是上述变量的指针类型进行接收,这将使被调函数的形参变量“指向”主调函数中,需要更改的那个变量;
C、在被调函数中,通过 * (指向)运算,间接地修改主调函数相关变量的值。
1void fun (int *);
2void fun (int *num) {
3 *num = 5; //通过指针修改 a 空间的值,进行了指向运算
4}
5
6int main (void) {
7 int a = 6;
8
9 fun(&a); //传的是 a 空间的地址
10 printf("%d\n", a) //输出的结果是 5
11}
变量生命域与作用域
1、
全局变量与局部变量
(1)、全局变量的定义是在 {} 之外进行的;在 {} 之内定义的变量和所有的形参变量都是局部变量;
(2)、全局变量能被那些在书写顺序上,其后定义的函数所引用;局部变量只能被定义它的函数所引用;
(3)、全局变量的空间是在主函数运行之前申请的,且在主函数运行结束后(即,整个程序运行完毕后)才释放空间的;局部变量是在定义它的函数被调用时才申请空间,且随着函数运行结束而释放空间;
(4)、全局变量在定义后赋值前,其值由 C 语言强制赋值为 0;局部变量在定义后赋值前,其值为垃圾;
(5)、全局变量的使用,可使程序设计变得方便,但这种方便会带来函数之间“耦合”程度的增加,不利于大中型软件开发,不利于团队开发,也不利于软件的维护;全局变量的使用必须谨慎,这是《软件工程》思想所推荐的。
2、
变量存储类型
自动存储类:auto (可缺省)int a; <=> auto signed int
静态存储类:static
寄存器存储类:register
外部存储类:extern
(1)、自动存储类变量和静态存储类变量的定义:
1int a;
2static int b;
(2)、静态存储类变量是在主函数运行前申请空间,且在整个程序运行结束后释放;自动存储类变量是在定义它的函数被调用时才申请空间,且在定义它的函数运行结束后,释放其空间;
(3)、静态存储类变量在定义后未赋值前,其值被 C 语言强行赋值为 0;自动存储类变量在定义后未赋值前,其值为垃圾值;
(4)、静态存储类变量,如果是局部变量,则只能被定义它的函数所引用;
(5)、若某函数被多次调用,且该函数定义有静态存储类变量;则,在调用该函数结束后,它所定义的自动存储类变量的空间都被释放,而静态存储类变量的空间是不释放的;在下一次该函数被调用时,静态存储类空间的值是在上一次调用结束的基础上进行再一次引用的,因此,静态存储类变量具有“累加”效应。
函数遗留问题
一维数组名称作为实参、二维数组名称作为实参,形参该以何种数据类型接收的呢?又怎么使用呢?
函数的递归调用,也就是常见的递归算法有哪些条件呢?递归又该如何实现?有哪些应该的场景需要递归呢?
在操作系统底层源码中好多写法就是函数指针,指向函数的指针,那么指针函数和函数指针又该怎么理解呢?怎么使用呢?
针对这些遗留问题,有兴趣的可以自己先去思考、去学习,去研究,我在写 C 进阶之旅的时候,都会写到。
本篇文章仅仅是把 C 语言的函数带入门,以及说明了为什么要有指针,指针的出现就是为了函数中的传参,学习 C 语言之旅,路还很长,琢磨透彻,在敲代码实现,总会有进步的!