C++ 注释

c++基础 › C++ 注释 (第 5 章)
文章工具

注释是写给人和工具看的说明,编译器在预处理阶段就会把它整段丢掉——理解「什么时候丢、丢成什么」,你才能踩准它的每一条边界。

学完你能做什么

  • 能写出单行 // 和多行 /* */ 注释,并准确说出它们各自在哪一行结束。
  • 能解释「注释被替换成一个空格」这条规则,并预测 int/**/x = 1; 能否通过编译。
  • 能说清为什么 /* */ 不能嵌套,而两种注释互相包含却没问题。
  • 能识别「行末反斜杠吞掉下一行」这个经典陷阱,并知道用 #if 0 屏蔽大段代码。

先建立直觉:注释是便利贴,不是代码

想象你在给同事留一张便利贴,贴在电路板上。贴上写着「这里电压是 5V」。焊接机器人只看铜箔走线,便利贴上的字它一个字都不读,也不影响电路工作。

注释就是这张便利贴:写给人看,机器忽略。所以判断注释好坏的标准只有一个——读者看完是否更省力,而不是「写得多」。

从最简单的用法开始

单行注释以 // 开始,一直到本行结尾。它后面的内容全是说明文字。

C++ 示例

▶ 在线运行:改一改再点“运行”试试(本站沙箱执行,登录后可运行)。

预期输出:

C++ 片段需补全 main() 函数才能运行在实训台打开 →
36

多行注释以 /* 开始,以 */ 结束,中间可以换行:

C++ 片段需补全 main() 函数才能运行在实训台打开 →
/* 计算总价:
   单价乘以数量,
   这段文字不会参与运算 */
再往下看:编译器什么时候把注释丢掉(示意图)
图:再往下看:编译器什么时候把注释丢掉
顺序这一步做什么与注释的关系
① 续行合并先把「行末反斜杠 + 换行」拼成同一行发生在去注释之前,所以 // 说明 \ 会把下一行并进注释
② 去注释把每段注释替换成一个空格int/**/x = 1; 变成 int x = 1;;注释里的 "、/* 都只是普通文字
③ 词法 / 语法分析切分成 token 并解析语法,随后才展开宏这时已无注释,且宏展开晚于第 ② 步,#define SLASH / 拼不出注释

口诀:先接续行,再变空格,最后才分词。

再往下看:编译器什么时候把注释丢掉

C++ 标准把「从源码到程序」拆成若干翻译阶段,和注释相关的顺序是这样的:

  1. 先把「行末反斜杠 + 换行」拼成同一行(续行合并)。
  2. 再把每段注释替换成一个空格。
  3. 之后才是真正的词法与语法分析。

这个顺序解释了三件事:

  • 注释是「一个空格」,不是「消失」:int/**/x = 1; 预处理后变成 int x = 1;,合法。如果注释真的什么都不剩,就会粘成 intx,反而报错。
  • 行末反斜杠是陷阱:续行合并在注释处理之前发生。写 // 说明 \,反斜杠会把下一行并进本行,于是下一行代码也被注释吃掉。
  • 注释里的引号无害:注释在词法分析前就被替换掉了,里面的 " 只是普通字符,不会「打开一个字符串」。

还要分清两层:注释在哪个阶段被移除、替换成空格,是语言规则;编译器是否对可疑注释发警告、文档工具是否把 /// 当文档,属于实现细节,换编译器就可能不同。

对比:两种注释,一句口诀

特性// 行注释/* */ 块注释
结束位置本行行末遇到第一个 */
能否跨行不能能
能否嵌套同类不能(本身就是到行末)不能(后者会提前结束前者)
能否包含另一种的符号能,/* 只是文字能,// 只是文字
口诀:单行到行尾,块注释找第一个「星斜杠」;两种可以互装,同类绝不嵌套。

实战:临时屏蔽一段代码

C++ 示例

预期输出:

C++ 片段需补全 main() 函数才能运行在实训台打开 →
done
易错点为什么会错正确做法
以为 /* */ 能嵌套块注释遇到第一个 */ 就结束,不认识「层次」,后面代码变真代码用 #if 0 … #endif 屏蔽代码,可嵌套
行末反斜杠吞掉下一行续行合并在去注释之前,b = 2; 被并入注释行别让以反斜杠结尾的注释后面还跟着严肃代码
以为注释「消失」注释其实替换成一个空格,若真消失会粘成 intx记住是「一个空格」,空格隔开两个 token 才合法
拿注释当版本库大段废弃代码让读者分不清有效逻辑注释写为什么,历史交给版本控制
想用宏拼出注释去注释在第 ② 步,宏展开更晚,// 拼出来是除号注释不能由宏生成,只能直接写

口诀:同类不嵌套,末尾不留杠;注释是空格,历史归版本。

易错点(先错后对)

错误 1:以为 /* */ 能嵌套

C++ 片段需补全 main() 函数才能运行在实训台打开 →
/* 外层开始
   /* 想再嵌一层 */ <- 注释在这里就结束了
   这一行已经是真正的代码了
*/

原因:块注释遇到第一个 */ 就收尾,它不认识「层次」。第三行变成了真代码,编译报错。正确做法是屏蔽代码用预处理条件:

C++ 片段需补全 main() 函数才能运行在实训台打开 →
#if 0
// 这里可以放心地再嵌 /* */ 和 #if
#endif

错误 2:行末反斜杠吞掉下一行

C++ 片段需补全 main() 函数才能运行在实训台打开 →
int b = 1;   // 设置初值 \
b = 2;

续行合并让第二行并入第一行,b = 2; 也进了注释,b 最终仍是 1。正确做法:别让以反斜杠结尾的注释后面还跟着严肃代码。

错误 3:拿注释当版本库

把废弃代码大段注释留在文件里,会让读者分不清「哪些是有效逻辑」。注释解释为什么,历史交给版本控制。

再深入一点:工具为什么爱注释

注释在被忽略之前,还是一段可读文本,于是工具链会去「捡」它:Doxygen 等文档工具读取 /// 或 /** */ 生成 API 文档;静态分析工具读取 // NOLINT 之类的指令来抑制告警。这些约定不是语言规则,离开对应工具就失效。

另外,注释不能由宏「拼」出来:注释在第 2 阶段就被替换掉了,宏展开发生在更晚的阶段。所以写 #define SLASH / 再拼出 //,得到的是除号,而不是注释。

自测练习

  1. 下面两句都能通过编译吗?说明理由。
    int/**/a = 1; 与 int a/**/= 1;
  2. 执行后 b 的值是多少?
    C++ 片段需补全 main() 函数才能运行在实训台打开 →
    int b = 1; // 设置初值 \
    b = 2;
  3. 你要屏蔽 200 行代码,其中本身含有多处 /* ... */ 注释。直接用一对 /* */ 包起来会出什么问题?正确做法是什么?
参考答案

1. 都能。注释被替换成一个空格:第一句展开为 int a = 1;,第二句展开为 int a = 1;,都是合法声明。

2. b 等于 1。行末反斜杠触发续行合并,b = 2; 被并入上一行、整句落入注释,因此赋值从未执行。

3. 外层块注释会在内部第一个 */ 处提前结束,剩下的代码变成真代码,编译器就会报一堆错。正确做法是用 #if 0 … #endif,它可以嵌套且不受反斜杠影响。

一句话记住

注释在预处理阶段被替换成一个空格:它对程序零影响,但对读代码的人可能是全部价值。

延伸阅读

⚠️ 版权声明

本文为免费文章,欢迎转载。转载请注明出处:一木科技(https://www.pingquan.vip/2026/09/24/cpp-comments/),并保留原文链接。