参考自:《C语言程序设计(第五版)》
一、定义函数
类型名 函数名(形参表列)
{
函数体
}
//形参表列可为空,不填或填void
//类型名表示函数返回值类型,无返回值填void。
二、调用函数
1、函数调用时的数据传递
1/ 形式参数和实际参数
在定义函数时函数名后面括号中的变量名成为“形式参数”或“虚拟参数”;
在主调函数中调用一个函数时,函数名后面括号中的参数为“实际参数”,可以是常量(如1)、变量(如a)、表达式(如a+b)。
e.g.
int max(int a,int b) //a和b是形参
{
return a + b;
}
int m = 1;
int n = 2;
int t = max(m,n); //m和n是实参
2/ 实参和形参间的数据传递
在调用函数过程中,系统会把实参的值传递给被调用函数的形参。或者说,形参从实参得到一个值。
该值在函数调用期间有效,可以参加该函数中的运算。
形参与实参的类型应相同或赋值兼容。
2、函数调用的过程
- 定义函数中指定的形参,在未出现函数调用时,他们并不占内存中的存储单元。
在发生函数调用时,函数的形参才被临时分配内存单元。 - …
- 调用结束,形参单元被释放。
三、声明函数
1/ 在一个函数中调用另一个函数需要具备条件:
- 被调用的函数必须是已经定义的函数(是库函数或用户自己定义的函数)。
- 若使用库函数,应在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中。
- 若使用用户自己定义的函数,而该函数的位置在调用它的函数的后面(在同一个文件中),
应在主调函数中对被调用的函数作声明。
2/ 声明的作用:
是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查函数是否合法。
程序进行编译时是从上到下逐行进行的,如果没有声明,当编译到调用的地方时,编译系统无法确定如上例的max是不是函数名,也无法判断实参的类型和个数是否正确,因而无法进行正确性的检查。
3/ 声明方式:
函数类型 函数名(参数类型1 参数名1,...,参数类型n 参数名n);
或
函数类型 函数名(参数类型1,...,参数类型n);
四、局部变量和全局变量
1、局部变量:
- 在函数内部定义的变量只在本函数范围内有效,在函数以外不能使用它们
- 在复合语句内定义的变量只在本复合语句范围内有效
float f1(int a)
{
int b,c; //a,b,b只在这个大括号内有效
...
}
int main(){
int m,n;
...
return 0; //m,n只在这个大括号里有效
}
注:
- 主函数中定义的变量(如m,n)也只在主函数中有效,并不因为在主函数中定义而在整个文件或程序中有效;
- 不同函数中可以使用同名的变量,它们代表不同的对象,互不干扰。
- 形参也是局部变量;
- 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。
int main()
{
int a,b;
...
{ //----------复合语句开始
int c;
c = a + b; //c在此复合语句内有效
...
} //----------复合语句结束
}
2、全局变量
程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。
在函数内定义的变量是局部变量,在函数之外定义的变量称为外部变量,外部变量是全局变量(也称全程变量)
全局变量可为本文件中其他函数共用。有效范围从定义变量的位置开始到本源文件结束。
注:
- 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。
所以建议不在必要时不要使用全局变量。
五、变量的存储方式和生存期
1.动态存储方式与静态存储方式
变量按作用域方面可分为:全局变量和局部变量
变量按变量值存在的时间(即生存期)可分为:静态存储方式和动态存储方式
- 静态存储方式:在程序运行期间由系统分配固定的存储空间的方式;
- 动态存储方式:在程序运行期间根据需要进行动态的分配存储空间的方式。
内存中供用户使用的存储空间可以分为3部分:
- 程序区
- 静态存储区
全局变量全部存放在这个区,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。
在程序执行过程中他们占据固定的存储单元,而不是动态进行分配和释放。
- 动态存储区
存放以下数据:
- 函数形式参数。在调用函数时给形参分配存储空间。
- 函数中定义的没有static关键字的变量,即自动变量。
- 函数调用时的现场保护和返回地址等。
在函数调用开始时分配动态存储空间,函数结束时释放这些空间。
六、变量的存储类别
在C中,每个变量和函数都有两个属性:数据类型和数据的存储类别。
局部变量的存储类别:
1、自动变量(auto变量)
函数的局部变量,若不专门声明为static存储类别,则都是动态地分配存储空间,数据存储在动态存储区。
int f(int a)
{
auto int b,c = 3; //定义b,c为自动变量,auto可以省略
...
}
2、静态局部变量(static)
-
静态局部变量属于静态存储类别,在静态存储区分配存储单元。在程序整个运行期间都不释放。
-
对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。
以后每次调用函数时,不再重新赋初值而只是保留上次函数调用结束时的值。
而对于自动变量赋初值,不是在编译时进行的,而是在函数调用时进行的,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
-
若在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符’\0’(对字符变量)。
而对自动变量,它的值是一个不确定的值,这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的内容是不可知的。
-
静态局部变量依旧是局部变量,只能被本函数引用。
3、寄存器变量(register变量)
一般变量的值是存放在内存中的,当程序用到哪一个变量时,由控制器发出指令将内存中该变量的值送到运算器中。
可以将一些使用频繁的局部变量(如一个函数中执行10000次循环,每次循环都要引用某局部变量,则为存取变量的值要花费不少时间),将局部变量的值放在CPU的寄存器中,需要用时直接存寄存器取出,不必到内存中去存取,可以提高执行效率。
因为对寄存器的存取速度要远高于对内存的存取速度。
register int f; //定义f为寄存器变量
全局变量的存储类别:
1、extern作用域扩展
extern对外部变量作“外部变量声明”,表示把该外部变量的作用域扩展到此位置。
因为外部变量是在函数外部定义的全局变量,作用域是从变量的定义处开始,到本程序文件的末尾。
1/ 如在第10行定义了外部变量,但是在同文件的第五行处的一个函数里想用这个变量,则要声明;
如果这个变量的定义写在了文件的开头则不需要再声明;
2/ 如果一个文件想用另一个文件的外部变量,则用extern声明。
A文件:
int A;
...
B文件:
extern A;
...
注:
把建立存储空间的声明称为定义,不需要建立存储空间的声明称为声明。
所以extern是声明。
2、static作用域限定
加了static的外部变量,称为“静态外部变量”,只能用于本文件。
回顾总结static:
- 对局部变量用static,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在;
- 对全局变量用static,则该变量的作用域只限于本文件模块(即被声明的文件中)。
七、内部函数和外部函数
1.内部函数:只能被本文件中其他函数所调用的函数。(又称静态函数)(static)
//定义内部函数时,前面加上static。
static 类型名 函数名(形参表);
//与其他文件中内部函数同名也互不干扰。
2.外部函数:可供其他文件调用。(extern)
//定义外部函数时,前面加上extern。(extern可省略,默认为外部函数)
函数首行:extern int fun(int a,int b);
在另一个文件想使用这个函数时,需要先声明,要加extern关键字,表示该函数是在其他文件中定义的外部函数。