目录
一、函数调用详解(从中发现程序运行的秘密)
在 C 语言中,函数调用的方式有多种,例如:
1. 函数作为表达式中的一项出现在表达式中
2. z = max(x, y);
3. m = n + max(x, y);
4. 函数作为一个单独的语句
5. printf("%d", a);
6. scanf("%d", &b);
7. 函数作为调用另一个函数时的实参
8. printf( "%d", max(x, y) );
9. total( max(x, y), min(m, n) );
二、函数声明以及函数原型
C 语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。
所谓声明(Declaration) ,就是告诉编译器我要使用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我会把定义补上。
函数声明的格式非常简单,相当于去掉函数定义中的函数体,并在最后加上分号;如下所示:
dataType functionName( dataType1 param1, dataType2 param2 ... );
也可以不写形参,只写数据类型:
dataType functionName( dataType1, dataType2 ... );
函数声明给出了函数名、返回值类型、参数列表(重点是参数类型)等与该函数有关的信息,称为函数原型(FunctionPrototype) 。函数原型的作用是告诉编译器与该函数有关的信息,让编译器知道函数的存在,以及存在的形式,即使函数暂时没有定义,编译器也知道如何使用它。
有了函数声明,函数定义就可以出现在任何地方了,甚至是其他文件、静态链接库、动态链接库等。
//函数声明
int sum(int m, int n); //也可以写作int sum(int, int);
对于多个文件的程序,通常是将函数定义放到源文件(.c 文件)中,将函数的声明放到头文件(.h 文件)中,使用函数时引入对应的头文件就可以,编译器会在链接阶段找到函数体。
函数声明的例子:
三、全局变量和局部变量
1、局部变量:
定义在函数内部的变量称为局部变量(Local Variable) ,它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。 例如:
int love(int a)
{
int b,c; a,b,c仅在函数love()内有效
return a+b+c;
}
int main()
{
int m,n; m,n仅在函数main()内有效
return 0;
}
几点说明:
- 在 main 函数中定义的变量也是局部变量,只能在 main 函数中使用;同时, main 函数中也不能使用其它函数中定义的变量。 main 函数也是一个函数,与其它函数地位平等。
- 形参变量、在函数体内定义的变量都是局部变量。 实参给形参传值的过程也就是给局部变量赋值的过程。
- 可以在不同的函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰,也不会发生混淆。
- 在语句块中也可定义变量,它的作用域只限于当前语句块。
2、全局变量:
在所有函数外部定义的变量称为全局变量(Global Variable) ,它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。 例如:
int a, b; 全局变量
void func1()
{
//TODO:
}
float x,y; 全局变量
int func2()
{
//TODO:
}
int main()
{
//TODO:
return 0;
}
a、 b、 x、 y 都是在函数外部定义的全局变量。
四、C 语言变量的作用域
所谓作用域(Scope) ,就是变量的有效范围,就是变量可以在哪个范围以内使用。有些变量可以在所有代码文件中使用,有些变量只能在当前的文件中使用,有些变量只能在函数内部使用,有些变量只能在 for 循环内部使用。
变量的作用域由变量的定义位置决定,在不同位置定义的变量,它的作用域是不一样的。 本节我们只讲解两种变量,一种是只能在函数内部使用的变量,另一种是可以在所有代码文件中使用的变量。
1、在函数内部定义的变量(局部变量)
在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable) 。函数的形参也是局部变量,也只能在函数内部使用。请看下面的例子:
2、在所有函数外部定义的变量(全局变量)
在一个函数内部修改全局变量的值会影响其它函数,全局变量的值在函数内部被修改后并不会自动恢复,它会一直保留该值,直到下次被修改。
全局变量也是变量,变量只能保存一份数据,一旦数据被修改了,原来的数据就被冲刷掉了,再也无法恢复了,所以不管是全局变量还是局部变量,一旦它的值被修改,这种影响都会一直持续下去,直到再次被修改。
3、关于变量的命名
- 不同函数内部的同名变量是两个完全独立的变量,它们之间没有任何关联,也不会相互影响。
- 函数内部的局部变量和函数外部的全局变量同名时,在当前函数这个局部作用域中,全局变量会被“屏蔽”,不再起作用。也就是说,在函数内部使用的是局部变量,而不是全局变量。变量的使用遵循就近原则,如果在当前的局部作用域中找到了同名变量,就不会再去更大的全局作用域中查找。另外,只能从小的作用域向大的作用域中去寻找变量,而不能反过来,使用更小的作用域中的变量。
五、C 语言块级变量(在代码块内部定义的变量)
C 语言还允许出现单独的代码块,它也是一个作用域。
#include <stdio.h>
int main()
{
int n = 22; //编号①
由{ }包围的代码块
{
int n = 40; //编号②
printf("block n: %d\n", n);
}
printf("main n: %d\n", n);
return 0;
}
运行结果:
block n: 40
main n: 22
六、再谈作用域
每个 C 语言程序都包含了多个作用域,不同的作用域中可以出现同名的变量, C 语言会按照从小到大的顺序、一层一层地去父级作用域中查找变量,如果在最顶层的全局作用域中还未找到这个变量,那么就会报错。
七、C 语言递归函数(递归调用)
一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。
求n的阶乘
long factorial(int n)
{
if (n == 0 || n == 1)
{
return 1;
}
else
{
return factorial(n - 1) * n; 递归调用
}
}
八、递归的条件
每一个递归函数都应该只进行有限次的递归调用,否则它就会进入死胡同,永远也不能退出了,这样的程序是没有意义的。
要想让递归函数逐层进入再逐层退出,需要解决两个方面的问题:
- 存在限制条件,当符合这个条件时递归便不再继续。对于 factorial(),当形参 n 等于 0 或 1 时,递归就结束了。
- 每次递归调用之后越来越接近这个限制条件。对于 factorial(),每次递归调用的实参为 n - 1,这会使得形参 n的值逐渐减小,越来越趋近于 1 或 0。
标准 C 语言(ANSI C)共定义了 15 个头文件,称为“C 标准库”,所有的编译器都必须支持,如何正确并熟练的使用这些标准库,可以反映出一个程序员的水平。
- 合格程序员: <stdio.h>、 <ctype.h>、 <stdlib.h>、 <string.h>
- 熟练程序员: <assert.h>、 <limits.h>、 <stddef.h>、 <time.h>
- 优秀程序员: <float.h>、 <math.h>、 <error.h>、 <locale.h>、 <setjmp.h>、 <signal.h>、 <stdarg.h>