从零开始 / 一次只解释眼前的一步
第一份程序:
保存、编译与顺序执行。
改了源码,为什么运行结果可能没变?这一章从一份空文件开始,让你亲手分清源码、程序和它们执行时的状态。
这是首个完整试学章,5小时尚未经过真实学习者校准。可分多次完成;工具下载的额外等待单独记下。手机可读图和做预测,编译需要电脑终端。
这一章怎样学
先读一小段,写下预测,再运行程序。每次只改一个条件,最后关掉示例,从空文件独立写一次。遇到错误,把第一条报错和自己的修复记下来;不用赶着把页面滚到底。
这里先学一个整数、两份独立的值和按顺序执行。第01章结束时,你应能解释自己程序里的每一个符号,以及“保存了”为什么不等于“新程序已生成”。
这章从一个很实际的问题开始: 我已经改了 .cpp 文件,为什么屏幕上的数字还是旧的? 要回答它,我们要分清你编辑的文字、编译器生成的程序,以及程序运行时保存的数值。每次只增加一个概念;不要求你提前会终端、变量或普通函数。
本章对应一个完整学习单元,五节合计预估5小时。这个数字包括慢读、预测、实际操作和回头找错,是设计估算,没有经过个人试学校准。可以分几次完成;打开页面或运行下载示例,不代表你已经能够独立完成。
准备一个纯文本编辑器和终端。本章操作适用于macOS的Terminal及常见Linux终端;命令不是Windows原生PowerShell教程。先跟01.1准备文件,到了01.2才写第一份源码。遇到不认识的符号时,请沿本章顺序读。
01.1 文件、终端和编译器 · 预估45分钟
先认清手上是哪一种东西
源文件(source file) 是一份纯文本。比如 hello.cpp,里面保存你写的C++文字。.cpp是文件名的后缀,常用来表明它是C++源码。把一份文字改名为 .cpp,并不意味着文字已经符合C++规则。
可执行程序(executable) 是构建成功后得到的另一个文件。本章让它叫 hello,没有 .cpp 后缀。你编辑 hello.cpp;运行的是 hello。它们不是同一个文件的两种显示方式。暂时把编译(compile)理解为:让工具检查保存的源码,并把它构建成计算机能够运行的程序。这条命令也会完成生成可执行文件所需的后续连接工作;内部阶段留到后面的构建章节展开。
终端(terminal) 是输入命令、查看结果的窗口;其中的命令解释器叫 shell 。你输入一行命令并按回车,shell负责找到工具并执行。本页标为 sh 的代码框放的是终端命令;标为 cpp 的框放的是源文件内容。不要把编译命令贴进 .cpp,也不要把整个C++程序贴进shell。
知道自己站在哪个目录
目录就是文件夹。终端有一个“当前目录”,不带完整路径的文件名通常从这里寻找。先认识三个小操作:pwd显示当前目录;ls列出当前目录的文件名;cd改变当前目录。命令和后面的内容用空格分开,每行按一次回车。示例省略了终端自己显示的提示符,不需要补上 $ 或 %。
先输入下面一行,看看当前目录在哪里:
pwd
再看文件列表:
ls
为这章准备一个专用文件夹。shell中的 ~代表你自己的用户目录;mkdir表示新建目录。下面目录名含一个空格,所以用英文双引号把整个名字包起来,避免被拆成两个名字。
cd ~
mkdir "cpp start"
cd "cpp start"
pwd
如果你之前已经建过这个目录,mkdir可能提示它已存在;这不意味着C++有错误。进入后先用 ls检查:若已有自己的练习文件,回到用户目录,换一个新目录名(例如 cpp start 2),再重复新建和进入的操作,避免覆盖以前的作品。pwd最后显示的路径应以你选择的目录名结尾。以后找不到源码,先查当前位置和文件名,不要先改程序。
g++和clang++都可以用,先看它们实际是谁
g++通常是GCC的C++编译入口,clang++是Clang的C++编译入口。它们都能处理本章的标准C++代码;报错措辞、版本与默认设置可以不同。尤其在macOS上,名为 g++ 的命令也可能实际调用Apple Clang。 名字是入口,版本输出才是判断当前工具身份的依据。
--version是询问工具版本的选项,不会编译你的文件。分别检查这两行:
g++ --version
clang++ --version
如果其中一个找不到,而另一个能显示版本,先用能工作的那个。若两个都显示Apple Clang,就不要把这次操作记录成“用两种不同编译器验证过”。若两个都无法使用,需要先完成系统的C++工具安装;那属于环境准备,不能用“看过命令”代替一次成功构建。以下统一写 g++;你使用Clang时,只需将命令开头换成 clang++,后面的本章参数保持一样。
macOS上还没有编译器:先完成这一步
在终端输入 xcode-select --install。它请求系统安装Command Line Tools(命令行开发工具),不是在编译源码;按系统窗口完成安装后,再运行上面的版本命令。安装需要的下载等待单独记为环境准备。若系统说工具已安装但版本命令仍失败,保留原始提示,先解决工具配置,再继续;不要反复修改C++代码。Apple的安装说明可用于核对系统提示。
Linux的安装命令依发行版而定;可以先查看已有的编译入门中的环境准备,完成版本检查后回到本节。这里不要求重装已经可用的工具。
保存在哪里,命令就指向哪里
在编辑器中创建纯文本文件 hello.cpp,保存到刚才的 cpp start目录。不要用会保存字体、版式的富文本文档;还要确认没有被自动加成 hello.cpp.txt。如果使用终端里的 nano编辑器,nano hello.cpp表示打开或创建这个文件;写完后按Control与O保存,按回车确认文件名,再按Control与X退出。这里Control键不是macOS的Command键。源码在下一节提供,现在先留空并保存即可。
nano hello.cpp
用 ls看到 hello.cpp,只能证明文件存在,不能证明内容正确。下一节写好内容并保存后,才执行这条构建命令:
g++ -std=c++20 hello.cpp -o hello
逐块读它:g++是工具;-std=c++20选择C++20语言模式;hello.cpp是输入源文件;-o意为指定输出文件,紧跟的 hello就是生成的程序名。不要把输出名字写成 hello.cpp,输入和输出必须分开。编译成功时可能没有任何文字提示,这是正常的。
运行用另一条命令:
./hello
这里 .表示当前目录,/把目录与文件名分开;./hello就是“运行当前目录的hello文件”。单写 hello会让shell按自己的查找规则找命令,未必会找到当前目录中的它。运行 ./hello.cpp也不是在请求编译。
认识两步后,可以把它们连在一起。shell的 &&表示左边命令成功后才执行右边命令 ;它不是本章C++代码里的符号。这样编译失败时,就不会顺手执行之前留下的旧程序。
g++ -std=c++20 hello.cpp -o hello && ./hello
源码和程序,是两份不同的文件
编辑器把你输入的C++文字保存为 hello.cpp。程序文件 hello 还不存在。
编译命令读取 hello.cpp,完成编译和链接,用 -o hello 指定生成的程序名。
./hello 启动当前目录中的程序文件。它执行当时编译进去的指令;不会替你读取或编译编辑器里的新内容。
保存新的 hello.cpp 只更新文字文件。hello 仍是上一次编译的程序,直接运行仍得到旧结果。
再次编译成功后,hello 才对应新源码。把编译和运行用 shell 的 && 连接,可以在编译失败时阻止继续运行旧程序。
查看所有步骤的文字与数值
- 1 · 先保存文字
hello.cpp:已保存的源码;hello:尚未生成
编辑器把你输入的C++文字保存为 hello.cpp。程序文件 hello 还不存在。
- 2 · 编译器读取已保存的源码
hello.cpp:输入文字;编译 + 链接:正在生成程序;hello:生成的程序
编译命令读取 hello.cpp,完成编译和链接,用 -o hello 指定生成的程序名。
- 3 · 启动程序
hello.cpp:本次运行不读取它;./hello:运行已生成的程序
./hello 启动当前目录中的程序文件。它执行当时编译进去的指令;不会替你读取或编译编辑器里的新内容。
- 4 · 只改源码
hello.cpp:已保存新内容;hello:还是上次的程序
保存新的 hello.cpp 只更新文字文件。hello 仍是上一次编译的程序,直接运行仍得到旧结果。
- 5 · 编译成功,才运行新版
hello.cpp:新内容;编译成功:更新 hello;./hello:运行新版
再次编译成功后,hello 才对应新源码。把编译和运行用 shell 的 && 连接,可以在编译失败时阻止继续运行旧程序。
小练习:分清动作与对象
你在编辑器里改了 hello.cpp,尚未保存,然后运行 ./hello。这一步会读取编辑器里未保存的新文字吗?要让新文字产生新的运行结果,需要依次做什么?
提示与答案:先区分两个文件
不会。./hello执行已有程序,不会自动读取编辑器内容。先保存 hello.cpp,再成功编译生成 hello,最后运行 ./hello。即使你已经保存,仍然需要重新编译;保存只更新源码文件。
01.2 逐个读懂最小程序 · 预估90分钟
在拼完整程序前,先读清每块的意思
#include <iostream>是放在源码开头的包含指令。#include告诉构建工具引入指定头文件提供的声明;iostream是C++标准库中提供常用输入输出声明的头文件名。本章只用输出。两边的尖括号标出这个头文件名,不是在做大小比较。这一行末尾不加分号。它在构建时起作用,不是程序运行时“先去网上下载一个库”。
int main()标出本程序的入口函数。先把 函数(function) 理解为有名字的一段工作;这一章只使用规定的入口 main,还不自己编写其他函数。开头的 int在这里说明 main返回一个整数;main这个入口名字要保持小写;后面的空圆括号表示我们选择了不接收参数的入口写法,并非可以随手删掉的装饰。
紧随其后的一对花括号 {和 }把入口内部的语句括起来,组成一个 块(block) 。在本章的顺序程序中,语句按出现的先后执行。每行前面的缩进帮助人看出它属于哪一块;真正规定边界的是花括号。打开一个块,就要有对应的结束花括号。
std::cout是标准库提供的标准输出对象,本章通常把内容送到终端显示。std是标准库名字所在的命名空间,作用类似名字的归属范围;中间的 ::读作“在这个范围中查找”,所以整体读作“std中的cout”。你现在需要会识别这个完整名字,不需要会定义自己的命名空间。不要省掉 std::,也不要把两个冒号换成一个。
在 std::cout后面的 <<是输出运算符 :把右边的值写到左边的输出中。这里根据左右对象判断它表示输出;现在不引入它在其他类型上的用途。一条输出语句可以连续写多个 <<,按顺序输出多个内容。
"Hello"是一段固定文本,叫字符串字面量。英文双引号标出文本边界,引号自身不显示;大小写按原样输出。'\n'使用英文单引号,表示一个换行字符:反斜杠与字母n合起来是换行的写法,输出后接下来的内容到新的一行。它不是要显示一个反斜杠再显示n。双引号写文本,单引号写这里的一个字符;不要换成中文弯引号。
输出语句最后的 ;叫 分号(semicolon) ,标明这条语句结束。换行排版不能代替分号。return 0;则表示结束 main并向运行它的环境返回整数0;这个数字是 退出状态(exit status) ,惯例中0表示成功。它不会自动显示在屏幕上。入口末尾虽允许省略这条成功返回,本章显式写出,方便区分“打印什么”与“向shell报告什么”。
支持样本:把已经解释的部分放到一起
现在把下面内容写入 hello.cpp,每个符号都应能在上面的解释中找到。这个小样本只用于确认输入、保存和运行链路;本章的两个完整状态例子在下一节和01.4。
#include <iostream>
int main() {
std::cout << "Hello" << '\n';
return 0;
}
保存后回到终端,确认仍在 cpp start目录,再执行已经学过的编译与运行命令。预期程序输出如下,末尾还有一次换行:
Hello
可以按执行过程复述:进入 main,把文本Hello写出,再写换行,随后 return 0;报告正常结束。包含指令不在这个运行过程里;输出中没有双引号,没有分号,也没有最后那个0。
第一次只改一个条件
把输出文本中的 Hello改成 Ready,其余内容保持。先在纸上预测会显示哪几个字符,再保存、编译、运行。若结果符合预测,说明你建立了“修改文本—生成新程序—观察输出”的联系。若还显示Hello,先回查保存、构建是否成功及运行文件名;暂时不要怀疑 cout会随机忽略文字。
小练习:屏幕文字与退出状态
这个程序输出了Hello,却写着 return 0;,二者矛盾吗?再指出本节的花括号、分号、尖括号分别在哪里承担什么作用。
答案:沿构建与运行分别解释
不矛盾。Hello由输出语句产生,0由 return交给运行环境,两者走不同用途的通道。花括号围住 main内部的语句;分号结束输出语句和返回语句;尖括号围住头文件名。
01.3 创建、读取与修改一个整数 · 预估60分钟
给一个数取名字
写固定文本很快会不够用。假设你需要记录手上有多少件物品,随后多了一件,想观察前后的数。 对象(object) 可以先理解为程序执行时保存一个值的地方;本章用一个有名字的整数对象来保存数量。 变量(variable) 这个名字,让我们能在源码中找到它并读取或改变它。
int count{2};要拆成四部分读:int选择整数类型;count是我们起的变量名;{2}给新对象初值2;;结束这条声明。创建对象时给定初值,叫 初始化(initialization) 。这里的2是直接写在源码中的小整数,叫整数字面量;本章仅使用很小的数,不涉及整数范围边界。
此处花括号属于初始化,里面是要保存的初值;main后面的花括号属于块,里面是一组语句。两者由位置和整体结构区分,不能见到花括号就假定它一定在创建一个代码块。
变量名要写得一致:count与 Count是不同名字。先用字母开头、不含空格的英文名字,后面可以有数字;本章不使用下划线命名。输出 count读到的是这个对象当前的数值;输出 "count"则只显示五个字母。去掉或加上双引号,会改变你要求输出的东西。
赋值为什么能把2改成3
count = count + 1;叫 赋值(assignment) 。右边先读取旧的count,再做加法;把得到的结果写回左边已经存在的count。若原值为2,过程就是读2、算2加1得到3、保存3。=在这条C++语句里表示赋值,不是数学里要求两边永久相等的等号。也不是创建第二个同名对象:这条语句前面没有再写 int。
读取本身不会改变count;一次赋值才改变它的当前值。已经打印出来的2也不会随之后的赋值变成3,终端保存的是此前输出的文字。
完整例子一:先看2,再看3
输入不需要你在运行时键入;初值2已经写在源码里。读完之前先停一下:你预测程序会输出一行还是两行?两行各是什么?把文件保存为 count.cpp后再运行。
#include <iostream>
int main() {
int count{2};
std::cout << count << '\n';
count = count + 1;
std::cout << count << '\n';
return 0;
}
g++ -std=c++20 count.cpp -o count && ./count
| 执行到哪里 | count当前值 | 这一步新增的输出 | 原因 |
|---|---|---|---|
| 初始化结束 | 2 | 无 | 新对象获得初值 |
| 第一次输出后 | 2 | 第一行2 | 读取不改值 |
| 赋值结束 | 3 | 无 | 先算右边,再保存 |
| 第二次输出后 | 3 | 第二行3 | 现在读到新值 |
完整输出为:
2
3
检查时同时核对数值与顺序。只有一个3,不能说明你完成了“观察前后状态”的任务;出现2与3但没有换行,也说明输出格式和当前示例不同。先说清差异,再确定是哪一条语句负责。
小练习:只改加法右边的一个数
保持初值2,把加法中的1改成2。其他字符不变。先预测两行输出,再保存、编译、运行。为什么第一行没有一起变?
提示与答案:给每次输出标时间
两行依次为2、4。第一次输出发生在赋值之前,所以仍读取初值2;后来才计算2加2并保存4。不要因为最后值为4,就把先前已经发生的输出也倒改成4。
01.4 两个独立状态与重新构建 · 预估60分钟
复制一个值,没有建立自动同步关系
int a{3};创建第一个整数对象,保存3。随后 int b{a};读取a 此刻的值 ,拿这个3初始化另一个整数对象b。这里的b有自己的值,不是a的另一个名字。现在才执行 a = 5;,只改动a,b仍为3。把过程画成两个格子就能避免“它们曾经一样,所以今后也一样”的误解。
| 时刻 | a的格子 | b的格子 | 变化的原因 |
|---|---|---|---|
| 创建a后 | 3 | 尚未创建 | 只有a得到初值 |
| 创建b后 | 3 | 3 | 读取当时的a,初始化b |
| 给a赋值后 | 5 | 3 | 这次赋值的左边只有a |
“尚未创建”不表示b存着0,也不是让你运行一条提前读取b的语句;它表示执行还没有到声明b的位置。本章只在声明与初始化之后读取变量。
给 b 一个初值,不会把 a 和 b 绑在一起
建立名为a的整数对象,放入3。此时b还没有建立。
读取这一刻a中的3,用它初始化另一个整数对象b。图中的两个格子表示两个独立对象,不是具体物理地址。
把5写回已经存在的a。没有语句要求修改b,所以b仍为3。
先读取a再输出;输出不会改写a。按例二的完整输出语句,先出现5和换行。
接着读取b并输出3。两个整数对象没有自动跟随关系;初值来自a,不表示以后的值一直相同。
查看所有步骤的文字与数值
- 1 · int a{3};
a:3;b:尚未建立;终端输出:还没有
建立名为a的整数对象,放入3。此时b还没有建立。
- 2 · int b{a};
a:3;b:3;终端输出:还没有
读取这一刻a中的3,用它初始化另一个整数对象b。图中的两个格子表示两个独立对象,不是具体物理地址。
- 3 · a = 5;
a:5;b:3;终端输出:还没有
把5写回已经存在的a。没有语句要求修改b,所以b仍为3。
- 4 · std::cout << a << '\n';
a:5;b:3;终端输出:5
先读取a再输出;输出不会改写a。按例二的完整输出语句,先出现5和换行。
- 5 · std::cout << b << '\n';
a:5;b:3;终端输出:先5,再3
接着读取b并输出3。两个整数对象没有自动跟随关系;初值来自a,不表示以后的值一直相同。
完整例子二:a改成5,b仍是3
下面没有键盘输入,固定起点是a的初值3。先遮住后面的输出,把初始化、赋值与输出语句按顺序口述一遍,再将完整程序保存为 copy.cpp。
#include <iostream>
int main() {
int a{3};
int b{a};
a = 5;
std::cout << a << '\n';
std::cout << b << '\n';
return 0;
}
g++ -std=c++20 copy.cpp -o copy && ./copy
预期输出:
5
3
第一行来自a,第二行来自b。编译器没有漏掉“让b也变”的工作,源文件根本没有要求它去改b。若你希望b以后再次得到a的新值,必须在相应位置明确写一次给b的赋值;这已是一个不同的程序,要重新预测,而不能把它当作当前例子的隐含步骤。
一次真实的旧程序观察
先使用下面这个 重编译支持版本 ,仍保存为你工作目录里的 copy.cpp。它只是完整例子二少了一条修改a的语句,并不是新的主例子。
#include <iostream>
int main() {
int a{3};
int b{a};
std::cout << a << '\n';
std::cout << b << '\n';
return 0;
}
成功编译、运行,确认两行是3、3。然后在创建b之后、第一次输出之前插入 a = 5;,保存;这时源码回到完整例子二。 暂时不要编译 ,只执行已经存在的程序:
./copy
它仍会输出3、3。原因是保存只改变 .cpp,没有改动上次生成的程序文件。随后真正重编译并运行:
g++ -std=c++20 copy.cpp -o copy && ./copy
此时才应输出5、3。每一次启动又从 main按源码的固定初值开始;上次运行结束时的a不会自动留给这一次。我们要区分三件事:编辑器里当前的文字、磁盘上保存的源码、最近一次成功构建的程序。若还显示3、3,依次查“是否保存到这个目录”“编译是否成功”“输入输出名字是否一致”。
小练习:把一条语句移动位置
若把 a = 5;移动到创建b之前,其他语句的顺序保持,最终两行是什么?这会不会证明复制总是让两个变量同步?
答案:区别复制时刻与后续同步
这次是5、5。因为创建b时,a已经变成5,所以b的初值就是5。它们仍然是两个独立对象;只是复制发生在另一个时刻。不能用一次数值相等,推导以后对a的修改都会传给b。
01.5 读第一条报错,再做独立练习 · 预估45分钟
先判断错误发生在哪一步
第一次遇到红色报错时,不用一口气读完整页诊断。先回答:这是shell找不到命令或文件,还是编译器发现源码不符合规则,还是程序已经运行但结果不对?它们需要的修复不同。
| 看到的现象 | 先检查什么 | 此时不要做什么 |
|---|---|---|
| 找不到g++或clang++命令 | 工具是否安装、当前入口名是否正确 | 不要修改变量名来碰运气 |
| 编译器说找不到输入文件 | pwd、ls、命令中的文件名与大小写 |
不要把输出程序名当输入源码 |
error后带源码位置 |
第一条诊断所指语句及上一条语句 | 不要一次改十个地方 |
| 编译成功,但输出与预测不同 | 运行的是哪个程序、执行顺序、读了哪个变量 | 不要把“能编译”当作结果正确 |
诊断样本:丢了一枚分号
下面是 故意不能编译的支持样本 ,保存为 missing-semicolon.cpp。先自己找出与已学规则冲突的位置,再让编译器提供证据。它没有合法的预期运行输出。
#include <iostream>
int main() {
int count{2}
std::cout << count << '\n';
return 0;
}
g++ -std=c++20 missing-semicolon.cpp -o missing-semicolon
常见诊断含文件名、行号,有时还有列号,以及 error后的原因描述。例如“expected ‘;’”表示这里期待分号;不同工具的完整句子和指向位置可能不同,不要求逐字匹配教程截图。这个文件第4行的初始化语句少了分号。工具也可能读到下一条输出语句时才确认拼接不成立,因此箭头附近只是线索,不保证错误字符一定在箭头正下方。
只在 int count{2}末尾补分号,保存,再编译运行。修正后应打印2并换行。不要给头文件包含指令随意加分号,也不要通过删除输出语句来躲开诊断;应修复真正违反规则的那条声明。
退出状态怎么观察
shell会保存刚刚结束的命令的退出状态。echo是显示文字的shell命令;$?是shell提供的“上一条命令退出状态”。下面第二行要紧跟第一行,中间不要先运行 ls等其他命令,否则看的就是另一条命令的状态。这些符号仍然写在终端,不写进C++源文件。
./hello
echo $?
若你保留了支持样本里的 return 0;并正常运行,第二条命令应显示0。你可以只把它改成 return 1;,保存并重编译,再分开执行上面两行:文本仍按输出语句显示,退出状态变成1。这个受控改动说明“有输出”与“报告成功”并不是一回事。观察完成后恢复0,并重新编译。
如果要单独检查编译状态,同样在编译命令后立刻运行 echo $?;不要把编译和程序的两种状态混为一谈。前面学的 &&依靠成功状态决定是否执行右边,因此源码编译失败时会跳过运行。但旧程序文件可能仍在目录里,手动再运行它依然可能看到旧结果。
小练习:先修哪一处
你删除了一个分号,编译失败;随后手动运行旧程序,它照常输出2。能否因此判断新源码没有问题?编译器把位置标在下一行时,你应该怎么找?
答案:运行成功的是哪个版本
不能。旧程序的运行不能验证没有成功构建的新源码。读第一条诊断,检查所指语句和紧邻的上一条语句是否完整,补回真正缺失的分号,然后保存并成功构建。使用编译成功后才运行的 &&可以减少误跑旧程序;它不能替你判断数值结果是否正确。
独立练习阶梯:预测 → 补全 → 找错 → 迁移
这四步只使用本章讲过的构造,计入本章操作时间。每次先写自己的答案,再打开对应答案;看过答案后可以回到空文件重做,但要如实记录哪一步需要帮助。
第一步,预测。 不运行完整例子二,画出a、b在各条语句后的状态,写最终输出,并说明为什么b不随a改变。
展开预测答案
a先得到3;b读取当时的a得到3;给a赋5后,a为5、b为3。最终先打印5,再打印3。理由必须包含“复制发生时读值”和“两个独立对象”,不能只说“我运行过是这样”。
第二步,补全。 在自己的 count.cpp中保留两条输出及入口结构,把初始化和赋值两条语句暂时移到草稿。合上主例子,根据“第一行2,第二行3”的要求,独立补回它们;每补一条说明它应位于哪次输出之前。
展开补全答案
第一次输出之前需要 int count{2};,两次输出之间需要 count = count + 1;。初始化只做一次,后面是修改已存在对象。重新编译,核对输出顺序2、3;只写最终的3不能完成要求。
第三步,找错。 有人认为count增加后,saved也会增加。他运行下面的诊断样本,并希望两行都是3。先分别说出每条输出实际在观察谁,指出哪一个判断错了。如果要求第二行也观察count的最新值,应该只改哪个名字?
#include <iostream>
int main() {
int count{2};
int saved{count};
count = count + 1;
std::cout << count << '\n';
std::cout << saved << '\n';
return 0;
}
展开找错答案
实际两行是3、2。第一条输出读取count的最新值,第二条读取独立副本saved;复制之后,给count加1不改变saved。若第二行的任务是再次观察count,把第二条输出中的 saved改为 count即可。不要为了让答案看起来一致,假装saved自动改变了。
第四步,迁移。 从空文件开始,自己选两个变量名。先让原变量得到4,再让副本读取这个4,输出二者,成功构建并保存实际结果。然后在创建副本之后加一条赋值,让原变量变成9;保存、重新编译,最后输出必须是9、4。保留前后两次编译命令及实际输出,不能只下载答案运行。
先看提示,再展开完整迁移参考
提示:决定副本初值的是它创建的时刻。第二次版本要先创建副本,再修改原变量,最后输出。第一次版本的预期为4、4;第二次版本为9、4。下面是第二次版本的一个参考,变量名可以不同。
我已独立尝试,查看参考源码
#include <iostream>
int main() {
int original{4};
int saved{original};
original = 9;
std::cout << original << '\n';
std::cout << saved << '\n';
return 0;
}本章完成时,应留下什么
留下自己的两份程序修改记录、预测与实际输出的对照,以及一条错误诊断和修复说明。你应能不看答案解释:哪个是源码,哪个是程序;为什么保存后还要编译;初始化与赋值有什么区别;为什么复制出的整数不会自动同步;缺分号时从哪里开始找。
完成本章独立迁移与口述后,进入第02章:类型与表达式。贯穿案例中的数组、循环和条件判断按后续章节的先备安排学习。不要把一次读完页面当成整个C++基础G0已经通过。
用于查证的资料
命令写法可查GCC的输出文件与整体选项及Clang命令指南。main的返回类型、入口形式与正常返回含义见C++工作草案的main条款,标准输出声明见iostream头文件概要。这些页面供核实具体规则;完成本章不要求先读懂它们的全部术语。本章使用C++20已有规则,实际工具版本以你的版本输出和构建记录为准。
关掉参考,再做一次
把理解说出来
先完成正文的独立迁移,再回答下面六题。它们只检验本章内容;打开答案、编译成功或填写用时,都不会自动通过本章,更不代表通过 G0。
解释
hello.cpp、g++和./hello分别是什么?为什么只保存hello.cpp不能让已有hello自动改变?
对照推理与英文回答
hello.cpp是源文件,g++是启动构建的工具入口,./hello要求shell运行当前目录中的程序hello。保存改变的是源文件;最近一次成功生成的程序仍反映当时的源码。要运行修改后的版本,应先保存,再确认编译成功,最后运行正确路径的程序。
The .cpp file contains source text. The compiler builds an executable from the saved file, and ./hello runs that executable. Saving the source does not rebuild the executable.解释与追问
输出语句里的std::、<<和'\n'分别做什么?return 0;会不会在Hello下面再打印一行0?
对照推理与英文回答
std::说明cout这个名字属于标准库的命名空间;这里的<<按输出用途把右侧内容依次写入cout;'\n'写出换行字符。return 0结束main并向运行环境报告成功状态,不会替你打印数字。追问时应区别屏幕输出和退出状态,不能只说0代表没有输出。
std:: identifies the standard-library namespace, and << sends values to cout in this expression. The newline character starts a new output line. Returning zero reports successful completion; it does not print a zero.代码预测
count初始化为2,先输出count,再执行count = count + 1,最后再输出count。两行是什么?赋值是否创建了新的count?
对照推理与英文回答
两行依次为2和3。声明负责创建并初始化count;赋值读取旧值2,加1,再写回已有对象。第一次输出在赋值前已经发生,因此不会被之后的修改改写。这里没有创建第二个同名对象。
The outputs are two and then three. The assignment reads the old value, adds one, and stores the result in the existing object. It does not create another count or change earlier output.预测与追问
a初值3,b用a初始化,随后只把a赋为5,最终二者各是多少?如果把给a赋5移到创建b之前呢?
对照推理与英文回答
原顺序中a为5、b为3,因为b复制创建时读到的3。移动赋值后,创建b时读到5,所以a与b都为5。这两种结果都不表示自动同步;差异来自复制发生的时刻。
In the original order, a is five and b is three because b copied the earlier value. If the assignment happens before b is initialized, both values are five. The objects are still independent.找错
初始化语句缺分号后,编译器把错误位置指向下一条输出语句。你运行目录里的旧程序仍看到正常输出。应如何定位和验证修复?
对照推理与英文回答
先看第一条诊断,检查所指语句与上一条声明的结束位置,补上真正缺失的分号并保存。旧程序能运行只说明旧的构建仍存在,不能验证新源码。重新成功编译后再运行并核对输出;不要为了消除箭头处的错误而随意删除输出语句。
I would inspect the first diagnostic and the statement immediately before it. Running an old executable does not validate source that failed to compile. After fixing the missing semicolon, I would rebuild successfully and compare the new output with my prediction.英文追问
Your compile command fails, but yesterday's executable is still present. What does using && change, and what does it not prove? Follow-up: does the command name g++ guarantee that GCC is the actual compiler?
对照推理与英文回答
shell中的&&仅在左侧命令报告成功后执行右侧,因此本次编译失败会跳过连接在右侧的运行命令。它不证明算法或输出正确,也不保证删除旧程序;手动运行旧文件仍可能成功。g++是命令入口名,macOS上可能指向Apple Clang,应检查--version,不能根据名字伪称已经跨编译器验证。
The shell runs the command after && only if the previous command succeeds. This prevents that command chain from running the old executable after a failed build, but it does not prove the result is correct. I check --version to identify the actual compiler; the name g++ alone is not enough on macOS.和正文是同一份源码
示例文件
先自己输入和预测,卡住时再下载对照。文件名相同不代表内容相同;把它们放在单独的练习目录中,避免覆盖自己的作品。
- count.cpp完整例子一:count先是2,再变成3
- copy.cpp完整例子二:a变成5,b仍为3
- hello.cpp支持样本:确认编译和输出链路
- copy-before.cpp重编译支持版本:尚未给a赋5
- missing-semicolon.cpp故意编译失败:初始化语句缺少分号 · 故意用于编译报错
- wrong-observation.cpp诊断支持样本:当前值与旧副本
- transfer.cpp独立迁移参考:原变量9,副本4
完整编译、输出和失败路径验证随本地构建记录保留;这里的程序没有调用加速卡。
帮助下一章更准确
记下你真正花的时间
每完成一个学习时段,再填实际分钟。环境准备、阅读推演、独立编码和卡点排查分别记录,避免同一段时间重复计算。离开吃饭或做其他事情的时间不算进去。
记录只保存在你的浏览器,可导出给我复盘。留空表示尚未记录,不等于零耗时;页面停留时间不会自动计为学习。不要把开发者检查时间填进来。
尚无真实试学用时。
换设备:导入记录,或取回损坏的旧记录
导入会合并时段,相同编号不重复累加;发生冲突会保留现有记录。
阅读记录与课程验收分别保存。
本章资料与查证
- GCC:整体选项 ↗
-o file指定输出;--version识别工具版本。
- Clang:命令指南 ↗
DESCRIPTION与Language Selection and Mode Options:构建工具入口及-std=c++20。
- C++工作草案:main入口 ↗
main返回int、空参数入口形式、从main返回和成功退出。
- C++工作草案:iostream头文件概要 ↗
iostream声明的标准输入输出对象。
本章独立解释所需读法;资料用于核对与补充。工具版本、操作系统和实际执行状态见自己的运行记录。