前言
最近在学习的时候发现自己陷入了一个瓶颈,就是发现自己过去主要的学习方式–看视频,大脑有点脱敏,且频频走神。看视频时常产生一种“我在进步”的错觉,但实际写代码时会大脑一片空白。
之前看过的一本书《1000hours》 里曾经提到一种理论,和我现在的情况很像,就是如果在学习一项技能,或者学习一个新事物时,如果仅仅是投入时间和金钱,但是没有投入最重要的注意力,则其效率会变的非常低下。
自己思考了一下发现自己现在日常生活中最专注的情况主要是:读书、和AI深度交流、完全沉浸式的娱乐;基于自己的专注能力,打算亲身实践一下新的学习方法,即啃高信息密度的书/文档、遇到不懂的问题问AI,遇到需要动图辅助理解的知识点再去找视频讲解,最后对每日啃过的内容进行一个思维整理输出,写为笔记方便复习,希望新的学习方法能改善我当前的学习困境。
本篇笔记的内容也主要集中于我在啃这个网站Learn C++ – Skill up with our free tutorials 文档内容的每日总结输出,和中间过程与AI探讨的一些小Tips和理解。
Day01
Programs and programming languages
计算机硬件的局限性与“二进制”语言
计算机的核心由 CPU(中央处理器) 和 内存(RAM) 组成。尽管 CPU 拥有强大的计算能力,但它本质上是非常“笨”的,只能识别电信号的通(1)与断(0)。这意味着,计算机原生唯一能理解的语言就是机器语言(Machine Language)。程序员在早期必须通过冗长的 0 和 1 序列来下达指令,这不仅极其违反人类直觉、容易出错,而且由于不同厂家的 CPU 指令集各异,一份代码无法在不同的电脑上通用,这种“不可移植性”严重限制了软件的发展。
低级语言的改良:汇编语言的过渡
为了缓解机器语言带来的痛苦,汇编语言(Assembly Language) 应运而生。它引入了“助记符”的概念,用简单的英文单词(如 ADD、MOV)代替了晦涩的二进制码。虽然这让代码变得稍微可读了一些,但它依然属于低级语言。汇编语言与底层硬件是一一对应的,程序员仍需关注寄存器和内存地址等硬件细节。此外,它依然需要一个名为“汇编器”的工具将其翻译回机器码,且跨平台运行的问题依然没有得到根本解决。
高级语言的革命:效率与跨平台的飞跃
为了让编程真正回归逻辑本身,高级语言(High-level Languages)(如 C++、C、Python)成为了现代开发的主流。高级语言最大的突破在于它允许程序员使用接近自然语言和数学逻辑的方式编写代码。更重要的是,它通过**编译器(Compiler)**实现了代码的“可移植性”:程序员只需编写一套逻辑,编译器就能根据不同的目标机器,将其翻译成对应的机器码。这种抽象不仅让代码更易维护,还让复杂软件的协作开发成为了可能。
Note 跨平台是高级语言与硬件脱钩后的必然产物。它将程序员从繁琐的硬件指令中解放出来,专注于逻辑表达。尽管 C++ 需要为不同平台重新编译(源码级跨平台),而 Java/Python 通过虚拟机实现(字节码级跨平台),但它们的本质逻辑是一致的:即通过一层“翻译中介”,屏蔽底层硬件差异。作为开发者,应当区分“语言特性”与“系统特性”,优先使用标准库以确保代码的可移植性。
翻译的双轨制:编译器Compiler与 解释器Interpreter
在高级语言进化的过程中,为了平衡“运行效率”与“开发便捷性”,分化出了两种核心的翻译策略。编译器路线(以 C/C++ 为代表)采取的是“离线翻译”模式,在程序运行前将整篇代码一次性转化为机器码。这就像翻译一本完整的书,虽然编译过程耗时,但产出的程序执行速度极快,且能在运行前通过严格的语法体检排除隐患。而解释器路线(以 Python/LISP 为代表)则采取“同声传译”模式,在程序运行时逐行翻译并执行。这种方式虽然牺牲了部分运行性能,但换来了极高的调试效率和灵活性,允许程序员即写即看结果。
如下为两种代码翻译方法的示意图:


Note “中间地带” (JIT)
现在很多语言(如 Java 或 C#)并不属于纯粹的其中一类。 它们先将代码编译成一种中间代码(Bytecode),然后在运行的时候,通过一个叫 JIT (Just-In-Time Compiler) 的技术,把经常运行的代码实时编译成机器码。这结合了二者的优点。
C++ 在其中的定位
C++ 坚定地选择了**“高级语言 + 编译器”**的组合。这一选择的本质是在追求极致的性能(通过编译器优化)的同时,赋予程序员强大的抽象能力(通过高级语法)。
跨平台特性:源码级移植性
C++ 被称为跨平台语言,是因为它实现了“源码级移植”。与 Java 的“一次编译,到处运行”不同,C++ 遵循的是“一次编写,到处编译”。通过针对不同硬件架构使用特定的编译器,C++ 能够确保在保持高性能的同时,让逻辑代码在 Windows、Linux、macOS 等多个系统中复用。但这也要求程序员尽量使用标准库,避免过度依赖某个系统的专用 API。

Example
假设现在我们要执行的操作是:将两个数字(5 和 10)相加,并将结果存入内存。
三种语言的对比展示
C++ (高级语言)
这是你写的代码。它关注的是逻辑和意图。其特点是:人类完全可读,变量名 a 和 b 具有语义,符号 + 直观。
| |
汇编语言 (Assembly - x86-64 架构)
这是编译器为你生成的中间代码。它关注的是寄存器和指令。你需要亲自管理 CPU 内部的存储单元(寄存器),并且每一步操作都必须拆解到最细微的动作。
| |
机器语言 (Machine Code - 十六进制表示)
这是计算机最终执行的内容。它关注的是电压开关信号。完全不可读。如果此时你换了一个 ARM 架构的 CPU(比如苹果 M1 芯片),这些二进制码将完全失效,电脑根本不知道你在说什么。
| |
C++ 编译器为你省去了哪些麻烦?
编译器不仅是一个“翻译官”,它更像是一个极其专业的管家。它帮你解决了以下三大难题:
A. 硬件抽象(不用记寄存器)
在汇编语言中,你必须知道 CPU 有哪些寄存器(EAX, RBX, R13…),哪些是通用的,哪些是特殊的。
编译器的功劳:你只需要定义变量 int a,编译器会自动帮你决定这个变量该放在哪个寄存器里,或者放在内存的哪个角落。它屏蔽了底层硬件的复杂性。
B. 平台适配(写一次,到处运行)
Intel CPU 的加法指令和 ARM CPU(手机)或 RISC-V CPU 的指令完全不同。
编译器的功劳:你写的 a + b 是通用的。如果你想在手机上运行,就用针对 ARM 的编译器重新编译一下;想在电脑上跑,就用针对 x86 的编译器。你不需要为了换台设备而重写逻辑。
C. 自动优化(比你更懂效能)
手动写汇编时,为了节省哪怕 1 纳秒的时间,你需要精心排列指令顺序以避免 CPU 流水线停顿。
编译器的功劳:现代编译器(如 GCC, Clang, MSVC)非常聪明。如果你写 int result = 5 + 10;,编译器在编译阶段就会直接计算出 15,生成的机器码里根本没有加法指令,直接就是给结果赋值 15。这种优化人类很难手动做到极致。
Day02
C++ 的设计初衷
要理解 C++,首先要明白它是一门编译型语言,这意味着你写的代码必须经过转换才能被计算机运行。
C++ 的核心设计思想是面向对象编程(Object-Oriented Programming, OOP)。简单来说,传统的编程关注“动作”(如:计算、打印),而 OOP 关注“对象”(如:一个“学生”或一个“按钮”),通过将数据和操作数据的方法封装在一起,使管理复杂的大型软件变得更加容易。此外,C++ 的强大之处在于它提供了对硬件的直接控制能力(这被称为“底层”特性),同时又具备了高级语言的抽象能力。
另一个关键概念是C++ 标准(Standards):由于 C++ 可以在不同类型的电脑上运行,为了确保你在 Windows 上写的代码在 Mac 上也能跑通,国际标准化组织(ISO)每隔几年就会发布一套官方规则(如 C++11, C++20),规定了代码该怎么写、编译器该怎么解释,这就是“现代 C++”的由来。
开发流程中的核心概念
在实际开发中,我们首先接触到的是源代码(Source Code)。源代码本质上是一个纯文本文件(以 .cpp 结尾),它是人类可读的指令,但 CPU 无法直接理解。为了高效编写这些代码,程序员通常使用 IDE(集成开发环境)。IDE 就像是一个“多功能工具箱”,它集成了文本编辑器、编译器和调试器(Debugger)。这里“调试”是一个核心概念:它是指当程序运行结果不符合预期时,程序员通过逐行检查代码来定位并修复错误的过程。开发流程的本质就是一个循环:你先在源代码中表达逻辑,通过工具将其转化,最后不断测试和修复,直到程序完美运行。
从你写下的文本到最终的双击运行,涉及三个至关重要的技术环节。首先是编译器(Compiler),它的任务是将人类可读的源码翻译成计算机能听懂的机器语言(Machine Language)。机器语言是由 0 和 1 组成的指令集。编译器在工作时会产生一个中间产物——目标文件(Object File)。之所以需要它,是因为大型项目包含很多文件,编译器一次只处理一个 .cpp 文件并生成一个对应的目标文件,它此时并不关心其他文件的内容。
紧接着是库(Library)的概念。库是预先编写并编译好的代码集合,例如处理屏幕显示的指令。你不需要自己去写如何控制显示器像素的代码,只需调用库即可。
最后一步是链接器(Linker)。链接器的作用就像“强力胶”,它负责把所有的目标文件以及需要的库文件整合在一起。如果你的 A 文件调用了 B 文件里的功能,链接器会负责把它们“缝合”成一个最终的可执行文件(Executable File)。如果你写错了语法,那是编译错误;如果你写对了语法但漏掉了某个函数的定义,导致链接器找不到对应的功能,那就是链接错误。