
对内置类型对象进行手动初始化因为c不保证初始化它们。对于内置类型有些场景会正常初使化但是有些场景不会比如局部变量。如果不进行初使化变量是个随机值这在后面的使用可能会导致未知错误。c不初始化内置类型的原因1.为了性能。 C 的设计原则“你不用为不使用的东西付出代价”。在底层初始化一个变量比如把一个int设为 0意味着 CPU 必须执行一条内存写入指令。这会拖慢CPU的执行效率。2. 兼容 C 语言。使用工具检查未初使化变量gcc编译阶段检查-O1-Wuninitialized-Wmaybe-uninitialized-O1/-O2/-O3前置条件如果你的项目处于-O0不优化模式GCC 的未初始化检查是直接失效的底层原因未初始化检查属于高级数据流分析。只有开启了优化哪怕是最基础的-O1GCC 才会去构建函数的控制流图、分析变量的生命周期。如果在调试阶段Debug想抓这个 Bug切记至少要开到-O1。-Wuninitialized作用开启基础的未初始化检查。当 GCC 能够100% 确定某条代码路径上一个局部变量在没有被赋值的情况下就被读取了就会弹出警告。-Wmaybe-uninitialized作用开启“可能未初始化”的检查。它对付的是那些复杂的逻辑分支比如if-else或switch-case。如果变量只在某些分支里赋了值但在另一些分支里没赋值、且后续被读取了GCC 就会报这个警告。示例cpp #include iostream int main(int argc, char* argv[]) { int a; std::coutastd::endl; }对于上面的代码在执行带检查参数的时候g --stdc11 -O1 -Wuninitialized -Wmaybe-uninitialized noinitialized.cpp会有下面的告警noinitialized.cpp:4:16: 警告‘a’未经初始化被使用 [-Wuninitialized] 4 | std::coutastd::endl; | ^ noinitialized.cpp:3:9: 附注‘a’在此声明 3 | int a; | ^使用Clang-Tidy进行静态检查clang-tidy noinitialized.cpp -header-filter.*-checkscppcoreguidelines-pro-type-member-init,clang-analyzer-core.UndefinedBinaryOperatorResult---stdc11会有如下报错5 warnings generated. noinitialized.cpp:4:5: warning: 1st function call argument is an uninitialized value [clang-analyzer-core.CallAndMessage] 4 | std::coutastd::endl; | ^ ~ noinitialized.cpp:3:5: note: a declared without an initial value 3 | int a; | ^~~~~ noinitialized.cpp:4:5: note: 1st function call argument is an uninitialized value 4 | std::coutastd::endl; | ^ ~构造函数初使成员变量构造函数初使化成员变量尽量优先使用初始化列表的方式进行初使。原因分析如下区分赋值和初始化// 初始化示例classStorageNode{public:// 正确示范使用初始化列表StorageNode(intid,conststd::stringpath):node_id(id),meta_path(path)// 顺便提一句这里的顺序最好跟下面声明的一致{// 此时成员变量已经完全初始化好了这里通常是空的}private:intnode_id;std::string meta_path;};// 赋值示例classStorageNode{public:StorageNode(intid,conststd::stringpath){node_idid;// 这是赋值不是初始化meta_pathpath;// 这是赋值不是初始化}private:intnode_id;std::string meta_path;};一个对象的初始化流程在底层经历了以下三个严格的步骤基类优先如果这个类有父类先按照继承列表的顺序初始化父类。成员依序接着按照成员变量在类中从上到下声明的顺序依次初始化。构造函数体最后才执行构造函数大括号{}内部的赋值代码。不推荐使用赋值的原因在进入构造函数的函数体之前会对成员变量执行默认初使化然后再进入函数体进行赋值。这对于内置类型没有什么性能区别但是对于复杂的类类型来说会先调用一次默认构造函数然后调用一次拷贝构造函数。对比使用初始化列表的方式进行初始化多了一次构造函数调用。Non-local static对象的初使化顺序Non-local static是指定义在非局部作用域Non-local scope且具有静态存储期Static storage duration的对象local static对象会在该函数被调用时首次遇上该对象的定义时被初使化。non-local static对象的初始化顺序在同一个.cpp源文件里non-local static对象严格按照它们从上到下的出现顺序依次初始化。C 标准不保证不同源文件.cpp之间静态对象的初始化顺序这也就是说对于不同源文件中的non-local static对象之间如果有依赖关系那么就很有问题了。那么怎么解决这个问题呢——用local static对象替换non-local static对象即常见的单例模式。// 将全局变量换成一个返回引用的静态函数inlinestd::stringGetGlobalConfigPath(){// 完美的保底谁先调用这个函数这个静态字符串就会瞬间当场初始化staticstd::string config_path/etc/kv_server.conf;returnconfig_path;}// 别的地方使用时LoadConfig(GetGlobalConfigPath());// 绝对安全100% 不可能读到未初始化的状态使用asan定位初始化顺序问题// file1.cppintget_init_value(){return10;}intaget_init_value();// file2.cpp#includeiostreamexterninta;intba1;intmain(intargc,char*argv[]){std::coutbstd::endl;return0;}这个代码可以正常编译运行但是输出的结果不是预期的11而是1。使用带asan参数的g命令编译g -fsanitizeaddress -g file2.cpp file1.cpp -o asan_init_test --stdc11然后开启初始化检测参数执行编译好的二进制文件ASAN_OPTIONScheck_initialization_ordertrue:strict_init_ordertrue ./asan_init_test。会输出如下报错29992ERROR: AddressSanitizer: initialization-order-fiasco on address 0x000000403380 at pc 0x0000004005a7 bp 0x7ffee40ddf00 sp 0x7ffee40ddef8 READ of size 4 at 0x000000403380 thread T0 #0 0x0000004005a6 in __static_initialization_and_destruction_0 effective_cpp/item_4/file2.cpp:4 #1 0x0000004005fc in _GLOBAL__sub_I_b effective_cpp/item_4/file2.cpp:9 #2 0x7f405ac106e3 in __libc_start_mainGLIBC_2.34 (/lib64/libc.so.60x36e3) (BuildId: ff0267465bc3d76e21003b3bc5598fd5ee63e261) #3 0x000000400444 in _start (effective_cpp/item_4/app0x400444) (BuildId: 648f97369c399543264dd0fc39bf1ec9655852a9) 0x000000403380 is located 0 bytes inside of global variable a defined in file1.cpp:5:5 (0x000000403380) of size 4 registered at: #0 0x7f405b21be68 in __asan_register_globals.part.0 (/lib64/libasan.so.80x1be68) (BuildId: 25975f766867e9e604dc5a71a8befeaed3301942) #1 0x0000004006d3 in _sub_I_00099_1 (effective_cpp/item_4/app0x4006d3) (BuildId: 648f97369c399543264dd0fc39bf1ec9655852a9) #2 0x7f405ac106e3 in __libc_start_mainGLIBC_2.34 (/lib64/libc.so.60x36e3) (BuildId: ff0267465bc3d76e21003b3bc5598fd5ee63e261) #3 0x000000400444 in _start (effective_cpp/item_4/app0x400444) (BuildId: 648f97369c399543264dd0fc39bf1ec9655852a9) SUMMARY: AddressSanitizer: initialization-order-fiasco effective_cpp/item_4/file2.cpp:4 in __static_initialization_and_destruction_0 Shadow bytes around the buggy address: 0x000000403100: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x000000403180: 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 0x000000403200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403300: 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 0x000000403380:[f6]f6 f6 f6 f6 f6 f6 f6 00 00 00 00 00 00 00 00 0x000000403400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x000000403600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb 29992ABORTING