本章所讲解的主要有八大模块:
目录
一.函数是什么
二.库函数
三.自定义函数
四.函数参数
五.函数调用
六.函数的嵌套调用和链式访问
七.函数的声明和定义
其中,函数递归将在下一篇单独讲解。这一篇只讲前7个模块
数学中我们经常会见到函数的概念,但是了解C语言中的函数吗?
我们看维基百科对它的定义:
·在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
·一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
以上我感觉不太难理解,简单来说呢,就是你把一项大工程,拆分成多个部分,然后实现每个部分的代码就叫做函数。
C语言中函数分类有两类:库函数和自定义函数。
为什么会有库函数?
1.我们在学习c语言的时候,在完成一个代码之后,总想迫不及待的知道代码的输出结果,那这个时候我们会频繁使用一个功能:讲信息按照一定格式输出到屏幕上(printf).
2.频繁的进行字符串拷贝工作(strcpy).
3.计算n的k次方的时候,也总是用这样的函数pow。
像上面描述的基础功能,它们不是业务性的代码,而是我们程序员都可能用得到,为了提高程序的可移植性和提高程序的效率,所以C语言的基础库提供了一系列类似的库函数,方便我们进行软件开发。
我们要想学习库函数里的拓展内容,可以参考下面的地址:
Reference - C++ Reference
这里面是关于库函数的一些介绍以及使用,感兴趣的可以去看看,我们平常用的库函数基本就那几个,想要拓展自己的视野可以去看看。
简单的总结,C语言的库函数都有:
IO函数(input output)输入输出函数
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
我们先来看一个例子:
strcpy
我们先来看第一行绿字:
char * strcpy ( char * destination, const char * source );
再来看接下来的内容:
Copies the C string pointed by source into the array pointed by destination , including the terminating null character(and stopping at that point).
这里介绍的它的作用
就是将source数组里面的字符串拷贝到destination数组里面去,包括终止的空字符。
这样我们知道也要主要参数的前后顺序,否则会可能出现拷贝相反的情况。
后面的那一句就是拷贝时的注意事项,大致就是必须保证destination数组空间必须大于等于source数组的空间。
这样如何看就大致说清楚了,还要记得引相对应的头文件哦。右上角就是头文件的名字
所以使用之前记得引用。
#include
2.1 如何学会使用库函数?
需要全部记住吗?No
需要学会查询工具的使用:
MSDN(Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
英文很重要。最起码得看懂文献。
如果库函数能干所有的事情,那还要程序员干什么?
所有更加重要的是自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。
函数的组成:
ret_type(返回类型) fun_name(函数名字)(para1(参数1),para2(参数2))
{
statement;(语句)
}
下面我们来举一个例子来简单介绍
| 写一个函数来找出两个整数中的最大值
#include
//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;
}
因为两个整数比较最后要返回一个较大的值,这个值是个整型,所以返回类型是int
我们要两个数进行比较,所以要传入两个参数,分别为num1,num2.
到了函数这里再创建两个形参x,y。分别对应num1和num2,所以对x,
y进行比较选出较大的值返回即可。
一共有两个概念,我们了解一下:
1.实际参数(实参):真实传递给函数的参数,叫做实参。
例如上一个例子,我们把num1,num2传给了函数,其中num1,num2叫做实参。
实参可以是:常量,变量,表达式,函数等。
无论实参是何种类型的量,我们在传递时,实参都必须要有一个确定的值。
2.形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内
存单元),所以叫形参。形参在函数被调用完之后就自动销毁了。
就如刚才上例的x,y。
在函数调用的时候, x , y 拥有自己的空间,同时拥有了和实参一模一样的内容。
所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。
有两种:
1.传值调用
字面意思是把数据的值传入到函数里面。
进入函数里面,会创建一份形参,此时函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
2.传址调用
字面意思:把一个数的地址传入到函数里面。
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
我们知道,任何数据的存储都需要在内存中开辟一块空间,而这块空间也有它对应的名字(地址)。就好比我们人,你知道他的名字,就能找到他。计算机里面也是如此,知道数据的地址,计算机也就能找到它,每个数据的地址都是不一样的。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。
举个非常简单的例子:
先看这段代码:
void fun(int a)
{
a=10;
}
int main()
{
int x = 5;
fun(x);
printf("%d\n",x);
}
我们先创建了一个变量x并初始化为5,然后经过传值调用(把5传进去了)函数,函数里面创建了一份形参a=10.然后函数调用完毕,输出此时的x值,我们发现结果不变,依然是5.
因为调用函数只是创建了它(x)的一份形参,改变形参不会对它本身造成影响。
再看下面这一段:
void fun(int *a)
{
*a=10;
}
int main()
{
int x = 5;
fun(&x);
printf("%d\n",x);
}
这个时候,我们再输出,发现结果由5变为了10.
我们把数据的地址传进去,然后在函数里面接收它的地址,解引用它并将它重新赋值为10.这个时候再输出结果当然是10了。(这个里面也创建了一份形参int* a,它接收了x的地址,虽然最后它被销毁了,但是我们在函数里面已经通过地址对它本身进行了对应的操作了,完成我们的目的了,即使销毁也没影响)
形象一点理解它:
传值调用:
一个人叫x,调用了一次函数,把他自己的身体(数据x本身,即5)传了进去,创建了一个和x一模一样的克隆人a。你对a无论进行任何操作,直到最后函数结束a被销毁,都不会对x造成任何的影响。
传址调用:
还是一个人叫x,调用函数,这次它不在把它自己的身体传进去了,他把他这个名字x(相当于x的地址)传了进去,一个人来记住了他这个名字,然后你要对它进行操作时,都需要先通过x的名字找到这个人(相当于解引用*),然后再进行。这个时候所进行的操作都是实打实的落在x身上。
最后记住名字的那个人被销毁了,但好像没什么影响了,因为你所进行的操作都已经完成了,目的达成了,那个人也并没什么太大的意义了。
函数的嵌套调用
嵌套调用就是某个函数调用另外一个函数(即函数嵌套允许在一个函数中调用另外一个函数)。
看下面这一段代码:
图片来源:比特就业课
在这段程序中:
1.首先程序从main函数开始,执行函数three_line();
2.进入函数three_line,再进入第一次循环,执行函数new_line();
3.进入函数new_line,输出hehe。
4.new_line函数结束,继续进行刚才的three_line操作,此时第一次循环结束,i++,一共循环执行3次
5.三次循环完以后,three_line函数结束,返回main函数继续执行。此时该执行return 0,程序结束
流程图大体如下:
注意:函数可以嵌套调用,但是不能嵌套定义。
举个嵌套定义的例子:
void three_line()
{
void new_line()
{
printf("hehe\n");
}
}//ERROR
函数的链式访问:
把一个函数的返回值作为另一个函数的参数
看下面一个例子:
#include
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
我们把printf的返回值作为外面printf的参数传了进去。
这里也说明了:printf的返回值是打印在屏幕上字符的个数
所以printf("%d", 43)打印在屏幕上是一个字符'43’,2个字符‘4’,‘3’,所以返回值是2
然后作为外面printf的参数输出出来结果是2,只有一个字符‘2’,所以外面的printf返回值是1
最后,1再作为最外面的printf的参数,输出到屏幕上是1
所以最终结果是4321。
函数的声明
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
大概意思呢,就是说如果你在程序中用到了某个函数,你最好再程序的开头来声明一下这个函数,就是来告诉编译器你要用这个函数,而且这个函数是存在的(这个只是你说的,实际存不存在还得看你写不写)。
举个例子:
我们先不声明:
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
int x = 1;
int y = 2;
Add(x, y);
}
可以轻松得到结果是3
但是如果我们把函数放到下面呢?
#include
int main()
{
int x = 1;
int y = 2;
Add(x, y);
}
int Add(int x, int y)
{
return x + y;
}
我们可以看到程序运行出错了,原因在下面说Add未定义。
我们不是定义了Add吗,为什么说Add未定义呢?
程序是从上到下执行,由于我们没有声明Add,导致我们执行到Add时编译器不知道这个函数的内容,所以报错。
这个时候,我们就需要声明一下。
怎么声明呢,我们声明格式是
返回类型 + 函数名 + 参数类型
其中参数类型中具体参数可以省略,但是定义中必须得写
如下:
#include
int Add(int x ,int y);
int main()
{
int x = 1;
int y = 2;
Add(x, y);
}
int Add(int x, int y)
{
return x + y;
}
或者改为
int Add(int ,int );
两者均可以正常运行。
以上就是全部内容了,下一章我们将详细讲解函数的递归。
如果有什么疑问,欢迎私信或者评论区留言!