C++ 是什么
这一课你将能做到
- 说清楚 C++ 的编译执行模型,以及它和 Java「编译成字节码 + JVM 运行」的本质区别
- 在本机确认 g++(或 MSVC/clang)可用,手动编译并运行第一个 C++ 程序
- 读懂一个最小 C++ 程序的每一行:
#include、main、std::cout各自在做什么
为什么要学
本站的在线判题功能,判题沙箱本身就是 C++ 写的:拿到用户提交的代码后,要毫秒级完成「编译 → 起进程 → 跑测试点 → 回收资源」,每一步都压着性能红线,这正是 C++ 的主场。游戏引擎、高频交易、嵌入式、数据库内核,用的也是同一套逻辑。
如果你已经会 Java:C++ 没有 JVM,也没有垃圾回收。Java 源码编译成 .class 字节码交给 JVM 解释/JIT 执行;C++ 源码直接编译成目标平台的机器码,操作系统直接加载运行。这带来两个后果:一是 C++ 程序跑起来更快、内存更省;二是跨平台要「每个平台各编译一次」,不像 Java 一次编译到处运行。
本轨对齐 C++17/20 标准,不背标准条文,只练能写进真实项目的默认习惯。
先建立图像
Java 的路径:
.java → javac → .class 字节码 → JVM(解释 + JIT)→ CPU
↑ 运行时还在干活
C++ 的路径:
.cpp / .hpp → 预处理 → 编译 → 汇编 → 链接 → 可执行文件 → CPU
↑ 这四步在运行前全部做完,运行时没有任何「中间商」
判题沙箱评测用户代码时,本质上就是替用户走一遍右边这条流水线,再掐表计时——所以理解编译模型,是理解「C++ 为什么快」的第一步。
跟着做
步骤 1:确认编译器
Windows 推荐 MinGW-w64(或 VS + C++ 工作负载),macOS 用 xcode-select --install,Linux 用 sudo apt install g++。打开终端验证:
g++ --version
能打印出版本号(如 g++ (MinGW-w64) 13.2.0)就说明环境通了。判题沙箱内部同样会先探测编译器版本再决定编译参数。
步骤 2:写第一个程序
新建文件夹 cpp-lab,在里面新建 hello.cpp:
#include <iostream>
int main() {
std::cout << "Hello, C++!" << std::endl;
return 0;
}
步骤 3:编译并运行
在终端里(进入 cpp-lab 目录):
g++ -std=c++17 hello.cpp -o hello
./hello # Windows 下是 .\hello.exe 或 hello
输出:
Hello, C++!
这条命令做了四件事:预处理(展开 #include <iostream> 的全部内容)→ 编译成汇编 → 汇编成目标文件 → 链接标准库,最终生成一个可以直接运行的可执行文件。-std=c++17 指定按 C++17 标准编译,本轨全程带上它。
步骤 4:逐行理解
#include <iostream> // 预处理指令:把输入输出库的头文件「复制粘贴」进来
// C++ 的 include 是文本替换,不是 Java 的 import
int main() { // 程序入口。操作系统加载可执行文件后从 main 开始执行
std::cout << "Hello, C++!" << std::endl;
// std::cout 是标准输出流对象,<< 是流插入运算符,可以连续拼接
// std:: 是命名空间,表示这个名字属于标准库——相当于 Java 的包名
// std::endl 输出换行并刷新缓冲区;"\n" 只换行不刷新,更快
return 0; // 返回 0 表示程序正常结束,非 0 是错误码
}
对比 Java:System.out.println("Hello") 和 std::cout << ... 干的是同一件事,只是 C++ 用运算符重载实现了流式输出。
步骤 5:体会「编译型」的含义
故意写一个错误:
int main() {
int x = "hello"; // 类型不匹配
}
编译:
g++ -std=c++17 hello.cpp -o hello
你会得到类似 error: invalid conversion from 'const char*' to 'int' 的编译错误,可执行文件根本不会生成。Java 也有编译检查,但 C++ 的检查发生在机器码生成之前,报错信息更底层、也更「凶」。读编译错误是 C++ 初学者的必修课,本轨每一课都会带你读。
步骤 6:看一眼机器码长什么样(可选)
g++ -std=c++17 -S hello.cpp # 生成 hello.s 汇编文件
打开 hello.s 能看到一堆 mov、call 指令——这才是 CPU 真正执行的东西。Java 字节码是给 JVM 看的,C++ 汇编是给 CPU 看的,差别的根源就在这里。
讲清楚要点
- 编译期 vs 运行期:C++ 把大量工作(类型检查、模板实例化、内联优化)压到编译期,换来运行期零开销。这是 C++ 性能优势的总根源。
#include是文本复制:它不是「引入类路径」,而是真的把头文件内容原样粘进当前文件。所以头文件里放声明、源文件里放实现,能避免重复编译。main是唯一入口:每个可执行程序必须有且只有一个main。多文件工程里,其他文件只提供函数和类,不写main。- 标准库都在
std命名空间里:std::cout、std::string、std::vector。初学阶段建议写全std::前缀,不推荐using namespace std;——它会污染命名空间,判题沙箱这类大项目更是明令禁止。 - C++ 是 C 的超集(大体上):C 的语法基本都能在 C++ 里编译通过,所以你会同时见到 C 风格(
printf、裸指针)和 C++ 风格(iostream、引用)两种写法。本轨只教现代 C++ 风格。
你可能会懵的地方
-
C、C++、Java 到底什么关系? C 诞生于 1972 年,贴近硬件;C++ 在 C 上加了类、模板、标准库(1983 年命名);Java 借鉴 C++ 语法但砍掉指针运算、多继承,换上 JVM 和 GC。语法上三者相似,运行模型上 C/C++ 编译成机器码,Java 编译成字节码。
-
为什么 .cpp 和 .hpp 要分开放? .hpp(或 .h)是头文件,放「声明」:函数签名、类定义,给别人
#include用;.cpp 放「实现」。这样改实现只重编译一个 .cpp,改声明才触发大规模重编译。Java 没有这个区分,一个 .java 文件全包了。 -
endl和"\n"有什么区别? 都换行,但endl额外刷新输出缓冲区(立即显示),频繁使用会拖慢输出。判题沙箱要输出大量测试结果时都用\n。默认用"\n",需要实时看输出时用endl。 -
C++17、C++20 是什么? ISO 标准的年份版本,每三年一发。C++11 是现代 C++ 的分水岭(智能指针、lambda、auto 都是它引入的),C++17 加了结构化绑定、
std::optional,C++20 加了概念、范围库。本轨以 C++17 为主,穿插 C++20 实用特性。 -
为什么 C++ 编译比 Java 慢这么多? 编译期要做的事多得多:模板实例化、头文件层层展开、跨文件优化。Java 把这些推迟到类加载和 JIT 阶段。所以判题沙箱给每道题设了编译时限,超时直接判编译失败。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
#include <iostream.h> | 老 C++ 写法,现代编译器没有这个头文件 | #include <iostream> |
| 直接双击 .cpp 文件想运行 | .cpp 是源码,不是可执行文件 | 先 g++ xxx.cpp -o xxx 编译再运行 |
| 文件路径含中文或空格就报错 | 部分编译器/终端对路径处理不稳 | 工程路径全英文、无空格 |
每个文件都写 using namespace std; | 命名冲突风险,工程禁忌 | 显式写 std:: 前缀 |
void main() | 非标准写法,部分编译器拒绝 | 恒用 int main() |
卡点急救
g++不是内部或外部命令:编译器没装或没加 PATH。Windows 下把 MinGW 的bin目录加入环境变量 PATH 后重开终端。undefined reference to 'main':链接阶段没找到入口。检查是否忘了写main函数,或编译命令里漏掉了含main的那个 .cpp 文件。- Windows 控制台中文乱码:源文件保存为 UTF-8,并在编译时加
-fexec-charset=GBK(MinGW),或先执行chcp 65001切换控制台编码。
面试常问
-
C++ 编译执行和 Java 解释执行的区别? C++ 直接编译为目标平台机器码,运行时无虚拟机开销,启动快、性能高,但需按平台分别编译;Java 编译为字节码由 JVM 加载,通过解释器 + JIT 达到接近原生的性能,换来跨平台与运行时管理(GC、反射)。二者是「编译期优化」与「运行期优化」的取舍。
-
从源码到可执行文件经历哪几个阶段? 预处理(宏展开、头文件包含)→ 编译(生成汇编)→ 汇编(生成目标文件 .o)→ 链接(合并目标文件与库,解析符号引用)。常考链接阶段报错
undefined reference与编译阶段报错no member named的区别。 -
为什么在线判题系统常用 C++ 写沙箱? 判题要精确计量用户程序的 CPU 时间和内存,沙箱本身必须零干扰:C++ 编译成原生代码无 GC 停顿、无 JVM 开销,且能用系统调用直接做进程隔离、资源限额(如
setrlimit),延迟和可控性都最优。
自测三问
- 从 .cpp 到可执行文件要经过哪四个阶段?
#include和 Java 的import有什么本质区别?- 为什么不推荐
using namespace std;?
小结一句话
C++ 把一切压到编译期:源码直接变成机器码,没有 JVM 中间商,换来极致性能和手动资源管理——理解编译模型,就理解了 C++ 与 Java 的分野。
延伸
- cppreference:C++ 编译过程
- cplusplus.com:C++ 教程首页
- 下一课:基础语法——变量、控制流与函数。