[{"data":1,"prerenderedAt":776},["ShallowReactive",2],{"page-\u002Fcpp\u002Ffeatures\u002Fcpp的符号表":3},{"id":4,"title":5,"body":6,"description":12,"extension":770,"meta":771,"navigation":169,"path":772,"seo":773,"stem":774,"__hash__":775},"content\u002Fcpp\u002Ffeatures\u002FCPP的符号表.md","CPP的符号表",{"type":7,"value":8,"toc":758},"minimark",[9,13,21,33,37,70,80,83,87,101,111,114,117,120,123,238,241,244,354,357,360,363,423,426,495,498,536,539,542,545,548,551,559,581,584,588,591,594,690,706,709,728,754],[10,11,12],"p",{},"对于从 C# .NET 转到 C++ 的开发者来说，理解符号表是理解“静态链接语言”与“托管语言”差异的关键。",[10,14,15,16,20],{},"它主要参与编译器和链接器的工作，他记录了函数名、变量名、类名的“名称”与它们在",[17,18,19],"strong",{},"二进制文件","中的“偏移量（地址）”之间的映射。",[10,22,23,24,28,29,32],{},"以及参与名称修饰的工作，因为cpp也支持重载和命名空间，符号表里的名字会被处理成一长串奇怪的字符。比如 ",[25,26,27],"code",{},"std::is_same\u003Cint, int>::value"," 在符号表里可能叫 ",[25,30,31],{},"?is_same@std@@...","。",[34,35,36],"h2",{"id":36},"生命周期",[38,39,40,48,59],"ul",{},[41,42,43,44,47],"li",{},"在编译生成 ",[25,45,46],{},".obj"," 文件时产生。",[41,49,50,51,54,55,58],{},"在链接生成 ",[25,52,53],{},".exe"," 或 ",[25,56,57],{},".dll"," 时被消耗。",[41,60,61,62,65,66,69],{},"在生成的 Release 版二进制文件中，符号通常会被",[17,63,64],{},"剥离（Strip）","，除非你保留了 ",[25,67,68],{},".pdb"," 文件。",[10,71,72,73,76,77],{},"因此，他只会存在于编译和链接期，程序真正运行的时候就不会参与逻辑了，这个也是和.NET里面的方法表很大的不同，而且符号文件完全不参与运行时的内存映射工作，在 C++ 中，内存布局在",[17,74,75],{},"编译那一刻","就已经被彻底“算死”并硬编码进机器码了。机器码里只有“地址 + 偏移量”，根本没有“age”这个名字。",[17,78,79],{},"CPU 执行时完全不需要知道这个字段叫什么，也不需要任何映射表。",[34,81,82],{"id":82},"玩法",[84,85,86],"h3",{"id":86},"依靠pdb符号文件进行反射",[10,88,89,90,92,93,96,97,100],{},"既然 ",[25,91,68],{}," 包含了“名字”到“地址偏移”的映射表，那么只要我们能在",[17,94,95],{},"程序运行期间读取这个文件","，就能像 C# 的 ",[25,98,99],{},"System.Reflection"," 一样，通过字符串名字找到变量。",[10,102,103,104,107,108,110],{},"在 Windows 上，微软提供了一套工具包叫 **DIA SDK 或者更底层的 ",[17,105,106],{},"DbgHelp API","，当你用 Visual Studio 调试程序，输入变量名就能看到值，就是因为 VS 调用了 DIA SDK，在 ",[25,109,68],{}," 里查到了该变量的偏移量。",[10,112,113],{},"但是实际工程中，cpp开发者不会使用这种方案，因为PDB文件体积非常大，代码更新PDB就会同步更新，否则签名就不匹配，这套逻辑只能在 Windows + MSVC 环境下跑。",[10,115,116],{},"好消息！现代cpp还有另一条路可以走，C++ 26提供了一种编译时生成一套元数据代码，有点像C#，它不会有任何运行时开销。",[34,118,119],{"id":119},"菱形继承问题",[10,121,122],{},"如果对虚函数比较熟悉的，应该是写过多态继承相关的业务部分，先看个例子，这是个没有虚继承的例子：",[124,125,130],"pre",{"className":126,"code":127,"language":128,"meta":129,"style":129},"language-cpp shiki shiki-themes github-light github-dark","class Animal {\npublic:\n    int weight;\n    void eat() { std::cout \u003C\u003C \"Animal eating\" \u003C\u003C std::endl; }\n};\n\nclass Mammal : public Animal {  \u002F\u002F 普通继承\n    void breathe() { \u002F* ... *\u002F }\n};\n\nclass Bird : public Animal {    \u002F\u002F 普通继承\n    void fly() { \u002F* ... *\u002F }\n};\n\n\u002F\u002F 菱形继承：Bat 同时继承 Mammal 和 Bird\nclass Bat : public Mammal, public Bird {\n    \u002F\u002F ❌ 问题：Bat 中有两份 Animal 的副本！\n};\n","cpp","",[25,131,132,140,146,152,158,164,171,177,183,188,193,199,205,210,215,221,227,233],{"__ignoreMap":129},[133,134,137],"span",{"class":135,"line":136},"line",1,[133,138,139],{},"class Animal {\n",[133,141,143],{"class":135,"line":142},2,[133,144,145],{},"public:\n",[133,147,149],{"class":135,"line":148},3,[133,150,151],{},"    int weight;\n",[133,153,155],{"class":135,"line":154},4,[133,156,157],{},"    void eat() { std::cout \u003C\u003C \"Animal eating\" \u003C\u003C std::endl; }\n",[133,159,161],{"class":135,"line":160},5,[133,162,163],{},"};\n",[133,165,167],{"class":135,"line":166},6,[133,168,170],{"emptyLinePlaceholder":169},true,"\n",[133,172,174],{"class":135,"line":173},7,[133,175,176],{},"class Mammal : public Animal {  \u002F\u002F 普通继承\n",[133,178,180],{"class":135,"line":179},8,[133,181,182],{},"    void breathe() { \u002F* ... *\u002F }\n",[133,184,186],{"class":135,"line":185},9,[133,187,163],{},[133,189,191],{"class":135,"line":190},10,[133,192,170],{"emptyLinePlaceholder":169},[133,194,196],{"class":135,"line":195},11,[133,197,198],{},"class Bird : public Animal {    \u002F\u002F 普通继承\n",[133,200,202],{"class":135,"line":201},12,[133,203,204],{},"    void fly() { \u002F* ... *\u002F }\n",[133,206,208],{"class":135,"line":207},13,[133,209,163],{},[133,211,213],{"class":135,"line":212},14,[133,214,170],{"emptyLinePlaceholder":169},[133,216,218],{"class":135,"line":217},15,[133,219,220],{},"\u002F\u002F 菱形继承：Bat 同时继承 Mammal 和 Bird\n",[133,222,224],{"class":135,"line":223},16,[133,225,226],{},"class Bat : public Mammal, public Bird {\n",[133,228,230],{"class":135,"line":229},17,[133,231,232],{},"    \u002F\u002F ❌ 问题：Bat 中有两份 Animal 的副本！\n",[133,234,236],{"class":135,"line":235},18,[133,237,163],{},[10,239,240],{},"这里Bat同时继承了Mammal和Bird，这里有个很大的歧义，当我们实例化一个对象之后，调用的weight到底是Mammal::Animal::weight 还是 Bird::Animal::weight？并且eat方法也存在同样的歧义问题。",[10,242,243],{},"这个解决方案就是虚继承：",[124,245,247],{"className":126,"code":246,"language":128,"meta":129,"style":129},"class Animal {\npublic:\n    int weight;\n    void eat() { std::cout \u003C\u003C \"Animal eating\" \u003C\u003C std::endl; }\n};\n\nclass Mammal : virtual public Animal {  \u002F\u002F ✅ 虚继承\n    void breathe() { \u002F* ... *\u002F }\n};\n\nclass Bird : virtual public Animal {    \u002F\u002F ✅ 虚继承\n    void fly() { \u002F* ... *\u002F }\n};\n\nclass Bat : public Mammal, public Bird {\n    \u002F\u002F ✅ 现在只有一份 Animal 副本！\n};\n\nint main() {\n    Bat bat;\n    bat.weight = 10;  \u002F\u002F ✅ 没有歧义\n    bat.eat();        \u002F\u002F ✅ 正常调用\n}\n",[25,248,249,253,257,261,265,269,273,278,282,286,290,295,299,303,307,311,316,320,324,330,336,342,348],{"__ignoreMap":129},[133,250,251],{"class":135,"line":136},[133,252,139],{},[133,254,255],{"class":135,"line":142},[133,256,145],{},[133,258,259],{"class":135,"line":148},[133,260,151],{},[133,262,263],{"class":135,"line":154},[133,264,157],{},[133,266,267],{"class":135,"line":160},[133,268,163],{},[133,270,271],{"class":135,"line":166},[133,272,170],{"emptyLinePlaceholder":169},[133,274,275],{"class":135,"line":173},[133,276,277],{},"class Mammal : virtual public Animal {  \u002F\u002F ✅ 虚继承\n",[133,279,280],{"class":135,"line":179},[133,281,182],{},[133,283,284],{"class":135,"line":185},[133,285,163],{},[133,287,288],{"class":135,"line":190},[133,289,170],{"emptyLinePlaceholder":169},[133,291,292],{"class":135,"line":195},[133,293,294],{},"class Bird : virtual public Animal {    \u002F\u002F ✅ 虚继承\n",[133,296,297],{"class":135,"line":201},[133,298,204],{},[133,300,301],{"class":135,"line":207},[133,302,163],{},[133,304,305],{"class":135,"line":212},[133,306,170],{"emptyLinePlaceholder":169},[133,308,309],{"class":135,"line":217},[133,310,226],{},[133,312,313],{"class":135,"line":223},[133,314,315],{},"    \u002F\u002F ✅ 现在只有一份 Animal 副本！\n",[133,317,318],{"class":135,"line":229},[133,319,163],{},[133,321,322],{"class":135,"line":235},[133,323,170],{"emptyLinePlaceholder":169},[133,325,327],{"class":135,"line":326},19,[133,328,329],{},"int main() {\n",[133,331,333],{"class":135,"line":332},20,[133,334,335],{},"    Bat bat;\n",[133,337,339],{"class":135,"line":338},21,[133,340,341],{},"    bat.weight = 10;  \u002F\u002F ✅ 没有歧义\n",[133,343,345],{"class":135,"line":344},22,[133,346,347],{},"    bat.eat();        \u002F\u002F ✅ 正常调用\n",[133,349,351],{"class":135,"line":350},23,[133,352,353],{},"}\n",[10,355,356],{},"Mammal虚继承了Animal，Brid也是虚继承了Animal，最后Bat再去继承两个类，这样只会有一份Animal的副本。本质是使用了虚基类表来共享虚基类副本，存储子对象的偏移量，使用了vbptr 虚基类指针。",[84,358,359],{"id":359},"虚继承常见问题",[10,361,362],{},"虚继承中的初始化基类显式初始化虚基类。",[124,364,366],{"className":126,"code":365,"language":128,"meta":129,"style":129},"class Base { Base(int x) { \u002F* ... *\u002F } };\nclass D1 : virtual public Base { \n    D1(int x) : Base(x) { \u002F* ... *\u002F }  \u002F\u002F ❌ 被忽略\n};\nclass Final : public D1 {\n    Final(int x) : D1(x) { \u002F* ... *\u002F }  \u002F\u002F ❌ 错误：虚基类未初始化\n};\n\n\u002F\u002F ✅ 正确：\nclass Final : public D1 {\n    Final(int x) : Base(x), D1(x) { \u002F* ... *\u002F }  \u002F\u002F 显式初始化虚基类\n};\n",[25,367,368,373,378,383,387,392,397,401,405,410,414,419],{"__ignoreMap":129},[133,369,370],{"class":135,"line":136},[133,371,372],{},"class Base { Base(int x) { \u002F* ... *\u002F } };\n",[133,374,375],{"class":135,"line":142},[133,376,377],{},"class D1 : virtual public Base { \n",[133,379,380],{"class":135,"line":148},[133,381,382],{},"    D1(int x) : Base(x) { \u002F* ... *\u002F }  \u002F\u002F ❌ 被忽略\n",[133,384,385],{"class":135,"line":154},[133,386,163],{},[133,388,389],{"class":135,"line":160},[133,390,391],{},"class Final : public D1 {\n",[133,393,394],{"class":135,"line":166},[133,395,396],{},"    Final(int x) : D1(x) { \u002F* ... *\u002F }  \u002F\u002F ❌ 错误：虚基类未初始化\n",[133,398,399],{"class":135,"line":173},[133,400,163],{},[133,402,403],{"class":135,"line":179},[133,404,170],{"emptyLinePlaceholder":169},[133,406,407],{"class":135,"line":185},[133,408,409],{},"\u002F\u002F ✅ 正确：\n",[133,411,412],{"class":135,"line":190},[133,413,391],{},[133,415,416],{"class":135,"line":195},[133,417,418],{},"    Final(int x) : Base(x), D1(x) { \u002F* ... *\u002F }  \u002F\u002F 显式初始化虚基类\n",[133,420,421],{"class":135,"line":201},[133,422,163],{},[10,424,425],{},"类型转换具有二义性：",[124,427,429],{"className":126,"code":428,"language":128,"meta":129,"style":129},"class A {};\nclass B : virtual public A {};\nclass C : virtual public A {};\nclass D : public B, public C {};\n\nD d;\nA* a_ptr = &d;  \u002F\u002F ✅ 可以，B 和 C 共享同一个 A\n\u002F\u002F 但是：\nB* b_ptr = &d;\nA* a1 = b_ptr;   \u002F\u002F ✅ 通过虚基类指针转换\nC* c_ptr = &d;\nA* a2 = c_ptr;   \u002F\u002F ✅ 同一个地址\nassert(a1 == a2); \u002F\u002F ✅ true\n",[25,430,431,436,441,446,451,455,460,465,470,475,480,485,490],{"__ignoreMap":129},[133,432,433],{"class":135,"line":136},[133,434,435],{},"class A {};\n",[133,437,438],{"class":135,"line":142},[133,439,440],{},"class B : virtual public A {};\n",[133,442,443],{"class":135,"line":148},[133,444,445],{},"class C : virtual public A {};\n",[133,447,448],{"class":135,"line":154},[133,449,450],{},"class D : public B, public C {};\n",[133,452,453],{"class":135,"line":160},[133,454,170],{"emptyLinePlaceholder":169},[133,456,457],{"class":135,"line":166},[133,458,459],{},"D d;\n",[133,461,462],{"class":135,"line":173},[133,463,464],{},"A* a_ptr = &d;  \u002F\u002F ✅ 可以，B 和 C 共享同一个 A\n",[133,466,467],{"class":135,"line":179},[133,468,469],{},"\u002F\u002F 但是：\n",[133,471,472],{"class":135,"line":185},[133,473,474],{},"B* b_ptr = &d;\n",[133,476,477],{"class":135,"line":190},[133,478,479],{},"A* a1 = b_ptr;   \u002F\u002F ✅ 通过虚基类指针转换\n",[133,481,482],{"class":135,"line":195},[133,483,484],{},"C* c_ptr = &d;\n",[133,486,487],{"class":135,"line":201},[133,488,489],{},"A* a2 = c_ptr;   \u002F\u002F ✅ 同一个地址\n",[133,491,492],{"class":135,"line":207},[133,493,494],{},"assert(a1 == a2); \u002F\u002F ✅ true\n",[10,496,497],{},"有性能开销：",[124,499,501],{"className":126,"code":500,"language":128,"meta":129,"style":129},"class Base { int x; };\nclass Derived : virtual public Base { int y; };\n\nDerived d;\nd.x = 10;  \u002F\u002F 访问需要经过 vbptr 间接寻址，比普通成员访问慢\n\n\u002F\u002F 在性能敏感的场景下，虚继承可能成为瓶颈\n",[25,502,503,508,513,517,522,527,531],{"__ignoreMap":129},[133,504,505],{"class":135,"line":136},[133,506,507],{},"class Base { int x; };\n",[133,509,510],{"class":135,"line":142},[133,511,512],{},"class Derived : virtual public Base { int y; };\n",[133,514,515],{"class":135,"line":148},[133,516,170],{"emptyLinePlaceholder":169},[133,518,519],{"class":135,"line":154},[133,520,521],{},"Derived d;\n",[133,523,524],{"class":135,"line":160},[133,525,526],{},"d.x = 10;  \u002F\u002F 访问需要经过 vbptr 间接寻址，比普通成员访问慢\n",[133,528,529],{"class":135,"line":166},[133,530,170],{"emptyLinePlaceholder":169},[133,532,533],{"class":135,"line":173},[133,534,535],{},"\u002F\u002F 在性能敏感的场景下，虚继承可能成为瓶颈\n",[84,537,538],{"id":538},"最佳实践",[10,540,541],{},"在开发中，应该接口优先，使用纯虚类作为接口，虚继承来组合，避免多层虚继承，尽量只在一个层级使用虚继承，并且应该明确初始化，在最终派生类中明确初始化虚基类。如果不需要多态行为，则考虑使用组合代替虚继承。",[84,543,544],{"id":544},"内存布局",[10,546,547],{},"我们在类中定义virtual方法的本质是创建vtable，而虚继承是创建vbtable。虚继承会通过间接寻址来实现共享，而不是复制数据。因为普通派生类都会保存自己的Animal副本，也就是刚才上面的代码实例，我们访问weight eat会有歧义，当我们使用了虚继承之后派生类会通过指针共享同一个Animal副本，就可以去掉歧义效果。",[10,549,550],{},"vbtable一般存储在只读数据段中，和vtable、RTTI等元数据放在一起：",[124,552,557],{"className":553,"code":555,"language":556},[554],"language-text","┌─────────────────────────────────────────────┐\n│  程序内存布局                                │\n├─────────────────────────────────────────────┤\n│  代码段 (.text)     ← 可执行代码            │\n├─────────────────────────────────────────────┤\n│  只读数据段 (.rdata) ← vtable, vbtable,    │\n│                        RTTI, 字符串常量     │\n├─────────────────────────────────────────────┤\n│  数据段 (.data)     ← 全局\u002F静态变量        │\n├─────────────────────────────────────────────┤\n│  BSS 段 (.bss)      ← 未初始化的全局变量   │\n├─────────────────────────────────────────────┤\n│  堆 (heap)          ← 动态分配的内存       │\n├─────────────────────────────────────────────┤\n│  栈 (stack)         ← 局部变量             │\n└─────────────────────────────────────────────┘\n","text",[25,558,555],{"__ignoreMap":129},[10,560,561,562,565,566,569,570,573,574,580],{},"在内存空间内，代码段在最前面，栈在靠后位置，属于高地址，代码段是最低的地址，并且一般加载的时候基地址是否固定就看是不是启用了ASLR 地址空间布局随机化。如果没有启用，那么加载基地址是固定的，WINDOWS EXE 默认是0x00400000，WINDOWS DLL是 0x10000000， ",[17,563,564],{},"Linux ELF","：默认基址通常是 ",[25,567,568],{},"0x08048000"," (x86) 或 ",[25,571,572],{},"0x400000"," (x64)。这意味着程序中的函数地址、全局变量地址，甚至我们讨论的 ",[17,575,576,579],{},[25,577,578],{},"vbtable"," 的地址，在每次运行时都是完全一样的","。这在早期是常态，但也带来了巨大的安全风险。",[10,582,583],{},"现代操作系统默认强制开启ASLR，操作系统在加载的时候给程序生成一个随机偏移量，vbtable的地址每次运行都会随机变化，无法知道绝对地址，CPU和操作系统通过相对寻址和重定位表来解决问题，这个主要为了防止ROP攻击，让攻击者无法跳转到内存中特定代码地址。",[34,585,587],{"id":586},"cpp调试问题","CPP调试问题",[10,589,590],{},"当我们访问一个无效的内存空间，比如说野指针、已经释放的内存、超出边界，而这块乃村并没有被映射到任何有效区域，WINDOWS会触发访问违规异常，如果是DEBUG模式下，编译器会在未初始化内存区域填充0XCC，作为哨兵标记，如果你试图访问0XCC的话就会触发INT3指令断点。",[10,592,593],{},"MSVC debug模式下常见的填充模式：",[595,596,597,610],"table",{},[598,599,600],"thead",{},[601,602,603,607],"tr",{},[604,605,606],"th",{},"填充值",[604,608,609],{},"含义",[611,612,613,631,648,662,676],"tbody",{},[601,614,615,621],{},[616,617,618],"td",{},[25,619,620],{},"0xCC",[616,622,623,626,627,630],{},[17,624,625],{},"未初始化的栈变量","、",[25,628,629],{},"INT 3"," 断点",[601,632,633,638],{},[616,634,635],{},[25,636,637],{},"0xCD",[616,639,640,643,644,647],{},[17,641,642],{},"已释放的堆内存","（",[25,645,646],{},"_CrtMemBlockHeader","）",[601,649,650,655],{},[616,651,652],{},[25,653,654],{},"0xDD",[616,656,657,643,660,647],{},[17,658,659],{},"已释放的栈变量",[25,661,646],{},[601,663,664,669],{},[616,665,666],{},[25,667,668],{},"0xFD",[616,670,671,643,674,647],{},[17,672,673],{},"堆的边界守卫",[25,675,646],{},[601,677,678,683],{},[616,679,680],{},[25,681,682],{},"0xFE",[616,684,685,643,688,647],{},[17,686,687],{},"未使用的堆内存",[25,689,646],{},[10,691,692,693,695,696,698,699,702,703,705],{},"访问 ",[25,694,620],{}," 会触发 ",[25,697,629],{}," 断点，调试器会立即暂停，访问未初始化的变量时，你会看到 ",[25,700,701],{},"0xCCCCCCCC","，提示你忘记初始化了，填充行为在栈帧之间填充 ",[25,704,620],{},"，防止缓冲区溢出。",[10,707,708],{},"Release模式下，这些填充和检查都会被移除，因为会影响性能，生产环境不依赖调试辅助，如果访问的是无效内存，会直接崩溃程序，而不是触发断点。",[10,710,711,712,715,716,719,720,723,724,727],{},"哦对了，这个不是msvc的独有行为，gcc和clang都支持类似的功能，但是需要主动通过编译选项来开始，而不是默认，GCC 和 Clang 都提供了 ",[25,713,714],{},"-ftrivial-auto-var-init="," 选项，可以指定使用 ",[25,717,718],{},"zero","（全零）或 ",[25,721,722],{},"pattern","（特定模式，如 ",[25,725,726],{},"0xAA","）来初始化未显式初始化的自动变量。",[10,729,730,731,734,735,741,742,745,746,749,750,32],{},"在 Linux 内核这类强调安全的项目中，Clang 可能会默认启用 ",[25,732,733],{},"-ftrivial-auto-var-init=pattern"," 选项",[736,737],"a",{"href":738,"rel":739},"https:\u002F\u002Fcgit.freebsd.org\u002Fsrc\u002Fcommit\u002Ftests?h=vendor\u002Fopenzfs\u002Fmaster&id=d0aa9dbccfb06778ca336732ee4e627f50475ad3",[740],"nofollow","。此外，Clang 对",[17,743,744],{},"未初始化变量的使用检测也更为积极","，例如会对 ",[25,747,748],{},"int i = i;"," 这种自我初始化写法直接报错",[736,751],{"href":752,"rel":753},"https:\u002F\u002Finbox.sourceware.org\u002Fgcc\u002FCAF1jjLuKQPQ69+t=Vb7XLpY7Drvp9FYgrEV402Nvw8=2JVqzHQ@mail.gmail.com\u002FT\u002F",[740],[755,756,757],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":129,"searchDepth":142,"depth":142,"links":759},[760,761,764,769],{"id":36,"depth":142,"text":36},{"id":82,"depth":142,"text":82,"children":762},[763],{"id":86,"depth":148,"text":86},{"id":119,"depth":142,"text":119,"children":765},[766,767,768],{"id":359,"depth":148,"text":359},{"id":538,"depth":148,"text":538},{"id":544,"depth":148,"text":544},{"id":586,"depth":142,"text":587},"md",{},"\u002Fcpp\u002Ffeatures\u002Fcpp的符号表",{"description":12},"cpp\u002Ffeatures\u002FCPP的符号表","tG_kuYMThkkBQ_pKY788kzez-PhWzJLeKESut2bLuWo",1791042462667]