2-x 第二章概要与测验
章节回顾
函数function是一组可重复使用的语句序列,旨在完成特定任务。用户自行编写的函数称为用户定义user-defined函数。
函数调用function call是指示CPU执行函数的表达式。发起函数调用的函数称为调用方caller,被调用的函数称为被调用方callee或被调用called函数。进行函数调用时切勿忘记添加括号。
函数定义中的花括号及内部语句称为函数体function body。
返回值的函数称为值返回函数value-returning function。函数的返回类型return type决定其返回值的类型,而返回语句return statement则指定具体返回给调用者的值。返回值return value通过值传递方式return by value从函数复制回调用方。非void类型函数若未返回值将导致未定义行为。
main函数的返回值称为状态码status code,用于告知操作系统(及所有调用程序)程序执行结果。约定上返回值为0表示成功,非零值表示失败。
践行DRY原则——“不要重复自己”。善用变量和函数消除冗余代码。
返回类型为void的函数不向调用方返回值。不返回值的函数称为void函数void function或无值返回函数non-value returning function。void函数不能在需要返回值的场景中调用。
若返回语句非函数末尾语句,则称为提前返回early return。此类语句将使函数立即返回调用方。
函数形参function parameter是函数中使用的变量,其值由函数调用方提供。实参argument则是调用方传递给函数的具体数值。当参数值被复制到参数变量时,称为值传递pass by value。
函数参数及函数体内部定义的变量称为局部变量local variables。变量存在的时段称为其生命周期lifetime。变量在运行时runtime(即程序执行期间)被创建和销毁。变量的作用域scope决定其可见与可用的位置。当变量可见且可用时,称其在作用域内in scope;当不可见时则不可用,称其超出作用域out of scope。作用域是编译时属性compile-time,即在编译阶段强制执行。
空白符Whitespace指用于格式化的字符。在C++中包括空格、制表符和换行符。
前向声明forward declaration允许我们在实际定义标识符前告知编译器其存在。为函数编写前向声明时,需使用函数原型function prototype声明——包含函数返回类型、名称及参数(不含函数体),末尾跟随分号。
定义definition(对函数和类型而言)或实例化(对变量而言)会实际实现标识符。声明declaration则是告知编译器标识符存在的语句。在C++中,所有定义同时充当声明。纯声明Pure declarations指既非定义的声明(如函数原型)。
大多数非简单程序包含多个文件。
当两个标识符以编译器或链接器无法区分的方式引入同一程序时,将因命名冲突naming collision导致编译或链接错误。命名空间namespace可确保其内部所有标识符唯一性,std命名空间即为此类命名空间。
预处理器preprocessor是在代码编译前运行的处理过程。指令Directives是针对预处理器的特殊指令,以#符号开头并以换行符结束。宏macro是定义输入文本如何转换为替换输出文本的规则。
头文件Header files用于向代码文件传播声明。使用#include指令时,该指令会被包含文件的内容所替换。包含头文件时,系统头文件(如C++标准库中的文件)应使用尖括号括起,用户自定义头文件(即您编写的文件)则使用双引号括起。包含系统头文件时,若存在无.h扩展名的版本,应优先包含该版本。
头文件保护机制Header guards可防止同一头文件内容被重复包含于单个代码文件中,但无法阻止同一头文件内容被包含于多个不同代码文件中。
测验时间
请务必使用编辑器的自动格式化功能,保持格式统一并提升代码可读性。
问题 #1
编写单文件程序(命名为 main.cpp),从用户处读取两个独立整数,将其相加后输出结果。程序需使用三个函数:
- 函数“readNumber”用于获取(并返回)用户输入的单个整数。
- 函数“writeAnswer”用于输出结果。该函数应接收单个参数且无返回值。
- 使用main()函数将上述函数整合。

显示提示
提示:您无需编写单独的函数来执行加法操作(直接使用运算符+即可)。
显示提示
提示:你需要调用两次 readNumber() 函数。
显示解决方案
main.cpp
#include <iostream>
int readNumber()
{
std::cout << "Enter a number to add: ";
int x {};
std::cin >> x;
return x;
}
void writeAnswer(int x)
{
std::cout << "The answer is " << x << '\n';
}
int main()
{
int x { readNumber() };
int y { readNumber() };
writeAnswer(x + y); // using operator+ to pass the sum of x and y to writeAnswer()
return 0;
}

问题 #2
修改练习#1中的程序,将readNumber()和writeAnswer()移至名为“io.cpp”的独立文件。通过前向声明在main()中调用它们。
若遇到问题,请确保“io.cpp”已正确添加至项目中以便编译。
显示解决方案
io.cpp
#include <iostream>
int readNumber()
{
std::cout << "Enter a number to add: ";
int x {};
std::cin >> x;
return x;
}
void writeAnswer(int x)
{
std::cout << "The answer is " << x << '\n';
}
main.cpp
// We don't need to #include <iostream> since main.cpp doesn't use any input/output functionality
// These are the forward declarations for the functions in io.cpp
int readNumber();
void writeAnswer(int x);
int main()
{
int x { readNumber() };
int y { readNumber() };
writeAnswer(x+y);
return 0;
}

问题 #3
修改问题 #2 中编写的程序,使其通过头文件(命名为io.h)调用函数,而非在代码文件(.cpp)中直接使用前向声明。确保头文件使用头文件保护机制。

显示解决方案
io.h
#ifndef IO_H
#define IO_H
int readNumber();
void writeAnswer(int x);
#endif
io.cpp
#include "io.h"
#include <iostream>
int readNumber()
{
std::cout << "Enter a number to add: ";
int x {};
std::cin >> x;
return x;
}
void writeAnswer(int x)
{
std::cout << "The answer is " << x << '\n';
}
main.cpp
#include "io.h"
int main()
{
int x { readNumber() };
int y { readNumber() };
writeAnswer(x+y);
return 0;
}
虽然从技术上讲,io.cpp 并不需要包含 io.h,但代码文件包含其对应的头文件是最佳实践。这在第 2.11 课——头文件中有所涉及。
若编译时出现如下错误:
unresolved external symbol "int __cdecl readNumber(void)" (?readNumber@@YAHXZ)
undefined reference to `readNumber()'
则可能是忘记在项目中包含io.cpp文件,导致readNumber()(及writeAnswer())的定义未被编译到项目中。

浙公网安备 33010602011771号