文章目录
函数
C语言允许我们将常用的代码以固定的格式封装成一个独立的模块,需要这个模块完成某个特定任务时就可以调用这个模块,这个模块就叫函数
函数是完成特定任务的程序独立代码单元,以可以被重复使用的代码块
函数的好处
- 可以避免编写重复功能的代码
- 函数让程序更加模块化,提高了程序代码的可读性,便于后续修改和完善
函数定义
函数定义就是函数体的实现,函数体就是封装成模块的代码单元,只有在函数被调用时,函数体才执行
datatype functionname(typaname1 param1,typename2 param2 ... )
{
函数体;
}
函数声明与函数原型
函数声明是向编译器提供函数的相关信息,用于确保函数能够被正确的调用
函数声明可以分为旧式风格和新式风格
旧式风格声明
旧式风格定义的函数,即K&R C,不声明任何形参,用单独的列表来给定参数的类型,在参数体列表和函数体的左花括号中间,即编译器只会记住返回值类型,不保存参数列表的信息
旧式风格声明
int* oldStyleFunc(param1,param2...) int param1;int param2;
{
这种形式的声明新标准仍然支持,为了支持较老的程序无需修改也能通过编译
新式风格声明
ANSI C标准要求在函数声明时还要声明变量的类型,即使用函数原型
函数原型声明了函数名、函数的返回类型、参数的数量和参数类型
编译器找到函数原型之后就会去检查该函数的调用,确保参数数量和类型、返回值匹配成功,若发现不匹配的实参和返回值,则编译器会将不匹配的实参和返回转换成正确的类型,但转换必须是可行的
函数的组成
函数由函数名、函数参数、函数类型(函数返回值)、函数体组成
函数参数
在C语言程序中,函数的参数出现在两个地方,一个是函数定义处,另一个是在函数调用处
在函数定义处出现的函数参数叫做函数形参
在函数调用处出现的函数参数叫做函数实参,当函数发生调用时实参会将值传递给形参
形参和实参的区别
- 函数形参只有在 函数被调用时才会分配内存,函数结束后就会释放内存,形参的作用域仅在函数内部
- 实参和形参在参数数量和类型以及参数顺序上要进行统一,否则在调用时会发生”参数不匹配“的情况,导致函数正确被调用
- 函数调用过程中数据传递是单向的,只能是实参将值传递给形参,传值方式传递参数,在调用函数 内改变形参是不会影响实参
#include <stdio.h>
int addFun(int x,int y)
{
x += 5;
y += 10;
printf("被调函数 x=%d,y=%d\n",x,y);
return x+y;
}
int main()
{
int a = 5,b = 10;
printf("函数调用前 a=%d,b=%d\n",a,b);
int ret = addFun(a,b);
printf("函数调用后 a=%d,b=%d\n",a,b);
return 0;
}
![实参与形参的区别](https://cdn.jsdelivr.net/gh/babybeekeeper/pic_Hosting@master/20211114/实参与形参的区别.5qray1t2fmw0.png)
函数返回值
函数返回值是指函数被调用之后,执行函数体代码之后所得到的结果,通过关键字return语句返回结果值
函数返回值可以分为函数有返回值和没有返回值
1、没有返回值的函数的返回值类型为空类型
2、具有返回值的函数,则用return 关键词修饰的结果值必须是可以转化为函数定义时返回的类型才不会发生错误
return语句
被调函数的函数体内部可以有多个return语句,但始终只有一个return语句被执行,函数只要遇到return语句就会停止执行,所有下面的语句就不会执行
递归函数
递归函数:函数在函数体内调用函数自身
执行递归函数就是反复调用函数自身,每调用一次就进入一次新的函数体内部,当最内层函数执行完毕后再一层一层由里到外退出
#include <stdio.h>
//求n的阶乘
long factorial(int n) {
if (n == 0 || n == 1) {
return 1;
}
else {
return factorial(n - 1) * n; // 递归调用
}
}
int main() {
int a;
printf("Input a number: ");
scanf("%d", &a);
printf("Factorial(%d) = %ld\n", a, factorial(a));
return 0;
}
![递归函数](https://cdn.jsdelivr.net/gh/babybeekeeper/pic_Hosting@master/20211114/递归函数.2s9b979852k0.png)
递归函数条件
使用递归函数的时候必须要明确退出的条件,否则函数无限制调用会导致程序的崩溃
1、以n的阶乘为例,限制条件就是n ==0或n ==1,因为只有1的阶乘就是1
2、每次递归函数的调用应当逐渐靠近这个限制条件,例是将n-1作为递归函数参数传递,每调用一次递归函数就越靠近n ==0或n ==1这个限制条件
递归函数的缺点
递归函数在解决某些特定问题的效果很好,但是递归函数这种形式是有很明显的缺点,所以要谨慎使用递归函数
空间开销
在程序运行期间,内存区域叫栈是用来给调用函数来分配内存的,递归函数的每一次调用都会在开辟栈帧,所以递归函数递归次数过多导致栈空间的溢出而导致程序崩溃
时间开销
递归函数的每一次调用都会栈上分配内存,退出时又会释放内存,并且在函数调用期间会修改寄存器上的值,所有递归次数过多会导致频繁的分配和释放内存,以及多次对寄存器的读写操作,这样会降低程序的运行效率
变量的作用域
作用域就是变量的有效范围。变量的作用域由变量的定义为位置决定,在不同的位置定义的变量,作用域是不一样的
在程序中变量根据定义位置可以分为在函数内定义(局部变量),在函数外定义(全局变量)
局部变量
局部变量即在函数体内定义的变量,作用域也仅限于函数体内,只能在函数体内进行使用
形参也是局部变量,只能在被调函数体内使用
main函数也是函数,在main函数体内定义的变量也是局部变量
全局变量
C允许在所有函数体外定义变量,也叫全局变量
全局变量的作用域是整个程序,全部变量只保留了 一份数据,一旦函数修改,其他函数使用相同的全局变量时数据也被修改了
块级变量
C语言允许在代码块内部定义变量,这样的变量具有块级作用域,即只能在代码块内部定义的变量只能在代码块内部使用
#include <stdio.h>
int func(int a, int b)
{
//若a<b,那么交换两变量的值
if(a < b){
int temp1 = a; //块级变量
a = b;
b = temp1;
}
//求最大公约数
while(b!=0){
int temp2 = b; //块级变量
b = a % b;
a = temp2;
}
return a;
}
int main(){
printf("最大公约数:is %d\n", func(100, 60));
return 0;
}
![块级变量](https://cdn.jsdelivr.net/gh/babybeekeeper/pic_Hosting@master/20211114/块级变量.3w3xlf85mz20.png)
循环条件变量
遵循 C99 标准的编译器允许在 for 循环条件里面定义新变量,这样的变量也是块级变量,它的作用域仅限于 for 循环内部
#include <stdio.h>
int sum(int m, int n)
{
int sum = 0;
for(int i=m; i<=n; i++){ //i是块级变量
sum += i;
}
return sum;
}
int main(){
printf("求和(1~100)=%d\n", sum(1, 100));
return 0;
}
![循环条件变量](https://cdn.jsdelivr.net/gh/babybeekeeper/pic_Hosting@master/20211114/循环条件变量.6khwii8p3gs0.png)
变量的命名
C语言规定在同一作用域内不能出现相同的变量名,会发生命名冲突,但在不同的作用域中,允许出现相同的变量名,就是因为作用域不同,其作用范围也会不同,不会产生冲突
#include <stdio.h>
void func() {
int a = 5;
printf("func函数作用域内:a=%d\n",a);
}
int main() {
int a = 10;
printf("main函数作用域内:a=%d\n",a);
func();
return 0;
}
![变量的命名](https://cdn.jsdelivr.net/gh/babybeekeeper/pic_Hosting@master/20211114/变量的命名.x3togcer0o0.png)
总结
C程序就是由各式各样功能的函数组成,函数的定义声明的方式,变量根据是否在函数体内部可以分为局部变量和全局变量,随着定义在不同位置的变量会产生不同的作用域效果
赶紧学习起来吧!我是一个正在努力找回自我的人,希望能和一起学习的人成长,有错误的地方请各位大佬帮忙指正,如果觉得有帮助就点个赞当作对我的一个小肯定❤,peace&love