如果你从未接触过C语言, 那么我建议你先阅读前面的文章:
什么是函数
在本系列的第一篇正式文章↗中, 已经初次提到了一个重要的名词: “函数”
从那时起, 就频繁使用到了一个名为printf()
的函数, 用来将字符串打印到终端上
之前只是简单的使用, 不知道你心中是否存在一个疑问: 为什么只调用一个printf()
就能把目标字符串打印到终端上呢?
虽然我们并不了解printf()
究竟是如何实现将字符串打印到终端上的
但是我们知道他一定并不是表面上看上去这一句话这么简单, 他一定执行了一系列的操作才完成的打印功能
它就像一个工具, 你给它一段文本, 它给你完成任务
但它到底是如何工作的?是谁写好了这个函数?我们是否也可以写出属于自己的类似工具?
这些问题的答案,都与函数的本质密切相关:
函数本质上,是一段具有特定功能的、可重复使用的代码块
通过定义函数, 我们可以把一组能够实现某种功能的语句封装起来, 在下次需要的时候直接调用函数, 而不必每次都重新编写同样的代码
我们频繁调用的printf()
, 实际是C语言标准写好的一个函数, 这个函数内部封装了非常复杂的逻辑, 最终能够实现打印的功能
如果标准没有提供, 这个函数, 那么 每次需要使用打印的功能, 就需要重新实现一遍打印的逻辑, 这是非常繁琐且复杂的
函数, 是C语言最基本的组成单位之一, 我们也可以定义、实现自己的函数
函数的定义与使用
一个自定义函数的结构是这样的:
返回值类型 函数名(参数列表) {
// 函数体
return 返回值;
}
只看语法结果结构, 可能会觉得有一些抽象, 不太容易理解
下面可以写一个非常简单的 进行两个整数加法运算的函数, 来帮助理解:
#include <stdio.h>
// 自定义的函数
int add(int num1, int num2) {
int num = num1 + num2;
return num;
}
int main() {
int num = add(10, 20);
printf("num: %d\n", num);
return 0;
}
编译运行这段代码:
从结果来看, num
值为30
, 但我们执行的是int num = add(10, 20);
结果说明, add(10, 20)
确实完成了10 + 20
并输出了结果30
再来看这个函数:
int add(int num1, int num2) {
int num = num1 + num2;
return num;
}
按照函数结构分析:
返回值类型:
int
函数名:
add
参数列表:
int num1, int num2
函数体:
int num = num1 + num2; return num;
返回值:
num
结合调用语句int num = add(10, 20);
, 调用过程可以理解为以下几个步骤:
- 调用处: 调用函数名为
add
的函数 - 调用处: 传入第一个参数
10
, 第二个参数20
- 函数
add
被调用, 函数内: 接收第一个参数10
存储到函数的临时变量num1
, 接收第二个参数20
存储到函数的临时变量num2
- 函数内: 执行函数体
int num = num1 + num2;
, 即num = 10 + 20;
- 函数内: 最终将
num
, 通过return
作为返回值返回 - 调用处: 接收
add(10, 20)
的返回值, 并将此整型数据赋值给变量num
最终完成了10
和20
的相加, 并得出了结果
函数的意义
上面实现了一个非常简单的函数add
, 作用是两个整型的加法
你可能会有疑问: 这又什么意义呢?
加法运算不用函数, 直接a + b
就能计算, 还特地写一个额外的函数, 有什么意义呢?
如果只是加减乘除这样的四则运算, 额外写一个函数确实没有什么意义
不过如果要实现更复杂的功能, 定义函数就非常有必要了
比如, 如果你要对1批数据进行排序: {181, 1681568, 45861, 684,1968, 4156, 84, 68, 456, 845, 684, 168, 4551}
你当然可以直接对这些数据进行排序, 此时你需要实现一遍排序的代码
int arr[] = {181, 1681568, 45861, 684,1968, 4156, 84, 68, 456, 845, 684, 168, 4551};
/*
* 假设这里有对arr的排序代码, 可能有20行左右
*/
但是如果你要对10批数据进行排序, 难道还要实现10遍基本相同的逻辑吗?
// 第1批数据
int arr1[] = { ... };
/*
* 实现20行针对arr1数据的排序
*/
// 第2批数据
int arr2[] = { ... };
/*
* 实现20行针对arr2数据的排序
*/
// 第3批数据
int arr3[] = { ... };
/*
* 实现20行针对arr3数据的排序
*/
// 第4批数据
int arr4[] = { ... };
/*
* 实现20行针对arr4数据的排序
*/
// 第5批数据
int arr5[] = { ... };
/*
* 实现20行针对arr5数据的排序
*/
...
如果是这样, 好像过于繁琐了, 而且, 你或许还不能保证每次处理数据的逻辑都百分百正确, 即使是逻辑相同的代码, 重复实现也可能会出现偏差或错误
此时, 函数的作用就体现出来了, 反正都是对整型数据排序, 排序的逻辑只需要封装在一个函数中, 在需要排序的时候, 调用函数将数据传入函数不就可以了吗?
而且只需要实现一遍代码逻辑, 并且不会出现因为重复实现可能出现的偏差或错误
如果将排序逻辑封装为函数, 上面的对数据的处理就可以演变为:
// 定义对整型数据排序的函数
void sort(数据) {
// 函数体, 排序逻辑, 可能20行左右
}
// 第1批数据
int arr1[] = { ... };
sort(arr1);
// 第2批数据
int arr2[] = { ... };
sort(arr2);
// 第3批数据
int arr3[] = { ... };
sort(arr3);
// 第4批数据
int arr4[] = { ... };
sort(arr4);
// 第5批数据
int arr5[] = { ... };
sort(arr5);
...
除此之外, 如果不使用函数, 以后写代码可能一个项目有上万行代码, 难道都要一次性全部在main()
函数中实现吗?
这肯定是不现实的
所以, 函数是C语言中非常重要的组成部分, 它可以: 提高代码的复用性、增强代码可读性和结构清晰度、减少维护成本等