章节回顾
函数是一系列可重用的语句,旨在完成特定工作。你自己编写的函数称为用户定义函数。
函数调用是告诉 CPU 执行函数的表达式。发起函数调用的函数是调用者,被调用的函数是被调用者或被调用函数。执行函数调用时不要忘记包含括号。
函数定义中的花括号和语句称为函数体。
返回值的函数称为返回值函数。函数的返回类型表示函数将返回的值的类型。return 语句确定返回给调用者的特定返回值。返回值从函数复制回调用者——此过程称为按值返回。如果非 void 函数未能返回值,将导致未定义行为。
函数 main 的返回值称为状态码,它告诉操作系统(以及调用你的程序的任何其他程序)你的程序是否成功执行。根据约定,返回值为 0 表示成功,非零返回值表示失败。
实践 DRY 编程——“不要重复自己”。利用变量和函数来删除冗余代码。
返回类型为 void 的函数不向调用者返回值。不返回值的函数称为 void 函数或不返回值函数。void 函数不能在需要值的地方调用。
函数中不是最后一条语句的 return 语句称为提前返回。这样的语句会使函数立即返回给调用者。
函数参数是函数中使用的变量,其值由函数的调用者提供。实参是从调用者传递给函数的特定值。当实参被复制到参数中时,这称为按值传递。
函数参数和函数体内部定义的变量称为局部变量。变量存在的时间称为其生命周期。变量在运行时创建和销毁,即程序运行时。变量的作用域决定了它在哪里可以被看到和使用。当变量可以被看到和使用时,我们称其为在作用域内。当它不能被看到时,它就不能被使用,我们称其为超出作用域。作用域是编译时属性,这意味着它在编译时强制执行。
空白字符是指用于格式化目的的字符。在 C++ 中,这包括空格、制表符和换行符。
前向声明允许我们在实际定义标识符之前告诉编译器标识符的存在。要为函数编写前向声明,我们使用函数原型,它包括函数的返回类型、名称和参数,但没有函数体,后跟一个分号。
定义实际上实现了(对于函数和类型)或实例化了(对于变量)一个标识符。声明是告诉编译器标识符存在的语句。在 C++ 中,所有定义都充当声明。纯声明是那些不是定义的声明(例如函数原型)。
大多数非平凡程序包含多个文件。
当以编译器或链接器无法区分的方式将两个标识符引入同一程序时,编译器或链接器将由于命名冲突而报错。命名空间保证命名空间内的所有标识符都是唯一的。std 命名空间就是这样一个命名空间。
预处理器是在代码编译之前运行的一个过程。指令是给预处理器的特殊指令。指令以 # 符号开头并以换行符结尾。宏是定义如何将输入文本转换为替换输出文本的规则。
头文件是旨在将声明传播到代码文件的文件。使用 #include 指令时,#include 指令被包含文件的内容替换。包含头文件时,包含系统头文件(例如 C++ 标准库中的头文件)时使用尖括号,包含用户定义头文件(你编写的头文件)时使用双引号。包含系统头文件时,如果存在,请包含不带 .h 扩展名的版本。
头文件卫士防止头文件的内容被多次包含到给定的代码文件中。它们不能防止头文件的内容被包含到多个不同的代码文件中。
小测验时间
请务必使用编辑器的自动格式化功能,以保持格式一致性并使代码更易于阅读。
问题 #1
编写一个单文件程序(名为 main.cpp),它从用户那里读取两个单独的整数,将它们相加,然后输出答案。该程序应使用三个函数
- 应使用名为“readNumber”的函数从用户那里获取(并返回)一个整数。
- 应使用名为“writeAnswer”的函数输出答案。此函数应接受一个参数,并且没有返回值。
- 应使用 main() 函数将上述函数连接在一起。
问题 #2
修改你在练习 #1 中编写的程序,以便 readNumber() 和 writeAnswer() 位于名为“io.cpp”的单独文件中。使用前向声明从 main() 访问它们。
如果你遇到问题,请确保“io.cpp”已正确添加到你的项目中,以便它被编译。
问题 #3
修改你在 #2 中编写的程序,使其使用头文件(名为 io.h)访问函数,而不是直接在你的代码 (.cpp) 文件中使用前向声明。确保你的头文件使用头文件卫士。
如果你编译程序并出现以下错误之一
unresolved external symbol "int __cdecl readNumber(void)" (?readNumber@@YAHXZ) undefined reference to `readNumber()'
那么你可能忘记将 io.cpp
包含在你的项目中,因此 readNumber()
(和 writeAnswer()
)的定义没有被编译到你的项目中。