【C语言】——详解函数
一、函数的概念
函数是什么?相信大家对函数并不陌生,早在小学我们便接触了这个词,函数是数学中重要的组成部分。那么C语言中的函数,又会是怎样的呢,下面就让我们一起来学习一下C语言中的函数。
在C语言中,函数又叫:子程序,子程序这种叫法或许更准确一些。C语言中的函数就是一个完成某项特定的任务的一小段代码
。正如搭积木一样,一件作品是由一块块积木堆叠而成,而整个C语言的程序其实是由无数个小的函数组合而成。
C语言中,常见的有两类函数
- 库函数
- 自定义函数
二、库函数
2.1、库函数是什么
程序员编写代码时,会发现一个问题:大家总会频繁地用到某些功能:比如:从键盘输入信息;将内容输出到屏幕上;计算一个数的 n n n次方等等。这些频繁使用的功能,每次实现起来都要敲一大段代码,难免太过繁琐,且效率低下,那么如何解决呢?
C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数;C语言的国际标准ANSI C规定了常用函数的标准(函数名、参数是什么、功能是什么),被称为标准库
,那不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列的函数的实现。这些函数就是库函数。像我们常用的printf、scanf等就是库函数。
编译器的标准库中提供了一系列的库函数,这些库函数根据功能的划分,都在不同的头文件中进行了声明:库函数相关头文件。
库函数的学习不用着急一次性学会,我们应该做的是:循序渐进,一点一点逐个击破。
2.2、库函数的学习方法
库函数的学习和查看工具很多,我这里推荐两个:
例 :我们搜索sqrt函数
库函数是在标准库中对应的头文件中声明的,所以库的使用,务必包含对应的头文件
。例如:使用printf函数时要包含头文件<stdio.h>
。
小贴士:我们不要看到网站的英文就被吓死,谈英语色变。要变成一种学习性的思维,要让英语为我所用。并且官方文件的用词不会太生僻,满满看慢慢学,总能学懂。
三、自定义函数
当然,库函数值包含了最基本的内容,要想写出具有创造性的代码,自定义函数可是必不可少。自定义函数和库函数一样,有函数名,返回值类型和函数参数。
3.1、函数的语法形式
ret_type function_name(形式参数)
{
函数体
}
- ret_type是函数的返回类型,有时返回类型可以是void,表示什么都不返回。
- function_name是函数名,函数名尽量要根据函数的功能起的有意义。
- 括号中放的是形式参数,函数的参数也可以是void,明确表示没有参数。如果有参数,要交代清楚参数的类型和名字,以及参数个数。
- 大括号括起来的是函数体。
3.2、函数的实例
例子 :写一个加法函数,完成两个整型变量的加法操作:
#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
//输入
scanf("%d %d", &a, &b);
//完成加法函数,完成a和b的相加
//求和的结果放在r中
int r = Add(a, b);
printf("%d", r);
//输出
return 0;
}
当然,Add函数可以进一步简化
int Add(int x, int y)
{
return x + y;
}
虽然加法的功能也可以在主函数中实现,但我们不妨想一想,当需要用到多次加法功能时,用函数无疑会使代码更简洁,同时逻辑也更加清晰,可读性更高
。
3.3、 函数的参数
(1)实际参数
顾名思义,实际参数就是
在函数调用时传递给函数的实际数值
,简称实参。
上述代码中,传递给Add函数的参数 a 和 b 就是实参
(2)形式参数
上述代码中,函数Add括号中的 x 和 y 称为形式参数,简称形参。
为什么叫形式参数呢?实际上,如果只定义了Add函数,而不去调用,Add函数的参数 x 和 y 只是形式上存在,不会向内存申请空间,不会真实存在
,所以叫形式参数。
(3)实参与形参的关系
我们不禁会想,实参和形参有什么联系呢,别急,让我们通过代码调试来一探究竟。
#include<stdio.h>
void swap(int x, int y)
{
//创建一个临时变量
int temp = 0;
//把x的值赋给temp
temp = x;
//把y的值赋给x
x = y;
//把temp的值赋给y,完成交换操作
y = temp;
}
int main()
{
int a = 0;
int b = 0;
printf("请输入:>");
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
//交换函数
swap(a, b);
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
运行结果:
???
为什么使用了交换函数后两个值并没有被交换呢?别急,让我们通过调试看看。
通过调试,我们发现,x 和 y 确实得到了 a 和 b 的值,但也仅仅只是得到而已,他们只是数值相同,地址是完全不同的,他们是两个不同的值,在两个独立的内存空间互不影响。
简单来说:形参是实参的一份拷贝,形参的修改不会影响实参
注:实参和形参的名字可以相同,但依然不是同一个地址。
四、 return语句
对于return语句,相信大家都不陌生,毕竟最简单的main函数后面总跟着一个return 0, 这里我来讲一下return语句的注意事项:
- return后面可以是一个
数值
,也可以是一个表达式
,如果是表达式则先执行表达式,在返回表达式的结果。- return后面也可以什么都没有,直接写
return;
。这种写法适合函数返回类型是void的情况。- return返回的值和函数返回类型不一致时,系统会自动将返回的类型的值隐式转换为
函数的返回类型
。- return语句执行后,函数就
彻底返回
,后面的代码将不再执行
。- 如果函数中存在 if 等分支语句,则要保证
每种情况下要都有return
,否则会出现编译错误。
五、 数组做函数参数
在调用函数解决实际问题时,我们难免会将数组作为参数传递给函数,让我们看看函数内部是如何对数组进行操作的
例: 设计函数,将数组的值都变为0,并打印
#include<stdio.h>
void change_arr(int arr2[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
//设置数组内容为0
arr2[i] = 0;
}
}
void print_arr(int arr3[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
//依次打印数组元素
printf("%d ", arr3[i]);
}
printf("\n");
}
int main()
{
int arr1[9] = { 1, 2,3,4,5,6,7,8,9 };
int sz = sizeof(arr1) / sizeof(arr1[0]);
change_arr(arr1, sz);
print_arr(arr1, sz);
return 0;
}
运行结果会是怎样的呢?会不会像刚才那样并不会改变数组的值呢?让我们一起来看一下运行结果:
显然,它变了。为什么呢?踩过上面的坑后,数组不是应该不变的吗?难道他们是同一个数组?让我们调试看看。
通过调试,我们发现:他们地址相同,是同一个数组,数组传参,形参是不会创建新的数组的
。
小贴士:数组传参是,传的是数组名,例如上面是arr1,不要传arr[9],它表示该数组中下标为9的元素,而不是数组。
六、嵌套调用和链式访问
6.1、函数的嵌套调用
前面我们学习了C语言的分支和循环,了解了他们的嵌套使用,那么函数的嵌套调用又会是怎样的呢,让我们看下面的代码:
int main()
{
printf("hello world\n");
return 0;
}
此时的你或许在想:这不就是一个C语言最简单的代码吗,哪来的函数嵌套,这作者是不是在逗我?
其实,这个简单的函数就包含了函数的嵌套调用,它是main函数中嵌套了printf函数。
当然,这只是一个最简单的例子,未来的稍微大一点的代码都是函数之间的嵌套调用。不过需要注意的是函数是不能嵌套函数定义的
。
6.2、函数的链式访问
链式访问简单来说就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。
例子:
#include<stdio.h>
int main()
{
//将strlen函数的返回值直接作为printf函数的参数
printf("%d\n", strlen("abcdef"));//链式访问
return 0;
}
运行结果:
下面我们来看一个有趣的代码,看看它的运行结果是什么
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
运行结果:
为什么是“4321”呢?这段代码的关键就是弄懂printf函数的返回值是什么
printf函数的返回值是打印在屏幕上的字符个数
第三个printf打印43,在屏幕上打印两个字符,再返回2
第二个printf打印2,在屏幕上打印1个字符,再返回1
第一个printf打印1
最终打印:4321
七、函数的声明和定义
不知道细心的你发现了没有,在前面我写函数时,都是把函数放在主函数之前,即函数的定义在函数的调用之前。那么我们不妨想一想,如果我们想调用函数再进行定义会怎么样呢。
让我们来看下面一段代码:
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int r = Add(a, b);
printf("%d", r);
return 0;
}
int Add(int x, int y)
{
return x + y;
}
运行结果:
在VS2022上编译会出现上述警告信息
为什么会说Add函数未定义呢?原来,C语言编译器对源代码进行扫描时,从第一行开始,从上往下扫描,当发现Add函数的调用时,并未发现该函数的定义,编译器不知道这是啥,自然就发出了上述警报
要解决这个问题,就要在调用该函数之前告诉编译器有这个函数。这个就是函数声明,函数声明就是告诉编译器这个函数的函数名
、函数的返回类型
、函数的参数
是什么。
下面是参考代码:
#include<stdio.h>
//函数的声明
int Add(int x, int y);
//主函数
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
//函数的调用
int r = Add(a, b);
printf("%d", r);
return 0;
}
//函数的定义
int Add(int x, int y)
{
return x + y;
}
注:函数的调用一定要满足先声明后使用
;同时,函数的定义也是一种特殊的声明
。
好啦,本期关于函数就介绍到这里啦,希望本期博客能对你有所帮助,同时,如果有错误的地方请多多指正,让我们在C语言的学习路上一起进步!