一.函数的概念
在数学中我们就学过函数的概念,而c语言中将这种概念引入。翻译为通俗的言语来说就是:子程序。c语言中的函数就是完成某项特定任务的一小段代码。这段代码有特定的写法和调用方法。c语言的程序从某种层面上来说就是由无数个小的函数组合而成的。如果一个函数的功能能够完成某项特定的任务,那这个函数进行可以复用。在c语言中我们通常将函数分为两类:库函数和自定义函数。
二.库函数
2.1标准库和头文件
在c语言标准中我们规定了c语言中的各种语法规则;c语言的国际标准ANSIC规定了一些c语言中常用的函数的标准,被我们称之为标准库,不同的编译器厂商将ANSIC提供的c语言标准给出一系列的函数实现。这些函数便被我们称为库函数。例如我们常用的scanf(),printf(),这些都是标准的库函数,库函数是一些现成的函数,我们只需要应用他们所在的头文件就可以直接使用。这样一些常见的功能我们就可以通过库函数来实现,不需要再自己进行编译,极大的提高了我们的编译效率。
各种编译器的标准库中提供的一系列库函数根据功能不同的划分再不同的头文件中进行了声明。
库函数相关头文件:https://zh.cppreference.com/w/c/header
2.2库函数的使用方法
库函数的学习与和查看工具有很多,我列举出下面这两个最为经典的两个链接供大家使用。
c/c++官方链接:https://zh.cppreference.com/w/c/header
cplusplus.com: https://legacy.cplusplus.com/reference/clibrary/
大家可以通过这两个链接去查询和使用你想使用的函数属于哪个头文件中。例如属于<math.h>中的开平方根函数sqrt,属于<stdio.h>中的输入,输出函数scanf,printf,属于<string.h>中用来计算字符串长度的strlen函数。大家可以看完这篇文章后自己去试一试。同时需要告诉大家在使用库函数时是一定要包含它所对应的头文件的,不然程序将会在编译时出现问腿。
三.自定义函数
了解完库函数之后我们来了解一下自定义函数。相对于库函数,其实自定义函数更加的重要,因为库函数是实现一些c语言中通用的,常见的功能;而自定义函数则是更加自由,它能够让程序员自由的发挥,实现你想要实现的功能,给程序员写代码提供的更多的创造性。
3.1函数的语法形式
ret_type fun_name(形式参数)
{
}
在其中,ret_type是函数的返回类型,fun_name是函数名称,小括号中是函数中的形式参数,大括号中则是函数体。我们可以将一个函数类比为一个小工厂,我们为函数输入的参数就是工厂的原材料,经过函数体的加工,输入成品,从而达到实现这个函数的功能。函数名的存在是为了方便我们使用,类似于人名,它的存在能够让我们精确的调用到这个函数。
3.2函数的样例
#include<stdio.h>
void multi(int num)
{
int i, j;
for (i = 1; i <= num; i++)
{
for (j = 1; j <= i; j++)
{
printf("%dx%d=%d\t", i, j, i * j);
}
printf("\n");
}
}
int main()
{
int num;
scanf("%d", &num);
multi(num);
return 0;
}
上面是一个输入一个数字,将会输出该数字及以下的乘法表。其中在进行输出乘法表的算法中是通过上述中的multi函数实现,在主函数中只需要调用该函数就可以使用。上述只是一个例子,在未来我们设计函数的时候,函数名,函数类型,参数类型等都是可以进行灵活变化的。
3.3形参与实参
在上述代码中在函数括号中定义的num是形式参数,简称形参。为什么叫形参呢?因为在上述中,如果你不调用该函数,那这个参数就只是形式上的,不会向内存申请空间,并不真实存在。只有当你调用这个函数时为了存放实参中传递过来的值,它才会向内存申请空间,这个过程就是形式的实例化。实参则是实际上的参数,我们简称为实参。实际参数就是真实传递给函数的参数。
我们虽然提到了形参是实参传递给它的,他们之间是有联系的,但是形参和实参各自有其独立的内存空间。我们可以自己在编译器上编译一个函数,在编译器上进行调试,我们将会得到实参与形参的内存地址是不同的。我们可以理解为形参是实参的一份临时拷贝。
四.return语句
在函数中,我们经常会使用return语句,在这里提示一下return语句的注意事项。
1.return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
2.return返回的值与函数的返回类型不一致,系统会自动将返回的值隐式转换为函数的返回类型。
3.return后也可以不返回值,直接写return。这种写法适合函数返回类型是void的情况。
4.return语句执行后,函数就彻底返回,后面的代码将不继续执行。
5.如果函数中存在if等分支语句,则需要保证每种情况都有return进行返回,否则程序将会出现错误。
五.数组做函数参数
我们再进行函数的编译使用时,难免会碰到将数组作为参数传递给函数,在函数内部对数组进行操作。在对数组进行传参时我们需要将数组和数组的元素个数都作为参数传递过去,需要传递两个参数。因为我们只有拿到数组和数组的元素个数才能够遍历打印数组的每一个元素。在这里我们又需要知道数组传参的几个重要知识点:
1.函数的形式参数要与实际参数的个数相匹配。
2.形参如果是一维数组,数组的大小可以省略不写。如果是二维数组,行可以省略,但是列不能省略。这点与数组的性质是一样的。
3.函数的实参是数组,那形参也可以写成数组的形式。
4.数组传参,形参是不会创建新的数组的。
5.形参操作的数组与实参操作的数组是同一个数组。
六.嵌套调用与链式访问
6.1嵌套调用
嵌套调用就是函数之间的互相调用,每个函数就类似于一个精密的玩具零件,当我们把函数之间高效的互相调用,就类似于零件相互组装,最后才能够写出相对大型复杂的程序。例如我们向设计一个程序用来计算某年某月有多少天?那我们就可以设计两个函数来实现这个程序。可以设计一个is_year_mouth()函数来计算该年是否是闰年,再设计出一个day_of_mouth()来计算该月有多少天。通过这两个函数的嵌套调用我们就能很好的解决这个计算某年某月有多少天的问题了。大家可以自己去编译器上试一试吧。
6.2链式访问
所谓链式访问就是将一个函数的返回值作为另一个函数的参数,类似于链条一样连接起来,因此称为链式访问。我们可以通过一个简单的例子来实现这个链式访问。
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
大家可以自己在你的编译器上试一试这个代码将会如何执行呢。它将会在打印界面上打印出什么呢?这里我就不透露这个简单的小问题了,大家自己去尝试一下就知道结果啦。
七.函数的声明和定义
在前面我们都是写完函数就直接使用,那是因为函数都是在主函数前面完成编译的,变相的进行了函数的声明。假设我们将函数的整体放在主函数的后面来实现,那这时你的编译器将会报出警告。因为你并未进行函数的声明,此时你的函数时无法使用的。函数的调用中一定要满足先声明后使用这个条件,因此我们可以先在主函数前进行函数的声明,如此便可以正常编译了。函数的声明中参数可以省略名字,只保留参数类型。
函数的定义就是我们在函数中实现函数具体功能的那部分,在进行了声明和定义后的函数就可以进行调用了。并且函数的定义是一种特殊的函数声明,就像上面的代码,都是定义完后直接调用,这样也是可行的。如果你写的代码时多文件的代码,那你同样可以在你的头文件中进行函数的声明,这样同样时可行的,能够成功的调用函数。
此外还有static,extern等用来修饰函数和声明外部符号的关键字,这些就不展开叙述了,大家知道有这么个东西即可。下次使用到的时候我再进行具体的说明。