目录
一、函数是什么
我们在数学的学习中,我们知道函数(function)在数学中是两不为空集的集合间的一种对应关系:输入值集合中的每项元素皆能对应唯一一项输出值集合中的元素。
但是在我们的计算机科学中,函数就是一个子程序,是一个大型程序中的某部分代码, 由一个或多个语句块组 成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
通俗的来说,函数就是我们的一个加工厂,当我们根据形参为这个加工厂提供它所需的原材料时,通过函数的处理,我们可以得到一个成品。
二、C语言中函数的分类
1. 库函数2. 自定义函数
1.库函数
为什么会有库函数?1. 我们知道在我们学习 C 语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想 把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格 式打印到屏幕上(printf)。2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作( strcpy )。3. 在编程是我们也计算,总是会计算 n 的 k 次方这样的运算( pow )。像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,所以C 语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发。
我们在平时学习的过程中,经常会用到库函数,库函数是我们用到最频繁的函数,经常使用库函数会提高我们写代码的效率。
简单的总结, C 语言常用的库函数都有:IO 函数字符串操作函数字符操作函数内存操作函数时间 / 日期函数数学函数其他库函数
我们在这给大家介绍几个库函数:
1.strcpy函数
我们还是通过前面介绍过的cplusplus网站进行库函数的学习。
这个函数本质上是一个拷贝字符串的函数。
char * strcpy ( char * destination, const char * source );
这是对于函数的定义,我们可以看出他的形式参数,以及形式参数的类型,还有函数的返回值。这在我们学习使用函数时是非常重要的。
2.memset函数
这个函数是一个填充内存块的函数。
void * memset ( void * ptr, int value, size_t num );
ptr指向要填充的内存块的指针。value要设置的值。该值作为 int 传递,但该函数使用此值的无符号 char 转换填充内存块。num为要设置为该值的字节数。
size_t 是无符号整数类型。根据函数的定义,我们就可以简单的操作这个函数
2.自定义函数
ret_type fun_name ( para1 , * ){statement ; // 语句项}ret_type 返回类型fun_name 函数名para1 函数参数
自定义函数顾名思义就是我们程序员自己进行定义的函数,它可以让我们设计出我们想要的功能不受限于库函数。
#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
return (x > y) ? (x) : (y);
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = get_max(num1, num2);
printf("max = %d\n", max);
return 0;
}
3. 函数的参数
1.形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。通俗来说,形式参数就是我们在使用这个函数时给的一个样本,我们必须提供相同类型的参数才可以调用函数。
2.实际参数(实参)
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
我们先来看两段代码
#include <stdio.h>
//实现成函数,但是不能完成任务
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
//正确的版本
void Swap2(int *px, int *py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);
printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);
printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
return 0;
}
这是一个想要实现 两个数字交换的代码,可以第一个程序并不能实现这个功能,而第二次改为用指针传递后,却实现了这个功能,这是为什么呢?我们一起先来探究一下。
在我们第一个程序中,我们发现形参只是对实参的数据进行了拷贝,形参也有了自己的地址,所以形参和实参并没有联系起来,在形参内将这两个数字进行交换,并不会影响实参的结果。
而当我们进行地址传递的时候,虽然形参也是实参的一份拷贝,它也会有自己的地址,但是我们传递过去的是实参的地址,他们的地址进行交换,我们就可以使用解引用的操作使实参和形参产生联系,最终来改变实参的值。
在我们使用形参实参的时候,我们要记得,在形参实例化之后,形参对实参进行临时拷贝,当我们调用函数之后,为形参分配内存单元,当我们调用完之后这个形参就自动销毁了。
4. 函数的调用:
4.1 传值调用函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。4.2 传址调用传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
5. 函数的嵌套调用和链式访问
1.嵌套调用
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
例如下边的程序
#include <stdio.h>
void new_line()
{
printf("ga\n");
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
这就是函数嵌套调用的示例,他最终会输出gagagga。
2.链式访问
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
这个程序就将printf函数的返回值作为printf函数的参数使用,这里我们就必须了解printf函数的返回值是什么,经过查阅,我发现了以下的定义。
成功后,将返回写入的字符总数。
如果发生写入错误,则设置错误指示器(ferror)并返回负数。
如果在写入宽字符时发生多字节字符编码错误,errno 将设置为 EILSEQ 并返回负数。
哦对,那返回值就是成功打印的字符个数,那么43的字符个数是2,2的字符个数为1,那么我们就知道了这个最终的输出结果就是4321.
六、函数的声明和定义
1.函数的声明
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。3. 函数的声明一般放在头文件中。
2、函数的定义
函数的定义是指函数的具体实现,交待函数的功能实现。
函数的声明一般放在头文件中,即后缀名为.h的文件,然后,函数定义一般在源文件中,在我们的主函数中,我们只需引入这个头文件,我们便可以实现多文件编写来操作这个函数。
#include "test.h"
//函数Add的实现
int Add(int x, int y)
{
return x+y;
}