Taps:个别尚未完成,代码以及文案还需润色。准备改版为匈牙利命名法。
匈牙利命名法:
一般规则
全局变量:g_ (如:int g_nCount = 0;)
常量:c_ (如:const int g_nNum = 0;)
C++类成员变量:m_(如:类中数据成员,int m_nCount = 0;)
静态变量:s_ (如:static int s_nNum = 0;)
类型部分
指针:p
C++指针:ptr,也可用 p
函数:fn
无效:v
句柄:h
长整型:l
短整型:n
布尔:b
浮点型:f
双精度浮点:d / dbl
字符串:s / sz
C++字符串:str
计数,通常用 cnt)
字符:ch(通常用 c)
整型:i(通常用 n)
字节:by
字:w
双字:dw
实型:r
无符号:u
描述部分
最大:Max 最小:Min
初始化:Init 临时变量:T(或 Temp)
源对象:Src 目的对象:Dest
导读
本章节习题介绍函数的用法,函数有三个重要组成部分:函数名、参数列表、返回值。
本章的核心知识点与之对应:
传值参数、传引用参数、数组参数和可变参数
无返回值、有返回值、返回数组指针
同名函数重载及重载函数的匹配
需要理解函数声明和定义的方法,弄明白在函数调用的过程中到底发生了什么。形参和实参是关于函数参数的一对重要概念,实参与形参的匹配涉及函数的很多知识点。值传递的参数只在函数体内有效,而引用传递的参数可以作用于函数体之外。
函数重载是 C++的一个重要语法特征,允许程序员使用同名函数执行目标类似 但细节有所区别的任务。函数匹配和发生在其中的参数类型转换是函数重载的关键。
习题:练习 6.1 ~ 练习 6.56
练习 6.1:实参和形参的区别是什么?
【出题思路】
函数名、返回值、参数三者组成了函数,其中参数分为实参和形参。
【解答】
实参是函数调用的实际值,是形参的初始值。
形参出现在函数定义的地方,形参列表可包含多个形参(以逗号分隔)。
形参规定了一个函数所接受数据的类型和数量。
实参出现在函数调用的地方,实参与形参数量一致。实参的主要作用是初始化形参,
并且这种初始化过程是一一对应的,实参的类型必须与对应的形参类型匹配。
练习 6.2:请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?
(a) int f() {
string s;
// . . .
return s;
}
(b) f2(int i) { /* . . . */ }
(c) int calc(int v1, int v1) /* . . . */ }
(d) double square(double x) return x * x;
【出题思路】
本题旨在考查函数的一般语法规则。首先,函数的三要素(函数名、返回值、
参数)必不可少;其次,函数的主体内容必须放在一对花括号内;最后,函数体内
返回的结果必须与函数的返回值类型相同。
【解答】
(a)是错误的,因为函数体返回的结果类型是 string,而函数的返回值类型是
int,二者不一致且不能自动转换。修改后的程序是:
string f() {
string s;
// . . .
return s;
}
(b)是错误的,因为函数缺少返回值类型。如果该函数确实不需要返回任何值,
则程序应该修改为:
void f2(int i) { /* . . . */ }
(c)是错误的,同一个函数如果含有多个形参,则这些形参的名字不能重复;另
外,函数体左侧的花括号丢失了。修改后的程序应该是:
int calc(int v1, int v2) {/* . . . */ }
(d)是错误的,因为函数体必须放在一对花括号内。修改后的程序是:
double square(double x) {return x * x;}
练习 6.3:编写你自己的 fact 函数,上机检查是否正确。
【出题思路】
通过练习编写自己的阶乘函数,熟悉函数的语法规则和定义函数的方法。
【解答】
原书中的 fact 函数使用 while 循环执行递减连乘运算,我们改写该程序,使
用普通 for 循环得到满足题意的函数,如下所示:
#include <iostream>
using std::cout;
using std::endl;
int fact(int val) {
if(val < 0) {
return -1;
}
int ret = 1;
// 从 1 连乘到 val
for(int i = 1; i != val + 1; ++i) {
ret *= i;
}
return ret;
}
int main() {
int val = 5;
cout << fact(5) << endl
return 0;
}
练习 6.4:编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数
字的阶乘。在 main 函数中调用该函数。
【出题思路】
理解函数的调用过程,尤其是注意区别形参和实参。
【解答】
#include <iostream>
using namespace std;
// 版本一
int fact(int val) {
if(val < 0) {
return -1;
}
int ret = 1;
// 从 1 连乘到 val
for(int i = 1; i != val + 1; ++i) {
ret *= i;
}
return ret;
}
/*
// 版本二
int fact(int i) {
return i > 1 ? i * fact(i - 1) : 1;
}
*/
int main() {
int num = 0;
cout << "请输入一个整数:";
cin >> num;
cout << num << "的阶乘是:" << fact(num) << endl;
return 0;
}
练习 6.5:编写一个函数,输出其实参的绝对值。
【出题思路】
练习定义函数并在 main 中调用该函数。
【解答】
根据参数类型的不同,我们可以分别求整数的绝对值和浮点数的绝对值。因为
本题主要考查的知识点是编写和使用函数,因此参数的类型不是关键。从通用性的
角度出发,我们不妨设定参数类型是双精度浮点数 double。满足题意的程序如下所
示:
#include <iostream>
#include <cmath>
using namespace std;
// 第一个函数通过 if-else 语句计算绝对值
double myABS(double val) {
if (val < 0) {
return val * -1;
}
else {
return val;
}
}
// 第二个函数调用cmath头文件的abs函数计算绝对值
double myABS2(double val) {
return abs(val);
}
int main() {
double num = 0.0;
cout << "请输入一个数:";
cin >> num;
cout << num << "的绝对值是:" << myABS(num) << endl;
cout << num << "的绝对值是:" << myABS2(num) << endl;
return 0;
}
在这个程序中,我们定义了两个函数myABS和myABS2,它们使
用两种不同的方法求绝对值。第一个函数使用if-else分支语句判断实参
是正数还是负数,从而计算实参的绝对值。第二个函数直接调用cmath
头文件的abs函数实现同样的功能。
练习 6.6:说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。
【出题思路】
理解局部变量的含义,理解自动对象的创建和销毁机制,弄清楚
我们为什么需要局部静态变量,应该如何使用局部静态变量。
【解答】
从形式语法上看:
形参:定义在函数形参列表中;
局部变量:定义在代码块中;
局部静态变量:在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止时才被销毁。
区别是:
● 形参是一种自动对象,函数开始时为形参申请内存空间,我们
用调用函数时提供的实参初始化形参对应的自动对象。
● 普通变量对应的自动对象也容易理解,我们在定义该变量的语
句处创建自动对象,如果定义语句提供了初始值,则用该值初始化;
否则,执行默认初始化。当该变量所在的块结束后,变量失效。
● 局部静态变量比较特殊,它的生命周期贯穿函数调用及之后的
时间。局部静态变量对应的对象称为局部静态对象,它的生命周期从
定义语句处开始,直到程序结束才终止。
// 例子
int myAdd(int val1, int val2) { // val1 和 val2 是形参
int result = vall + val2; // result 是普通局部变量
static unsigned iCnt = 0; // iCnt 是静态局部变量
++iCnt;
cout << "该函数已经累计执行了" << iCnt << "次" << endl;
return result;
}
int main() {
int numl, num2;
cout << "请输入两个数:";
while (cin >> numl >> num2) {
cout << numl << "与" << num2 << "的求和结果:"
<< myAdd(numl, num2) << endl;
}
return 0;
}
练习 6.7:编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。
【出题思路】
本题旨在考查局部静态变量的使用方法。
【解答】
我们定义一个非常简单的函数,该函数除了统计执行次数外什么
也不做。该函数的定义和调用形式如下所示:
#include <iostream>
using namespace std;
// 该函数仅统计执行的次数
unsigned myCnt() {
static unsigned iCnt = -1;// iCnt 是静态局部变量
++iCnt;
return iCnt;
}
int main() {
cout << "请输入任意子符后按回车键继续" << endl;
char ch;
while (cin >> ch) {
cout << "函数myCnt()执行次数是:" << myCnt() << endl;
}
return 0;
}
练习 6.8:编写一个名为Chapter6.h的头文件,令其包含6.1节练习
(第184页)中的函数声明。
【出题思路】
函数声明与函数定义的形式非常类似,唯一的区别是函数声明无
须函数体,用一个分号替代即可。函数应该在头文件中声明而在源文
件中定义。
【解答】
根据题目要求,Chapter6.h头文件的内容如下:
#ifndef CHAPTER6_H_INCLUDED
#define CHAPTER6_H_INCLUDED
int fact (int );
double myABS (double );
double myABS2 (double );
#endif // CHAPTER6_H_INCLUDED
练习 6.9:编写你自己的fact.cpp和factMain.cpp,这两个文件都应该
导入上一小节的练习中编写的Chapter6.h头文件。通过这些文件,理解
你的编译器是如何支持分离式编译的。
【出题思路】
理解分离式编译的机制。
【解答】
fact.cpp文件的内容是:
#include "Chapter6.h"
using namespace std;
int fact(int val) {
if(val < 0) {
return -1;
}
int ret = 1;
for(int i = 1; i != val + 1; ++i) {