文章目录
第七章:函数
7.1-函数概念及分类
函数相关概念
-
函数是c语言的功能单位,实现一个功能可以封装一个函数来实现
-
一个C程序可由多个源文件组成
-
一个源文件可由一个或多个函数组成
-
编译时以一个源文件为单位进行编译,而不是以函数为单位进行编译
-
C程序的执行总是以main()函数开始,在main()中结束整个程序的运行
函数分类
从定义角度,C语言可分类为:
- 库函数(C库实现)
- 自定义函数
- 系统调用(操作系统实现的函数)
从用户角度,C语言可分类为:
-
库函数:C语言提供了丰富的库函数,使用时必须用
#include <头文件> ceil(x),对x向上取整 floor(x),对X向下取整 abs(x),求x绝对值 fabs(x),对浮点数x求绝对值 sqrt(x),x的1/2次幂 exp(x),求e的x次幂
- 用户自定义函数:用户为解决专门问题而编写的函数
- 无参函数:在调用时无参数传递(特例):
int fun(void)
{}
int fun()
{}
- 有参函数:在调用时,主调函数与被调函数间有数据传递
int fun(int a,float b,double c)
{}
- 返回值函数
char fun()
{
char b='a';
return b;
}
fun() #定义函数时如果没有定义函数返回值类型,则默认返回整型
{
return 1;
}
- 无返回值函数
void fun(形参表)
{
return; # 不用return也可以结束调用
}
void fun(void) # 无返回值,无参函数
7.2-定义函数
- 形参说明格式:类型名 形参名
- 形参可有可无,但是()不能省略。如果有形参,形参必须带类型。
- 函数的定义不能嵌套
7.3-函数调用
- 带返回值函数:根据返回值的类型,需要在主调函数中定义一个对应类型的变量,接返回值
变量 = 函数名(实参列表);
mun = max(4,8);
- 不带返回值函数:不需要接收返回值
函数名(实参列表);
void fun()
{
return 1;
}
int main()
{
fun(); # 直接调用
}
- 有无形参
函数名(实参列表); //带参数
函数名(); //没有参数
注意:实参可以是常量,变量,表达式;形参是被调用函数的局部变量
函数调用时的数据传递
- 形参:在函数定义中出现的参数可以看做是一个占位符,形式参数只能等到函数被调用时才开辟空间,接收传递进来的数据,所以称为形式参数,简称形参。调用结束,空间释放。
- 实参:函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。
- 形参和实参的区别
- 形参变量只有在函数被调用时才会分配内存空间,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
- 实参可以是常量、变量、表达式、函数
- 实参和形参在数量、类型、顺序上必须严格一致
- 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参
- 函数调用时的参数传递
- 参数传递的步骤:
- 1.根据形参的声明格式,为每个形参分配存储空间
- 2.将实参的值赋给对应的形参
- 传递的值分为两种:普通值和地址值
- 赋值(普通值)调用:将实参的值传递给函数的形参,函数中形参的改变对实参没有影响
- 赋址(地址值)调用:将实参的地址值传递给形参。通过__共享存储空间__的方式改变主调函数中变量的值
- 参数传递的步骤:
return返回值作用
- return语句的作用:立即从所在的函数中退出,返回到主调函数,并将return语句后表达式的值作为函数值返回给主调函数,不需要返回值可不用return语句
- return形式:
7.4-函数声明(函数原型)
-
定义:对已经定义的函数,进行说明.函数的声明可以声明多次,但是定义只有一次
-
需要声明的情况:
1.当主调函数和被调函数在同一个.c文件中的时候
- 被调函数在上,主调函数在下,这种情况不需要声明
void fun(void) { printf("hello world\n"); } int main() { fun(); }
- 被调函数在下,主调函数在上,这种情况需要声明
int main() { fun(); } void fun(void) { printf("heool world\n"); }
-
声明方法:
- 直接声明法:将被调用的函数的第一行拷贝过去,后面加分号
void fun(void); # 直接声明法 int main() { fun(); } void fun(void) { printf("heool world\n"); }
- 间接声明法:将函数的声明放在头文件中, .c程序包含头文件即可
注意:自己创建的头文件用双引号,尖括号是直接到系统默认的路径下寻找库文件;双引号先到当前目录下找,找不到后再去系统默认的路径.
2.主调函数和被调函数不在同一个.c文件中的时候
- 声明的方法:
- 直接声明法:将被调用函数第一行拷贝过去,后面加分号,前面加extern
- 间接声明法:将函数的声明放在头文件中, .c程序包含头文件即可
-
一个分文件对应一个头文件,最后如果在主文件调用分文件的函数,只需要在主文件声明分文件的头文件即可,使得文件更加模块化
7.5-函数的嵌套使用
- 调用函数时,被调函数里面又调用其他函数
7.6-函数的递归调用
- 定义:如果一个函数直接或间接地调用自己,则称为函数的递归调用
- 特点:递归函数的特点是在函数内部直接或间接调用该函数本身
- 类型:直接递归,间接递归
- 缺点:巨大的时间开销和内存开销
7.7-数组作为函数参数
- 数组元素只能作函数实参,不能用作形参。由于形参临时分配单元,而数组元素单独分配储存单元。
- 数组元素做函数实参传递给形参时时以“值传递”方式,单向传递
- 数组名作函数实参传递给形参时以“地址传递”方式,同时也可用作__形参__
- 形参数组大小(二维数组的第一维)可不指定
- 形参数组名是地址变量
7.8-变量的作用域和存储类
变量的作用域
- 定义:变量的作用域即变量在程序中的有效范围
- 分类:局部变量、全局变量
- 局部变量:在函数内部、复合语句内部定义的变量,函数的形参也叫局部变量
- 全局变量:函数外部定义的变量
- 局部变量的作用域从定义的位置起,到函数体(或复合语句)结束为止
- 全局变量的作用域是从从定义位置的开始,到整个源程序结束为止
- 不同函数中可使用同名的变量,但代表不同的对象,在内存中所存储的单元也不同
- 局部变量有效范围重叠,则有效范围小的优先;全局变量和局部变量同名时,局部变量优先
7.9-变量的存储方式和生存期
动态存储方式和静态存储方式
- 变量的分类
- 从作用域分:全局变量、局部变量
- 从变量值存在的时间角度分:静态存储方式和动态存储方式
- 静态存储方式:指在程序运行期间由系统分配固定的存储空间的方式,程序运行期间不释放
- 动态存储方式:指在程序运行时根据实际需要临时分配存储空间,离开即释放
- 变量的两种属性:变量的数据类型和变量的存储类别
- C语言的内存映像:
- C语言内存分配方式:
- 在静态存储区分配:程序的全局变量和静态变量都在静态存储区上分配,且在程序编译时就已经分配好了,在程序运行期间始终占据这些内存,仅在程序终止前,才被操作系统收回
- 在栈上分配:在执行函数调用时,系统在栈上为函数内的局部变量及形参分配内存,函数执行结束时,自动释放这些内存。栈内存分配运算内置于处理器的指令集之中,效率很高,但是容量有限。如果往栈中压入的数据超出预先给栈分配的容量,那么就会出现栈溢出,从而使程序运行失败
- 在堆上分配: 在程序运行期间,用动态分配函数来申请的内存都是从堆上分配的。动态内存的生存期由程序员自己来决定,使用非常灵活,但也最易出现内存泄漏等问题。为防止内存泄漏的发生,程序员必须调用free() 释放已不再使用的内存
局部变量的存储类别
-
auto变量:函数中的形参和局部变量,如果不专门声明为static存储类别,都是动态地分配存储空间(栈),数据存储在动态存储区中。函数内定义的局部变量,默认为auto
- auto生存期:进入函数体(或复合语句)时生成,退出所在函数体(或复合语句)时消失。再次进入时,另行分配
-
static局部变量:在局部变量定义前加static关键字,则将该变量声明为静态局部变量,存储在静态数据区
- static生存期:贯穿于整个程序运行期间
- 内存赋予:在初始编译时初始化,调用结束后不消失
-
register寄存器变量:正常情况下,变量存放在内存中,CPU每次使用数据要从内存中读取。将局部变量的值放在CPU寄存器中,需要时直接取出参加运算,无需从内存中读取,这种变量称寄存器变量
-
只有自动变量、形式参数可作为寄存器变量,全局变量不能(即动态存储)
-
现在的优化编译系统能自动识别使用频繁的变量,自动存放在register中,不必要特别声明
-
由于寄存器变量使用的是CPU中的寄存器,寄存器存储变量没有地址,不能使用“&”求其地址
-
常用于循环控制变量,发挥存取速度快的优点
-
-
extern外部变量:静态存储
- 在一个文件内声明外部变量:若全局变量定义的位置不在开头,而在该定义点之前的函数想引用该变量,则在引用之前必须用extern将其声明为外部变量
extern float A;
- 在多文件的程序中声明外部变量:使得全局变量除了被源文件引用外,还可被其他文件使用。在任意文件中定义该全局变量,在其他文件中用extern将其声明为外部变量
-
静态全局变量:当需要将全局变量限制为只在定义它的源文件内使用而不能被其他源文件使用时,在定义时加上static将其申明为静态全局变量
7.10-关于变量的声明和定义
-
定义:建立存储空间
int a;
-
声明:不需要建立存储空间的声明
extern int a; <=> extern a; int main() { extern int A; //声明A是一个已经定义了的外部变量 dosth(); //执行函数 } int A;//定义A为整型的外部变量(全局变量)
-
外部变量
- 定义:只有一次在所有函数之外
- 声明:可有多次,既可在函数之内,也可在函数之外
- 存储单元的分配及初始化:初始化只在定义中进行,声明只是为了引用该变量
-
-
用static声明变量的作用:
- 局部变量用static:改变存储方式,执行期间不释放内存单元
- 外部变量用static:改变作用范围,作用域限于本文件模块中
7.11-内部函数和外部函数
根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数
内部函数
-
定义:又称静态函数,只能在本文件中被调用
-
格式:
static 类型标识符 函数名(形参表){....} static int f(void)
外部函数
-
定义:可供其他文件调用的函数
-
格式:
extern 类型标识符 函数名(形参表){....}
-
如果在定义函数时省略extern,则隐含为外部函数