从“能编译”到“看得懂”
上一页中,你用编译器把一个源文件变成了可以运行的程序。现在我们放慢脚步,看看那个文件里到底写了什么。每个 C++ 程序都由同样几种结构组成,一旦你能叫出它们的名字,一个陌生的 .cpp 文件看起来就不再是一堆乱码了。
下面是一个完整的、可以运行的程序。先读一遍,然后我们逐行拆开来看。
五行有意义的代码,每一行都在做一件具体的事。我们一行一行来看。
#include 这一行
#include <iostream>
这一行引入 <iostream> 头文件,让你可以使用 cout 这样的输入输出功能。#include 是一条预处理指令:它在真正的编译之前运行,基本上就是把那个头文件的内容粘贴到你的文件里。
#include 行有两点特别之处:
- 它们以
#开头,这表明它们是预处理指令。 - 它们不以分号结尾。你马上就要养成“每行都以
;结尾”的习惯,而这是其中一个常见的例外。
如果你用了 cout 却忘了写 #include <iostream>,编译器不知道 cout 是什么,你会看到类似 'cout' was not declared in this scope 的错误。解决办法几乎总是补上漏掉的 include。
main 函数
int main() {
// ...
return 0;
}
main 是程序开始的地方。运行编译好的程序时,执行从 main 里的第一行开始,在 main 返回时停止。每个 C++ 程序都有且只有一个 main,不多也不少。
各个部分:
int:main会向操作系统交回一个整数。按照惯例,0表示“成功结束”。main():函数名,后面跟着放参数的括号(这里是空的)。{ ... }:花括号把函数体包起来,也就是要运行的语句。return 0;:结束main并报告成功。如果你不写,C++ 会当作main替你返回了0,但写出来能让你的意图一目了然。
语句与分号
语句是一条完整的指令。在 C++ 中,每条语句都以分号结尾:
C++ 完全忽略换行和多余的空格。编译器不在乎你把语句写在一行还是十行里,告诉它每条语句在哪里结束的是 ;。对编译器来说,下面三种写法完全一样:
int a = 1; int b = 2;
int a = 1;
int b = 2;
int
a
= 1;
这种自由带来了新手最常掉进去的坑:忘写分号。看看这里会发生什么。
int x = 5 // <-- no semicolon
int y = 10;
你会看到类似 expected ';' before 'int' 的错误。注意其中的坑:编译器读到下一行时才发现出了问题,所以报告的行号常常比真正出错的地方低一行。当错误提到缺少 ; 时,去检查报错那一行的上一行。
花括号定义代码块
有些语言用缩进给代码分组,C++ 用的是花括号 { }。一对匹配的花括号之间的所有内容就是一个代码块。函数体、循环和 if 语句都用到它:
if 花括号里的两行只在条件为真时运行。最后一个 cout 在花括号外面,所以它总会运行。
因为定义结构的是花括号而不是缩进,所以 C++ 并不强制你缩进。但你还是应该缩进:人正是靠缩进来读懂花括号定义的结构。一个常见的 bug 是花括号不匹配:每个 { 都需要一个对应的 }。如果你漏了一个,编译器通常会在文件末尾附近报错(expected '}' at end of input),离你真正出错的地方很远。数一数你的花括号,或者让编辑器帮你配对,能省去很多困惑。
用 cout 和 << 输出
你已经见过几次 cout 了。它是标准输出流,你用 << 运算符把值送给它(可以读作“把这个放进输出”):
你可以连续使用 <<,一次打印好几样东西。endl 结束这一行(它还会刷新缓冲区);转义序列 "\n" 是打印换行更轻量的方式。对简单的程序来说,用哪个都行。
至于顶部那行 using namespace std;:标准库中的名字都位于 std 命名空间里,所以完整的名字是 std::cout。写一次 using namespace std;,你就可以省掉 std:: 前缀,直接写 cout。在简短的示例里这很方便,但在较大的项目中,很多团队更喜欢明确写出 std::cout 来避免命名冲突。所以看到两种写法都不必惊讶。
现在你能读懂任何程序的骨架了
顶部的 include、作为入口的 main 函数、以分号结尾的语句、把代码分成块的花括号,以及用于输出的 cout <<:几乎你今后打开的每个 C++ 文件里都有这副骨架。语言的其余部分,都是叠加在这几种结构之上的细节。当一个程序看起来让人发怵时,先找到 main,再按顺序读里面的语句,结构会给你指路。
下一步:注释
现在你程序里的每一行都是编译器要读的代码。接下来我们会加上一些编译器有意忽略的行,也就是注释,让你可以给自己和其他读者留下说明。你会看到 // 单行注释和 /* ... */ 块注释两种形式,以及各自适合在什么时候使用。
常见问题
为什么每条 C++ 语句都需要分号?
C++ 会忽略换行和空白,所以它需要一个明确的标记来表示一条语句在哪里结束、下一条从哪里开始。这个标记就是分号 ;。漏写分号是新手最常见的编译错误,而且报告的行号往往是你漏写分号那一行的下一行。
C++ 中的 int main() 是做什么的?
main 是程序开始运行的函数:执行从它的第一行开始,在它返回时结束。每个 C++ 程序都必须有且只有一个 main。int 表示它会向操作系统交回一个整数状态码,按照惯例,0 表示成功。
cout 前面一定要写 std:: 吗?
要么写 std::cout(完整限定名),要么在文件顶部加一次 using namespace std;,之后直接写 cout 就行。在较大的文件里,完整限定的写法更安全;using namespace std; 是小示例和教程中常用的简便写法。