
很多人一看到 const 关键字脑子里就蹦出只读变量这四个字然后觉得这题稳了。结果面试官稍微换个姿势问一下比如const char *p 和 char * const p 有什么区别、C 语言里的 const 变量能不能做数组长度、const 成员函数到底能不能修改成员变量当场就有一批人开始支支吾吾。说实话const 在 C/C 里是面试最高频的关键字之一没有悬念。它出现的场景太多了变量定义、指针、函数参数、函数返回值、成员函数、类型限定符每个角度都能出题。而且它不光是个语法点背后牵扯到编译器的类型检查机制、C 和 C 的语言差异、以及工程上如何用 const 写出更安全的代码。这篇文章就把我在面试中被问过的、以及平时面试别人时喜欢问的 const 相关知识点全部梳理一遍适合准备校招、社招 C/C 岗位的同学收藏也适合工作三五年的工程师查漏补缺。1. 从只读说起const 的基本角色与第一层陷阱1.1 const 变量不是常量是不允许被修改的变量先说个最基本的const int a 10;这行代码之后a 就是只读的。你后面对 a 做任何赋值编译器都会直接报错。这看起来简单但有个非常关键的细节——const 修饰的变量在 C 语言里并不是真正的常量它只是一个不能被修改的变量。区别在哪儿在 C 语言里const int n 10;这个 n 不能用来定义数组长度const int n 10; int arr[n]; // 编译报错数组长度必须是常量表达式这行代码在 C 语言中是错误的但在 C 中却完全合法。原因在于 C 和 C 对 const 的处理机制不同C 编译器在 n 是整数常量表达式的情况下会把 n 当作编译期常量直接替换而 C 语言标准规定const 修饰的对象仍然是变量不具备常量表达式的资格。所以面试如果问下面这段代码在 C 和 C 中分别能不能通过编译你应该能一下说出这个差异。到了实际项目里你有时候确实需要在 C 语言里用符号常量。正确做法是使用宏定义#define ARRAY_SIZE 10 int arr[ARRAY_SIZE];或者使用枚举enum { ARRAY_SIZE 10 }; int arr[ARRAY_SIZE];这两种方式在 C 语言中才是真正的编译期常量。很多从 C 转到 C 开发的工程师会习惯性地写const int len 5;然后再用 len 定义数组结果编译报错找半天才反应过来是 const 在两个语言里待遇不同。1.2 初始化const 变量必须在定义时初始化const 变量的另一个铁律是定义时必须初始化。因为一旦过了定义这个点任何赋值操作都会被编译器拦截你根本没有机会在后面给它赋值const int a; // 错误没有初始化 a 10; // 反正上面也过不了编译哪怕你觉得先定义后面某个时机再初始化更符合逻辑不好意思const 不允许这样做。这是编译器强制的目的是保证 const 对象从诞生那一刻起值就是确定的杜绝某个函数执行完后 const 变量的值才确定这种状态。这个规则在工程上有一个直接后果你在写代码时要提前想清楚哪些变量真的需要被标记为 const。如果变量的值是在运行过程中由函数计算得出的那它就不适合用 const 修饰——除非你把它放到函数体内在局部位置初始化int compute() { return 42; } void func() { const int result compute(); // 可以在局部作用域初始化 // result 后续不能被修改 }这里 const 的意义在于result 在初始化之后就不应该再变。编译器帮你保证这一点防止后续某段代码不小心改掉它。这也是 const 作为接口契约的基础含义。2. 指针与 const三种组合方式考倒一半人2.1 从右往左读const 修饰的是谁指针和 const 的组合是面试重灾区。核心问题就一个const 到底修饰的是指针本身还是指针指向的那个对象C 语言里指针声明可以用两种等价写法比如const int *p和int const *p是完全等价的都表示p 指向一个 const int即不能通过 p 修改它指向的值。而int * const p则表示p 本身是 const即 p 一旦初始化就不能再指向别处但可以通过 p 修改它指向的值。有一个非常实用的读法从右往左读。把声明拆开来看const int *p; // p 是 pointer指向 const int → 指向的内容只读 int const *p; // 等价于上面 int * const p; // p 是 const pointer指向 int → 指针本身只读 const int * const p; // 组合指针本身只读指向的内容也只读为什么面试官爱考这个因为实际工程中函数参数里经常出现const char *、const void *网上一堆教程和 API 文档里全是这种写法。如果你看到const char *p第一反应是p 不能变那后面读任何代码都会踩坑。正确的理解是const char *p这个写法的作用是告诉调用者——我不会通过这个指针去修改字符串的内容。至于指针自己要不要指向别处那是另一回事。面试中经常会有一个实操环节让你指出下面代码哪里有问题char str[] hello; const char *p str; // 正确把非常量对象赋给指向常量的指针是允许的 p[0] H; // 编译错误不能通过 p 修改指向的内容反过来就不行const char *str hello; char *p str; // 错误把指向常量的指针赋值给 char* 会丢掉 const 限定这里涉及类型检查规则去掉 const 是危险的加上 const 是安全的。C/C 的类型系统会阻止你无保护地丢弃 const因为你一旦拿着一个char *去写某块只读内存程序就可能崩溃。这也是 const 最重要的一个作用——编译器帮你防御非法写操作。2.2 字符串字面量与 const为什么有人写 char* 会崩溃说到指针和 const必须提到字符串字面量的问题。看这个经典代码char *p hello; p[0] H; // 未定义行为运行时崩溃或者默默失败在 C 语言标准中字符串字面量是存储在只读区域的对它的任何写入都是未定义行为。但由于历史原因C 语言允许把字符串字面量赋给char *而 C 则直接禁止这种写法必须用const char *p hello;才行。这导致一个很有意思的面试场景面试官拿出一个 C 编译环境让你编译char *p hello;你告诉他会报错换成 C 环境能编译但运行可能崩溃。抓住这个点就能在面试中展示你对标准、实现、运行时三个层面的理解。在实际项目里我建议无论 C 还是 C凡是指向字符串字面量的指针一律写成const char *。这不是为了应付面试而是真的能救命。有一次我接手一个 C 项目里面有人写了char *p config然后在启动流程里把这个字符串当成缓冲区去拼接内容在特定平台上一跑就段错误。排查了很久才发现是字符串字面量位于只读段。2.3 指向常量的指针拿来干嘛printf 和 scanf 的例子回到热词里提到的int scanf(const char *format, ...)。为什么要用const char *format因为这个 format 字符串是输入模式的说明scanf 只需要读取它绝不修改它。通过 const 限定编译器能够保证在 scanf 的实现代码中任何试图写 format 指向内容的行为都会在编译期被拦截。对比一下printf也是一样的道理。标准库在没有 const 的老早版本里就吃过这样的亏某个实现不小心改了调用者传入的格式串导致诡异 bug。后来接口设计规范都强调只读参数用 const 指针声明是接口设计的行业标准。当你自己写函数的时候也要养成这个习惯如果函数不修改传入的缓冲区形参就写const char *buf、const void *data。好处有几点调用方能一眼看出函数不会改动内容增强可读性。编译器在函数体内检查防止你手滑写出赋值语句。让函数既能接受 const 对象也能接受非 const 对象因为加 const是安全的兼容方向。特别是字符串处理函数比如size_t my_strlen(const char *s)你只有写成 const 形参才能传入一个const char *类型的字符串。如果你写成char *s那么调用方手里所有 const 字符串都没法传进来接口可用性大大降低。3. 函数的另一张脸const 修饰形参与返回值的作用3.1 形参 const 化值传递和地址传递要分清面试里经常有这种题void func(const int x)和void func(const int *x)有什么区别先说void func(const int x)。x 是按值传递的函数内部怎么折腾 x 都是形参的副本本来就不会影响调用者的变量。那 const 还有意义吗意义不大但能让实现者写代码时少犯错。比如void func(const int x) { x 20; // 错误不能修改 const 形参 }有些编码规范要求即使在按值传递的场景下形参也尽量都加 const理由是防止函数中途把形参当作临时变量乱改导致逻辑混乱。但面试时你要能说清楚按值传递时 const 不影响调用方它约束的是函数体内部的误操作。真正重要的是指针形参和引用形参C。void func(const int *p)表示函数保证不修改 p 指向的内容而void func(int *p)则没有这个承诺。如果你在函数内部写void func(const int *p) { *p 10; // 编译错误 }编译器当场拦截。这就是 const 在接口层面的价值——它是一个文档化的、由编译器强制执行的承诺。引用形参在 C 里同样常见void func(const std::string s)。它一方面通过引用避免拷贝的开销另一方面用 const 声明我只是想读不会改你的字符串。如果不加 const调用方需要担心传入的对象会被修改如果直接用值传递又可能产生不必要的深拷贝。const 是 C 里性能和安全的最佳折中这也是面试官爱考 const 引用和普通引用区别的原因。3.2 返回值用 const 修饰使用场景很有限但逻辑要清楚返回值加 const 最常见的场景是返回指针尤其是返回指向内部缓冲区的指针const char* get_name() { return name_; }这个写法的语义是调用者拿到的指针只能读取不能修改。如果调用者尝试写编译器报错。很多 C 接口里返回配置信息、版本号、数据库连接字符串等都会用这种模式。这是 const 作为封装手段的重要用法——暴露只读视图保护内部数据。但是请小心返回指向局部变量的指针本身是错的。比如const char* bad() { char buf[100]; strcpy(buf, hello); return buf; // 返回指向栈上局部数组的指针生命周期结束 }这跟 const 没关系纯粹是悬挂指针问题。面试中如果出现这种代码你要先指出生命周期错误再说 const 的修饰杯水车薪——它让内容不能改但没解决对象已经不存在的问题。对于 C 里的内置类型返回值比如const int func()这种情况我很少见到实战价值因为返回的是临时值调用方拿到的就是一个拷贝值加不加 const 几乎没有影响。面试偶尔会考你有没有意识到这点你可以答对于值类型返回值const 一般没什么意义。4. C 和 C 的 const从只读变量到编译期常量4.1 C 中的 const内存中的只读对象在 C 语言标准里const int a 10;会在内存中占据空间它本质上是一个有 const 限定符的对象。你不能修改它但它的地址可以被取出比如const int a 10; const int *p a; // 合法如果你用int *p (int *)a;强行去掉 const然后*p 20;这是未定义行为。在某些嵌入式平台上a 可能被放在只读区写它直接触发异常在某些平台上它可能还在可写区你改完才发现 a 的值居然变了。这种跨平台的不一致性恰恰说明在 C 语言里const 更多是程序员的自我约束 编译器的静态检查而不是底层的只读内存保护。C 语言的 const 还有一个特性就是它并不要求编译器把它放进只读区。到底放哪里取决于链接脚本、编译器实现、优化级别。所以面试时不要说什么C 语言的 const 变量存在只读数据段——这是真不一定。标准只约束行为不约束存储位置。4.2 C 中的 const可以成为真正的编译期常量C 对 const 的使用要激进得多。const int n 10;如果在编译期就能确定值它就是一个常量表达式可以用来定义数组、做模板参数、做 case 标号等const int n 10; int arr[n]; // 合法 constexpr int m n; // 合法从 C11 开始官方更推荐用constexpr来表达编译期常量这种需求而 const 则更多地表达运行期只读的语义。但面试中大量老代码和旧教材会继续用 const 来表达编译期常量所以你得知道这两个概念的界面在哪里表达意图C98/03 老办法C11 现代办法编译期常量const int且用常量表达式初始化constexpr int运行期只读对象const T objconst T objconst 成员函数void f() constvoid f() const编译期计算函数无法优雅表达constexpr函数4.3 顶层 const 与底层 constC 里必背的术语聊到 C 面试题有两个术语绕不开顶层 consttop-level const和底层 constlow-level const。顶层 const指针或对象本身是 const。比如int * const p是顶层 const 修饰指针const int a是顶层 const 修饰对象。底层 const指针指向的对象是 const。比如const int *p中的 const 是底层 const。在 C 的拷贝赋值、函数重载、模板推导中这两个概念会直接影响类型匹配。一个经典例子int x 10; int *p x; const int *cp p; // 合法添加底层 const安全 int *pc cp; // 非法丢掉底层 const禁止如果面试官让你解释底层 const 在函数重载里的作用其实是要你理解引用折叠和类型推导的边界。对于传值参数顶层 const 会被忽略对于指针参数底层 const 会被保留。所以void f(const int)和void f(int)不能形成重载但void f(const int *)和void f(int *)可以。这个知识点完整讲清楚需要整篇文章但在 const 的面试题里你至少要能脱口而出这两句话顶层 const 表示指针本身不可改底层 const 表示指向对象不可改。拷贝时不能丢掉底层 const。 做到这一点面试官就会觉得你对 const 有体系化的理解。5. C 的 const 成员函数与 mutable类设计中的关键5.1 const 成员函数对不修改对象的承诺C 类里边成员函数末尾加 const 表示这个函数不会修改对象的任何非静态成员变量class Person { public: std::string get_name() const { return name_; // 可以只读取 } void set_name(const std::string n) { name_ n; // 可以非 const 成员函数可以修改 } private: std::string name_; };如果你在一个 const 成员函数里写name_ n;编译器会直接报错。这个机制让程序员一眼就能看出哪些操作是只读的哪些操作会改变对象状态。对调用方来说只要拿到了一个 const 对象或者 const 引用就只能在 const 成员函数之间挑方法调用非 const 成员函数完全不可见。一个常被忽略的细节是const 成员函数中成员变量的类型全部被加上了 const。你可以理解为编译器在函数内部把this从Person *变成了const Person *。因此成员变量通过this访问时都带有 const 属性所以你不能把它们传给非 const 的形参。5.2 mutable打破 const 的特殊通道那么问题来了如果一个成员变量只是被 const 成员函数改一下行不行比如缓存、互斥锁、计数器这些它们本质上跟对象的逻辑状态无关但物理上确实会被修改。C 提供了mutable关键字来解决class Cache { public: int get(int key) const { if (cache_.count(key)) { hits_; // 修改 mutable 计数器 return cache_[key]; } return -1; } private: std::mapint, int cache_; mutable int hits_ 0; // mutableconst 成员函数可以修改它 };mutable的出现是为了在 const 语义和性能优化之间找到平衡。面试时如果问const 成员函数里能不能修改成员变量标准回答就是不能直接修改非 mutable 成员变量但可以修改 mutable 成员变量。再说出mutable 通常用在缓存、统计计数、锁等场景这题就答透了。5.3 与 static 的结合静态成员函数的特殊性const 成员函数和 static 成员函数不要混为一谈。static 成员函数不依赖具体对象没有 this 指针所以它不能访问非静态成员变量也根本不可能被声明为 const——因为没有对象可以给它承诺不改。这两者的区别也常常作为概念题出现。在 C11 之后的代码里如果你看到一个形如static int get_version() const的声明可以立刻判断它是非法的。但如果面试官只是把 static 和 const 放在一起讨论你还能补充一点static 成员变量本身可以用 const 修饰比如类内的static const int MaxSize 100;在 C17 中更是升级为inline static const int MaxSize 100;可以直接在类内定义而不需要类外单独定义。6. 面试实战高频题、易错代码与答题节奏6.1 以下几个代码片段你能在一分钟内看出全部问题吗我整理了几个我在实际面试中常拿出来问的 const 题目你可以先自己心里过一遍再往下看解析。片段一const char *p hello; p[0] A; printf(%c\n, p[0]);问题p[0] A编译失败因为const char *p表示指向的内容只读。就算你强转运行时也是未定义行为因为字符串字面量在只读区。片段二char * const p hello; p world;问题char * const p表示指针本身只读不能重新指向别处。但p指向的字符串内容却可以写这里没写编译报错发生在p world。片段三const int a 10; int *p (int *)a; *p 20; printf(%d\n, a);问题(int *)a强制去掉 const然后写入是未定义行为。打印 a 的值可能是 10 也可能是 20取决于编译优化和存储位置。你千万不要在面试里说肯定会打印 10——这样的回答暴露了对未定义行为的理解缺失。片段四Cclass A { void f() const; int a; }; void A::f() const { a 10; // 错误const 成员函数不能修改非 mutable 成员 }这题简直送分但很多同学会被const 成员函数里其实是通过 const this 访问成员这个理论绕晕回答时支支吾吾。建议直接说结论再简单提一句底层机制即可。6.2 答题节奏先说结论再补原理再举例子面试中凡是遇到 const 的代码分析题我的建议是遵循三步答题法第一步直给结果。比如这段代码编译报错原因是 const char * 让指针指向的内容只读。第二步说明机制。解释 const 限定符如何参与类型检查哪些操作违反语法。第三步补充工程价值。比如真实项目中我们通常用 const char * 传递只读字符串这样既安全又让接口意图清晰。这样回答的好处是节奏清楚。面试官如果只想要结论你第一步已经给出了如果他想深挖第二步和第三步就是展示你能力的地方。6.3 高频追问const 怎么和 volatile 同时用有不少面试官会在 const 之后紧跟着问一个 volatile。const volatile 组合看起来很矛盾——一个说不准改一个说随时会变。其实这在嵌入式开发里很常见一个硬件寄存器映射的内存地址程序不能修改它的值但它会被硬件异步更新。于是const volatile uint32_t *status_reg (const volatile uint32_t *)0x40000000;含义是这个指针指向的内容软件不能写const但每次读取都要从内存里重新取不能被编译器优化掉volatile。这种组合恰恰体现了 const 和 volatile 是两个维度的限定符互不排斥。掌握这一点在嵌入式相关岗位的面试中会很加分。7. 实战中的 const 应用代码规范与常见翻车场景7.1 接口设计里推荐用 const 的场景我把实际项目中常见的 const 用法整理一下你可以直接拿去当代码规范参考场景推荐写法理由函数只读字符串void print(const char *s)明确不改内容可传 const 参数函数只读外部缓冲区void parse(const char *data, size_t len)防止解析过程意外修改数据函数返回内部只读数据const char* get_name() const保护内部成员暴露只读视图类中只读查询方法size_t size() constconst 对象也能调用全局常量配置const int kMaxLen 100;替代宏定义参与类型检查C 接口参数避免拷贝void set_address(const std::string addr)高性能 只读承诺值得注意的是C 语言里很多项目习惯用大写加下划线的宏定义来定义常量比如#define MAX_LEN 100。宏的问题是没有类型检查也不遵循作用域。用 const 替代宏能获得编译器的类型检查能力这是现代 C/C 项目推荐 const 的核心原因之一。7.2 最容易踩的坑滥用 const_cast 和 C 风格强转讲到实战翻车最常出现的就是有人为了让代码通过编译直接上 const_cast 或者 C 风格强转去掉 const。比如const char *p hello; char *q const_castchar *(p); q[0] H; // 如果 p 指向真正的只读区域这里就是运行时崩溃const_cast 的正确使用场景是当一个接口设计不佳、形参是非 const 指针但实际不会修改内容时你在明确安全的前提下做一次窄桥转换。比如你调用一个第三方库的旧接口void legacy_compute(char *buf);而你手里只有一个const char *且确认 legacy_compute 不会写它。这时候你用 const_cast 是可以解释通的。但如果你为了去写一块只读内存而去除 const那基本就是在自爆。在 C 代码审查中我只要看到 const_cast第一反应就是要求作者写清楚为什么用它、安全机制是什么。如果没有合理注释直接打回重写。这个习惯在团队协作里非常重要它逼着每个人对类型系统负责而不是绕过类型系统。7.3 一个典型的真实场景解析配置文件的函数签名设计假设我要写一个解析 JSON 配置文件的模块核心函数签名可以这样设计bool parse_config(const char *json_text, Config *out);第一个参数用const char *因为解析器只需要读取 JSON 内容不需要修改它。第二个参数没有 const因为它是输出对象。这个组合天然地表达了输入只读 输出可写的语义任何人看到签名都能立刻理解数据流方向。反过来如果把第一个参数写成char *json_text就会给调用者一个印象这个函数可能会改动你的文本内容。调用者如果传入的正是只读字符串还得强转降低了安全性。所以真的要在接口设计上养成const 修饰只读输入的习惯这不光是为了面试是实打实的工程素养。8. const 的底层与跨语言对比一次彻底拉开差距的机会8.1 编译器眼中的 const类型系统的一部分而不是优化指令C/C 编译器对待 const 大体分为两个阶段静态检查和代码生成。静态检查阶段const 参与类型比较任何非法写入都报错。代码生成阶段编译器可以利用 const 做优化比如把const int a 10;直接内嵌到使用位置而不分配内存也可以在无法内嵌时分配到只读段。关键点是不要让 const 变成你唯一的防写手段。在 C 语言里如果一段内存是真地不能写你要么依赖操作系统和硬件的内存保护要么依赖开发者的自律。const 只是一个编译期约束它不会在运行时拦截你的强转写入。理解这一点你就能解释为什么(int *)a之后写 a 可能崩、可能不崩——行为完全由存储分布决定。8.2 为什么 C 语言不把 const 变量直接当作常量表达式这背后有标准演进的历史原因也有实现层面的原因。C90 时代 const 就已经存在了但标准委员会刻意没有把 const 对象纳入整数常量表达式的范畴因为对于文件作用域的 const 变量编译器无法保证它在编译期一定有确定的值——它可能来自外部链接、可能被链接器在运行时填充等等。强行把 const 当作常量表达式会让语言分析和链接模型变得复杂。C 之所以敢把有常量初始化器的 const int 当作常量表达式是因为 C 从一开始就更强调编译期计算和更强的类型推导。加上 constexpr 关键字之后这个能力被进一步正式化。面试答到这一层面试官会认定你是真的理解语言标准而不是背了几个代码片段。8.3 与其他语言对比Java 的 final、Python 的约定有些面试官喝口水继续问Java 的 final 跟 C 的 const 一样吗这种跨语言对比题也很常见。坦率地讲Java 的 final 在只能赋值一次这个语义上更接近 C 的 const但 Java 没有 const 指针或 const 成员函数的概念。Python 则根本没有内建的 const 机制通常靠命名约定——全大写变量名表示不要改它。这类对比不需要你每个语言都精通你只需要抓住核心差异C/C 的 const 在编译期参与类型检查能实实在在地拦截错误而 Java 的 final 更多是运行期语义和不可变引用的表达Python 则完全靠自律。面试场合能把这一层讲清楚比背出十道题还加分。9. 在真正写代码时别让 const 变成负担很多教程把 const 讲成越多越好我不完全同意。const 应该是有策略地使用而不是全覆盖。我在实际项目中见过的一种情况是一个小函数参数和局部变量全加了 const代码看起来是很规范但可读性反而下降了。因为每个 const 都在向读者发出信号这里不能改但读者其实根本没打算改它信号太多等于没有信号。我的实际经验是在接口边界和关键数据结构上坚持使用 const在函数内部的局部变量上按需使用。尤其是这些位置一定要有 const函数参数特别是 C 的引用和指针。类成员函数中不修改对象状态的查询函数。全局配置变量、只读表。传递字符串、传入缓冲区的时候。反之函数内部的中间计算变量值很快就变你加 const 反而让代码笨重。比如int tmp a b; tmp tmp * 2;这种变量完全没必要 const。关于 const 还有一个容易被误解的地方const 只保证通过这个路径不能改不保证对象真的不会被其他路径改掉。比如你有一个const int *p指向某个 int 变量 xx 本身是可变变量另一个非 const 指针完全可以修改它。const 只约束你当前手里的这个访问路径不是给被指向对象上锁。理解这个边界你在阅读复杂代码时才不会犯认为 const 指向的对象全局不可变的严重错误。10. 收尾前再补充几个面试细节面试时关于 const 的问答有几个非常容易被追问的细节点我合在一起最后过一遍const int a;没有初始化直接报错。int arr[const_expr]中 const_expr 是 const int 变量C 语言里报错C 里可能合法。const struct对象可以调用其成员的 const 成员函数C或通过 const 指针访问成员但不能修改成员。const char *用于 printf/scanf 的 format 参数表示该函数不修改格式串。函数重载中const T和T在按值传递时冲突因为顶层 const 被忽略。拷贝构造和赋值操作中形参通常是const T这是为了接收 const 对象和临时对象同时避免拷贝。mutable 只能用于非静态、非引用、非 const 的数据成员不能用于函数。constexpr 和 const 不是一回事constexpr 强调编译期求值const 只是只读。把这些细节记牢后再配合上面讲的指针读法和 C/C 差异const 相关的面试题基本就全覆盖了。我曾经在一次校招面试中让一位同学从const char *开始顺着把指针读法、字符串字面量、函数参数、C/C 常量差异、顶层底层 const、const 成员函数全部串起来讲了一遍。他答完最后一个问题时我直接在评分表上给这个环节打了高分。因为这种一条知识线串到底的能力在真实项目里非常有用——它说明一个人不是背 api 的而是真正理解了语言设计者的意图。最后分享一个实用的小技巧你在身边放一张纸随便写几个 const 指针组合从右往左读声明坚持练几天所有的 const 指针题目都会变成条件反射。这个习惯我到现在写代码时都在用尤其在分析那些复杂库的接口声明时能省下不少查文档的时间。