当前课程知识点:基于Linux的C++ > 第三讲 函数 > 3.2 函数声明、调用与定义 > LinuxCPP0302
首先来看函数的声明与调用
从函数调用的这个角度来讲
它实际上涉及到了两个函数
一个是发起调用的函数
往往称之为主调函数
有时候也称之为客户函数
第二个就是被调用的函数
称它为被调函数
有的时候也称它为服务器函数
就相当于一件事情 你请别人做
别人帮你完成它 你就是客户
别人实际上在这里边就充当了服务器
在函数调用的过程中
主调函数和被调函数之间
或者客户函数和服务器函数之间
它们会要传递一些信息
有些信息 是由客户传给服务器的
有些信息 是要由服务器传回给客户
所以在这里边 函数调用的时候
就会发生参数传递和返回值
可以看下面有这两个例子
有一个例子叫Swap
完成两个数的互换
这就是很典型的函数调用
传两个值进去
这就是两个参数a和b
要互换的是这两个参数的值
互换了以后 a、b两个值就会对调
所以它不需要带回来结果
那么这个函数的返回值就是空的
第二个例子就是Add(a,b)
a、b两个数加在一起
两个参数就要传进去
它就会带回来一个结果
我让你做两个数的加法
你得把这个加法的结果告诉我
那就是产生一个返回值
这个返回值 将赋值给n
这就是函数调用
在函数调用的过程中
为了保证调用没有问题
必须要为每一个函数
书写正确的函数原型
函数原型表达这个函数的实现
和调用格式的标准说明
正常情况下 它将充当函数的接口
所以它一般都出现在头文件里
它的标准格式呢
前面是一个函数的返回值类型
后面跟着一个函数的名称
有一个小括号对
它里面包含着一系列的形式参数
后面跟着一个分号
它是表达了它是一个函数的概念
看下面的三个例子
三个函数 一个叫Add函数
把两个整数加起来
结果是一个整数
所以函数的返回值类型是int
函数的名字是Add
两个函数的形式参数都是int 叫int x
用逗号分隔开的int y
这就是标准的函数原型
第二个就是Swap
要完成两个整数的互换
因为它没有返回值类型
所以定义成void
第三个Compute
就是简单地作一个计算
它不需要接受任何参数
返回值类型如果没有
你必须写void
接下来就是函数定义
不仅可以使用标准库里边
提供的一系列的函数来实现程序
还可以自己定义一些函数
函数的定义需要使用编程语言
来给出函数的执行步骤
它的每一条代码应该怎么怎么写
怎么装配
这个过程就叫函数的定义
在实现这个函数的过程中
要特别注意这个函数做完了以后
它将带回来一个结果
这个返回值如何提供给主调函数
按照什么样的方式来提供
后面会看到 将使用return语句
在这里 还有一种很特殊的函数
称它为谓词函数
所有返回bool类型值的函数
都称为谓词函数
它的最主要的一个目的
是为了表达某项任务是不是已经完成
或者某个条件是否已经满足
可以通过几个例子来看怎么实现函数
首先看Add函数的例子
要编写一个函数Add
求两个整数的和
代码这么写“int Add(int x,int y)”
除了后边没分号
分号变成了花括号对
封装的程序代码
这就叫函数实现
这里边三条语句
定义一个临时变量t
然后让t赋值为x + y 然后return t
把这个结果返回给主调函数
这里面是三条语句
其实你可以把它合成一条语句
就写一个“return x + y;”
按照这个模式也能实现Add函数
看第二个例子
叫Compare 要完成两个数的比较
比较它们的大小
如果x == y
那我们就返回0
如果 x > y的那我们就返回1
如果x < y就返回-1
要实现像这样的Compare函数
定义一个临时量t
然后if( x == y )
t就赋值为0
else if( x > y )
t就赋值为1
再否则t就赋值为-1
然后return t
这就是标准的Compare函数
第三个例子就是Swap这个函数
要互换两个整数的值
一个标准的三步互换动作
需要定义一个临时变量t
然后把x给t 把y给x
然后再把t给y
最后return完了
这就是Swap这个函数
注意到很特殊
Swap这个函数是没有返回值的
函数的原型前面是个void
函数的定义前面其实也是一个void
它什么都不能return
所以就孤零零的写一个“return;”
C/C++的编译器还告诉你
当这样的return语句
如果是在函数的最结尾的时候
其实你不写也没关系
这就是个标准的三步互换的动作
那么接下来有一个挑战性的问题
一定要使用这个临时变量t
来完成三步互换吗
其实不一定 不使用临时中转变量
也能够互换两个整形数据对象的值
同学们下课以后可以仔细地思考一下
如果程序中出现了多条return语句
那实际上也是合法的
正常情况下
对于函数内部的语言逻辑来讲
它见到了第一条return语句
这个函数其实就结束了
你的程序中其实可以写很多条return语句
但是你要知道
在return语句后面的代码
往往是不能够得到执行的
除非这些return语句是在不同的分支上
你比如说刚才的Compare例子
if-else if-else有个三路分支
互相之间是互斥的
你要么走这路分支
要么走那路分支
所以每路分支一做完以后
整个程序其实就结束了
编写这段程序代码的时候
你完全可以这么写
“if( x == y ) return 0; else if( x > y ) return 1; else return -1;”
这个代码这么写
临时变量t就不需要定义
就直接返回三个明确的数据0、1、-1
对于多条return语句的返回
往往都是出现在
这样的if-else if所有的分支上
如果你某条分支上面出现了return语句
而其他的分支上面是没有return语句
你要特别记住 你为什么这么写
没有return语句的那个分支
做完了以后它做什么
接下来有没有真正的return
同学们编程的时候需要特别注意这一点
这就是多条return语句
接下来就是很重要的一个函数
称它为谓词函数
我让同学们写一个函数叫IsLeap
来判断某个给定的年份
比如year这样的一个年份是不是闰年
前面其实已经通过一个例子
来给大家展示了它的判别表达式
所以现在实现谓词函数的话就简单了
boolIsLeap(int year) 然后花括号对
直接return这个逻辑表达式
这就是标准的谓词函数
谓词函数最重要的一个用途是什么
它往往可以充当条件表达式
判断一个年份是不是闰年
如果是闰年做什么
如果不是闰年做什么
就可以写这样的代码
“if( IsLeap( year ) )”
这个函数做完了
结果将作为一个条件表达式的值
来测试if分支应该做哪一路
这是非常重要的出现场合
谓词函数同学们一定要会用
C++提供了一个很特殊的函数的定义方式
称它为函数重载
所谓函数重载就是定义同名的
但参数不完全相同的函数
就叫函数重载了
参数不完全相同的意思是什么呢
参数个数可以不一样
参数的类型可以不一样
参数的顺序也可以不一样
它有什么好处呢
好处就在这个地方
比如说我比较两个数的大小
如果这两个数是int
那我就必须为它们写一个函数
叫“int Max( int x, int y);”
如果我还想比较两个字符型的数的大小
那我们需要写一个“char Max( char x, char y);”
我比较两个bool量大小
那就“bool Max( bool x ,bool y);”
这些函数你都会不断地写
每个都必须实现
但是在实现这些函数的时候
你要特别记得
如果我没有函数重载的话
这三个函数的名字必须是区分开的
你比如讲一个较Max1
一个叫Max2
一个叫Max3
或者一个叫MaxInt
一个叫MaxChar
一个叫MaxBool
你必须把名字给分开
否则的话
编译器找不到找不到对应的那个函数
现在有了这个函数重载机制
这三个函数的名字
就可以命名成完全一模一样的
命名成一模一样的有什么好处
就在于你不会搞混淆
你明确地知道调用的是Max
从功能语义上来讲
我求的就是两个数的大小
管这两个数是整型呢 还是字符型呢
还是bool型呢
总之 我是求的是这两个量的大小
名字固定就叫Max
这就是函数重载的好处
好 现在给大家演示
我们怎么样在Linux下边编写函数
我们刚才在控制台里创建了一个目录Add
现在里面是空的
我们通过gedit菜单打开一个新的文件
先保存 选择Add这个目录之后
把这个文件名字保存成main.cpp
然后就在这里敲代码
先敲代码后保存成不成呢 也成
但是先敲了代码
它不知道是cpp文件
就没办法替你做语法高亮显示
现在它知道了
我们这个文件名字是“.cpp”
后缀名字是C++文件
所以它就可以自动做语法高亮显示
然后intmain 这是主程序
我们要做的是写Add函数 做加法
对于这道题来讲情况其实很简单
首先是要拿到两个数
然后把这两个数加起来
然后把结果给输出
然后问b的数据
cin 拿到b的数据
然后sum = a + b
cout<< a<<“ + ”<< b <<“ = ”<< c
这个程序倒是蛮简单的 就这么多
我们最后给他一个endl
我实际上是要做加法的
要写一个函数来实现
而不是用a + b这个加法
所以我们要实现一个函数
int Add它要带双参数
这个就是Add函数的函数原型
“int Add(int x, int y);”
然后我们要在main函数的后边
给出这个函数的实现
我把这些函数实现在main函数的前边
行不行呢 行
其实C/C++的代码是一趟编译的语言
也就是说 它要使用这个函数
它必须在前面看到这个函数的定义
否则的话没法用
它不知道这个函数的入口地址在哪里
这是一个非常重要的设计策略
但是把所有的函数
都写在main函数前边 不好
原因就是 别人看你的代码
应该一招眼就应该看到
我们的主函数才对
如果他看到太多我们自己写的
内部函数
他就看到太多细节
然后再去理解这个主函数的框架
实际上就不太容易
所以说从理解这个程序
和维护这个程序的角度来讲
应该先写主函数
所有的函数都应该写在main函数的后边
问题就来了
把Add函数写在main函数的后边
那这个程序它能运行吗 不能
那怎么办呢
要把这个函数的原型写在main函数前面
这样就行了
你看这是很典型的代码
int t定义了一个整形变量t
然后t = x + y
return t
写完了我这里边还没调用呢
add( a, b )
程序就写完了 Ctrl + S保存
编译 编译肯定是错的 对吧
待会儿再说
我先编一遍试试 g++ main.cpp
它就给你一些错误
什么错误 a没有定义
原因就是我们没有定义这三个量
对吧 再来看 还是错的
c没定义
然后是13行在cin之前
它想要一个分号
一看我们代码
哎哟 这个地方少了一个分号
后来我们看cout< 搞错了 这个和不是c 是sum 修改 再来 OK 没问题了 ls 然后a.out就出来了 然后我们a.out 问你a的值 1 b的值 2 然后1 +2 =3 结束了 看我们这个程序 这是我们的主函数 然后我们实现了一个内部函数Add 加法函数 这是它的函数实现 写在main函数的后边 然后前边有一个函数原型 我们的函数理论上都应该 按照这样的方式进行设计 到了后边 我们会讨论 如果我们把这个程序 组织成一个库的模式 那么这样的函数的组织方式 就不是在和这个main函数放在一块了 事实上 这些函数的原型 都会放在专门的头文件里 函数的实现都会放在专门的源文件里
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口