C++ Primer习题集之第六章:函数(Chapter 6)

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) {
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值