先赞后看,不足指正!
这将对我有很大的帮助!
所属专栏:C语言知识
阿哇旭的主页:Awas-Home page
目录
引言
1. 函数的概念
2. 函数的类型
2.1 库函数
2.2 自定义函数
2.2.1 语法
2.2.2 举例
3. 函数的参数
3.1 实参
3.2 形参
3.3 实参与形参的关系
4. 数组作为函数的参数
5. return 语句
6. 嵌套调用和链式访问
6.1 函数的嵌套调用
6.2 函数的链式访问
7. 函数的声明和定义
7. 1 单个文件
7.1.1 函数定义在函数调用前
7.1.2 函数定义在函数调用后
7.2 多个文件
8. 结语
相信大家对函数的概念并不陌生,在我们数学学习中经常穿插着这个概念,比如,一次函数y=kx+b,k和b都是常数,我们给一个任意的x,就可以得到y的值。
那么,话不多说,我们一起来看看吧!
在C语言中引入了函数(function)的概念,也称作子程序,而构成C程序的基本单元是函数,函数中包含程序的可执行代码。同时一个函数如果能完成某项特定任务的话,这个函数也是可以复用的,提升了开发软件的效率。
我们可以举一个例子让大家更好地理解函数:
例如盖一栋楼房,在工程师的指挥下,有工人搬运建造材料,有工人建造房屋结构,有工人粉刷内外墙壁,各司其职。编写程序与盖楼的道理是差不多的,主函数就像工程师,控制每一步程序的执行,而子函数就像工人,分别去完成自己的工作。
#include
// 执行搬运功能
void Move()
{
printf("搬运建筑材料");
}
// 执行建造功能
void Build()
{
printf("建造房屋结构");
}
// 执行粉刷功能
void Paint()
{
printf("粉刷内外墙壁");
}
int main()
{
Move(); // 执行搬运函数
Build(); // 执行建造函数
Paint(); // 执行粉刷函数
return 0;
}
C语言标准中规定了C语言的各种语法规则,C语言并不提供库函数;C语言的国际标准ANSI C规定了一些常用的函数的标准,被称为标准库,那不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列函数的实现。这些函数就被称为库函数。
比如,我们经常使用的 printf 、scanf 都是库函数,而库函数也是函数,通常使用库函数都需要包含对应的头文件,不要忘记哦!
库函数的学习和查看工具很多,比如:
(1) 了解更多库函数头文件
(2) cplusplus.com
自定义函数是指由程序员自己编写的、用于执行特定任务的函数。而自定义函数更加灵活,也能给程序员写代码更多的创造性。
自定义函数和库函数是一样的,都有函数返回类型、函数名、形式参数以及函数体,具体形式如下:
datatype fun_Name(形式参数)
{
fun_body
}
自定义一个加法函数,完成两个整形变量的相加操作:
#include
int Add(int x, int y)
/*int ->函数返回类型
* Add ->自定义函数名称
* a、b是形式参数,均是int型
*/
{
int sum = 0;
sum = x + y;
return sum;
}
int main()
{
int a = 0;
int b = 0;
// 注意输入内容与格式化内容一致
scanf("%d %d", &a, &b);
int ret = Add(a, b);
printf("a + b = %d\n", ret);
return 0;
}
输入:22 17
输出:x + y = 39
当然,我们可以把Add函数简化一下:
int Add(int x, int y)
{
return x + y; // 直接返回结果
}
实参,也称为实际参数,也就是主函数main调用子函数时,传递给函数的参数a和b,而实际参数就是真实传递给函数的参数。
int ret = Add(a, b); // 加法函数举例
形参,也称为形式参数,也就是在函数名 Add 后的括号中写的 x 和 y,在函数未调用时不会向内存申请空间,形式参数只有在函数被调用的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形参的实例化。
int Add(int x, int y) // 加法函数举例
{
}
我们来观察下列代码,来探究二者之间的关系:
#include
int Swap(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
// 注意输入内容与格式化内容一致
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;
}
输入:1 5
输出:交换前:a=1, b=5 交换后:a=1,b=5
我们可以观察到,交换前后的结果相同,为什么会这样?我们可以通过调试来发现问题。
通过调试,我们不难发现,x和y的值确实发生了交换,但并未影响到a和b,是因为x和y的地址和a和b的地址是不一样的,在各自的内存空间内互不干扰。
当函数调用结束要返回的时候,前面创建的函数栈帧也要销毁。这里包含函数栈帧的创建和销毁的知识,我会在后面的章节为大家讲解,这样理解起来就更容易。
由此,我们可以得出一个结论:形参是实参的一份临时拷贝,改变形参并不会影响到实参。
写一个函数将一个整型数组的内容,全部重置为0,再写一个函数打印重置后数组的内容。
#include
// 重置数组函数
void Set_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
arr[i] = 0;
}
}
// 打印数组函数
void Print_arr(int arr[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int size = sizeof(arr) / sizeof(arr[0]);
Print_arr(arr, size); // 重置前
Set_arr(arr, size); // 重置数组
Print_arr(arr, size); // 重置后
return 0;
}
需要我们注意的是, Set_arr函数要能够对数组内容重置,就得把数组作为参数传递给函数,同时函数内部在设置数组每个元素的时候,也得遍历数组,需要知道数组的元素个数。所以我们需要给Set_arr传递2个参数,一个是数组,另外一个是数组的元素个数。Print_arr函数也是这样,只有得到了数组和元素个数,才能遍历打印数组的每个元素。
在函数中,也会经常使用return语句,下面是我们要注意的几点:
在C语言中,我们学习了分支和循环结构的嵌套使用,而函数嵌套调用就是函数之间的互相调用。
我们可以举一个简单的例子:
#include
void func1()
{
printf("函数1\n");
}
void func2()
{
printf("函数2\n");
func1(); // 在函数2中调用函数1
}
void func3()
{
printf("函数3\n");
func2(); // 在函数3中调用函数2
}
int main()
{
printf("主函数\n");
func3(); // 在主函数中调用函数3
return 0;
}
因此,当我们运行这段代码时,会按照函数的嵌套调用顺序,依次输出以下内容:
主函数
函数3
函数2
函数1
在上面的代码中,主函数main使用库函数printf,也是属于函数嵌套调用。
注:函数之间可以嵌套调用,但是不能嵌套定义。
所谓链式访问就是将一个函数的返回值作为另外一个函数的参数,像链条一样将函数串起来就是函数的链式访问。
#include
#include
int main()
{
printf("%d\n", strlen("abcdef"));//链式访问
return 0;
}
输出:6
我们可以观察下列代码,猜测输出结果是什么?
#include
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
输出:4321
为什么呢?我在第一次遇见也卡住了。
注:printf函数返回的是打印在屏幕上的字符的个数。
从上面代码中,可以知道第一个printf打印的是第二个printf的返回值,第二个printf打印的是第三个 printf的返回值。
第三个printf打印43,在屏幕上打印2个字符,再返回2;
第二个printf打印2,在屏幕上打印1个字符,再返回1;
第一个printf打印1,所以最终输出:4321。
#include
// 函数定义
int Add(int x, int y)
{
int sum = 0;
sum = x + y;
return sum;
}
int main()
{
int a = 0;
int b = 0;
// 注意输入内容与格式化内容一致
scanf("%d %d", &a, &b);
int ret = Add(a, b); // 函数调用
printf("a + b = %d\n", ret);
return 0;
}
#include
int Add(int x, int y); // 函数声明
int main()
{
int a = 0;
int b = 0;
// 注意输入内容与格式化内容一致
scanf("%d %d", &a, &b);
int ret = Add(a, b); // 函数调用
printf("a + b = %d\n", ret);
return 0;
}
// 函数定义
int Add(int x, int y)
{
int sum = 0;
sum = x + y;
return sum;
}
在函数调用之前先声明一下这个函数,这样代码就能正常编译运行。
在大项目中,代码可能比较多,通常不会将所有的代码都放在一个文件中;我们往往会根据程序的功能,将代码拆分放在多个文件中,这样有助于提高代码的可读性、可维护性和可扩展性。
一般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的实现是放在源文件(.c)中。
应用举例:~扫雷游戏来啦~
希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。
完结!咻~