├── LICENSE ├── README.md ├── acknowledgements.md ├── c-cpp-rules.json ├── c-cpp-rules.md ├── c-ub-list.md ├── cpp-ub-list.md ├── logo.png └── main-logo.png /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | # 360 安全规则集合 6 | 7 | [![Version](https://img.shields.io/badge/v-1.3-blue)](https://github.com/Qihoo360/safe-rules/blob/main/c-cpp-rules.md) 8 | [![License](https://img.shields.io/badge/license-Apache--2.0-brightgreen)](https://github.com/Qihoo360/safe-rules/blob/main/LICENSE) 9 | 10 |
11 | 12 |    13 |   《360 安全规则集合》简称《安规集》,是一套详细的 C/C++ 安全编程指南,由 360 集团质量工程部编著,将编程时需要注意的问题总结成若干规则,可为制定编程规范提供依据,也可为代码审计或相关培训提供指导意见,旨在提升软件产品的可靠性、健壮性、可移植性以及可维护性,从而提升软件产品的综合安全性能。 14 | 15 |   《安规集》适用于桌面、服务端及嵌入式软件系统,提供: 16 | 17 |     [`c-cpp-rules.md`](./c-cpp-rules.md):C/C++ 规则详细说明文档 18 |     [`c-cpp-rules.json`](./c-cpp-rules.json):C/C++ 规则结构化文档 19 |     [`c-ub-list.md`](./c-ub-list.md):C 未定义行为成因列表 20 |     [`cpp-ub-list.md`](./cpp-ub-list.md):C++ 未定义行为成因列表 21 | 22 |   各文档的网页版本请参见: [https://saferules.github.io/](https://saferules.github.io/) 23 | 24 |   传达语言标准,融汇行业标准,积极推广安全编程理念是《安规集》的宗旨。欢迎提供修订意见和扩展建议,由于《安规集》的文档是自动生成的,请不要直接编辑各文档,可在 Issue 区提出意见,管理员录入数据库后会在专门的“[致谢列表](./acknowledgements.md)”中铭记您的贡献。 25 | 26 |   可在 Apache-2.0 协议许可范围内随意传播《安规集》相关内容,也可以适当取舍某些规则以适应自身需求。 27 | -------------------------------------------------------------------------------- /acknowledgements.md: -------------------------------------------------------------------------------- 1 | # 致谢 2 | 3 | 感谢 360 技术中台、360 质量工程部效率平台,以及各产品线的指导和支持。 4 | 5 | 感谢下列 Github 贡献者的指导: 6 | - [qihoo360github](https://github.com/qihoo360github) 7 | - [comicer424](https://github.com/comicer424) 8 | - [lixue-xy](https://github.com/lixue-xy) 9 | - [caixuewen360](https://github.com/caixuewen360) 10 | - [jinpu3000](https://github.com/jinpu3000) 11 | - [likezjuisee](https://github.com/likezjuisee) 12 | - [LiMuoMuo](https://github.com/LiMuoMuo) 13 | - [wuxvincent](https://github.com/wuxvincent) 14 | - [LiuLingLing1217](https://github.com/LiuLingLing1217) 15 | - [Ohto-Ai](https://github.com/Ohto-Ai) 16 | - [happycoderat](https://github.com/happycoderat) 17 | - [Zaoly](https://github.com/Zaoly) 18 | - [lhmouse](https://github.com/lhmouse) 19 | - [Timothy-Liuxf](https://github.com/Timothy-Liuxf) 20 | - [onionblack](https://github.com/onionblack) 21 | - [Mq-b](https://github.com/Mq-b) 22 | - [frederick-vs-ja](https://github.com/frederick-vs-ja) 23 | - [xukeawsl](https://github.com/xukeawsl) 24 | - [adah1972](https://github.com/adah1972) 25 | - [plopop](https://github.com/plopop) 26 | 27 | 感谢下列组织和媒体的推广: 28 | - [360 技术](https://blog.csdn.net/qihoo_tech) 29 | - [QECon 全球软件质量效能大会](http://www.qecon.net/) 30 | - [HelloGitHub](https://hellogithub.com/) 31 | - [爱码网](https://www.likecs.com/) 32 | 33 | 本列表会实时更新,如有疏漏请联系我们。欢迎提供指导意见,愿我们共同创造安全、享受安全! 34 | 35 | 再次向大家表示最诚挚的谢意! 36 | -------------------------------------------------------------------------------- /c-ub-list.md: -------------------------------------------------------------------------------- 1 | # 附录:C 未定义行为成因列表 ![Version](https://img.shields.io/badge/version-1.4.0-brightgreen) 2 | 3 | > blog.llvm.org: "*Many seemingly reasonable things in C actually have undefined behavior, and this is a common source of bugs in programs.*" 4 | 5 |   未定义的行为(Undefined Behavior),指程序不可预测的执行效果,一般由错误的代码实现引起。出于效率、兼容性等多方面原因,语言标准不便于定义错误程序的明确行为,而是将其统称为“未定义”的行为,可以是崩溃,也可以是非预期的任意表现,有些问题在编译器和执行环境的特殊处理下也可能不会造成实质性的危害,但不具备可移植性。代码质量管理的一个重要目标就是消除会导致未定义行为的代码。 6 | 7 |   ISO/IEC 9899:2011 在 Annex J.2 中罗列了导致未定义行为的各种情况,但某些条目缺少上下文信息不便于直接阅读,本文对其进行了补充并转译如下: 8 | 9 |
10 | 11 | | ID | Circumstance | Provenance | 12 | | :----: | :---- | :---- | 13 | | 1 | 违反了出现在约束之外的 shall 或 shall not 要求 | clause 4 | 14 | | 2 | 非空源文件未以换行符结尾,或以换行符结尾但换行符之前是反斜杠,或以不完整的预处理符号结尾,或以不完整的注释结尾 | 5.1.1.2 | 15 | | 3 | 预处理符号连接产生了通用字符名称(universal character name) | 5.1.1.2 | 16 | | 4 | 宿主环境(hosted environment)中的程序没有按要求定义 main 函数 | 5.1.2.2.1 | 17 | | 5 | 具有数据竞争(data race)的程序 | 5.1.2.4 | 18 | | 6 | 除标识符、字符、字符串、头文件名、注释、不会转换成符号的预处理符号之外,存在非基本字符集中的字符 | 5.2.1 | 19 | | 7 | 标识符、注释、字符、字符串、头文件名中包含非法字符 | 5.2.1.2 | 20 | | 8 | 在一个翻译单元中同一个标识符既具有内部链接性又具有外部链接性 | 6.2.2 | 21 | | 9 | 在对象生命周期之外访问对象 | 6.2.4 | 22 | | 10 | 指针指向的对象生命周期已结束,但指针的值被继续使用 | 6.2.4 | 23 | | 11 | 使用未初始化或已被销毁的具有自动存储期的对象 [\[注\]](#comment_11) | 6.2.4 | 24 | | 12 | 通过非字符类型的 lvalue 读取 trap representation | 6.2.6.1 | 25 | | 13 | 通过非字符类型的 lvalue 产生 trap representation | 6.2.6.1 | 26 | | 14 | 某些运算可以产生负 0,但实现不支持负 0 | 6.2.6.2 | 27 | | 15 | 对同一对象或函数进行多次类型不兼容的声明 | 6.2.7 | 28 | | 16 | 由变长数组类型(variable length array,VLA)获取相关的合成类型(composite type)时,数组长度没有求值 | 6.2.7 | 29 | | 17 | 整数类型转换产生了相关类型无法表示的值 | 6.3.1.4 | 30 | | 18 | 浮点类型转换产生了相关类型无法表示的值 | 6.3.1.5 | 31 | | 19 | 对未指定对象的 lvalue 求值 | 6.3.2.1 | 32 | | 20 | 在需要对象值的上下文中使用具有不完整类型的非数组 lvalue | 6.3.2.1 | 33 | | 21 | 通过 lvalue 访问未初始化的数据 | 6.3.2.1 | 34 | | 22 | 数组类型的 lvalue 被转为指向数组起始元素的指针,而数组对象具有寄存器存储类 | 6.3.2.1 | 35 | | 23 | 使用 void 表达式的值,或对 void 表达式进行类型转换(除了转为 void) | 6.3.2.2 | 36 | | 24 | 将指针转为无法表示指针值的整数 | 6.3.2.3 | 37 | | 25 | 指针之间的转换造成了未正确对齐的结果 | 6.3.2.3 | 38 | | 26 | 通过指针调用不兼容类型的函数 | 6.3.2.3 | 39 | | 27 | 在一个逻辑行中单引号或双引号不匹配 | 6.4 | 40 | | 28 | 在预处理阶段使关键字参与和其保留意义无关的工作 | 6.4.1 | 41 | | 29 | 标识符中广义字符名称的值不在许可的范围内 | 6.4.2.1 | 42 | | 30 | 标识符起始字符为表示数字的广义字符名称 | 6.4.2.1 | 43 | | 31 | 两个标识符仅在非重要字符上有所不同 | 6.4.2.1 | 44 | | 32 | 标识符 \_\_func\_\_ 被显式声明 | 6.4.2.2 | 45 | | 33 | 修改字符串字面常量 | 6.4.5 | 46 | | 34 | 在 \#include 指令中,'、"、\\、//、/\* 出现在定界符 < > 之间,或 '、\\、//、/\* 出现在定界符 " 之间 | 6.4.7 | 47 | | 35 | 表达式求值依赖无确定顺序的副作用 | 6.5 | 48 | | 36 | 表达式的结果未在数学上定义或超出其类型的取值范围 | 6.5 | 49 | | 37 | 通过非法类型的 lvalue 访问对象 | 6.5 | 50 | | 38 | 调用没有原型声明的函数,实参与形参个数不符 | 6.5.2.2 | 51 | | 39 | 调用没有原型但在相关作用域中有定义的函数时实参与形参类型不兼容 | 6.5.2.2 | 52 | | 40 | 调用没有原型且在相关作用域中也没有定义的函数时实参与形参类型不兼容 | 6.5.2.2 | 53 | | 41 | 函数定义时的类型与函数调用时的类型不兼容 | 6.5.2.2 | 54 | | 42 | 访问原子结构体或联合体的成员 | 6.5.2.3 | 55 | | 43 | 一元 \* 运算符作用于无效值 | 6.5.3.2 | 56 | | 44 | 将指针转为非整数非指针的其他类型 | 6.5.4 | 57 | | 45 | / 或 % 运算符第二个操作数的值为 0 | 6.5.5 | 58 | | 46 | 指针加减运算产生了超越数组边界的结果 | 6.5.6 | 59 | | 47 | 指针加减运算产生了超越数组边界的结果,并对其解引用 | 6.5.6 | 60 | | 48 | 未指向同一数组的指针相减 | 6.5.6 | 61 | | 49 | 数组下标越界 | 6.5.6 | 62 | | 50 | 指针相减的结果不能用 ptrdiff\_t 类型的对象表示 | 6.5.6 | 63 | | 51 | 移位运算符右操作数为负数或超过相关类型比特位的数量 | 6.5.7 | 64 | | 52 | 对负数进行左移运算 | 6.5.7 | 65 | | 53 | 比较未指向同一对象或数组的指针 | 6.5.8 | 66 | | 54 | 将对象的值赋给具有部分重叠区域的对象,或完全重叠但类型不兼容的对象 | 6.5.16.1 | 67 | | 55 | 要求具有常量整数类型的表达式不具有整数类型,或相关操作数不具有整数类型 | 6.6 | 68 | | 56 | 初始化器(initializer)中的常量表达式不是:算术常量表达式,空指针常量,地址常量,或地址常量加减常量偏移量 | 6.6 | 69 | | 57 | 常量算术表达式不具备算术类型,或者相关操作数不是整型常量、浮点数常量、枚举常量、字符常量、sizeof 表达式、\_Alignof 表达式等 | 6.6 | 70 | | 58 | 需要常量表达式时,通过 \[ \]、.、\->、&、\* 等运算符访问对象的值 | 6.6 | 71 | | 59 | 声明对象标识符时没有声明链接性,并且对象的类型在其声明符之后或在其初始化声明符之后不完整 | 6.7 | 72 | | 60 | 在局部作用域中使用除 extern 之外的存储类说明符声明函数 | 6.7.1 | 73 | | 61 | 结构体或联合体没有任何具名成员 | 6.7.2.1 | 74 | | 62 | 访问没有元素的柔性数组(flexible array) | 6.7.2.1 | 75 | | 63 | 需要完整类型时,没有提供完整类型定义 | 6.7.2.3 | 76 | | 64 | 通过没有 const 限定的 lvalue 修改常量对象 | 6.7.3 | 77 | | 65 | 通过没有 volatile 限定的 lvalue 访问 volatile 对象 | 6.7.3 | 78 | | 66 | 函数类型被 const、volatile、restrict、\_Atomic 等类型限定符限定 | 6.7.3 | 79 | | 67 | 应该兼容的类型不具备相同的类型限定符(const、volatile 等) | 6.7.3 | 80 | | 68 | 通过常量类型的 restrict 指针访问已被修改的对象 | 6.7.3.1 | 81 | | 69 | restrict 指针指向基于另一个 restrict 指针的对象 | 6.7.3.1 | 82 | | 70 | 具有外部链接性的函数被声明为 inline,但没有在相同的翻译单元中定义 | 6.7.4 | 83 | | 71 | 具有 \_Noreturn 属性的函数返回至调用方 | 6.7.4 | 84 | | 72 | 同一对象的定义和声明具有不同的对齐说明符(alignment specifier) | 6.7.5 | 85 | | 73 | 不同翻译单元内对同一对象的声明具有不同的对齐说明符(alignment specifier) | 6.7.5 | 86 | | 74 | 应该兼容的指针类型不具备相同的类型限定符(const、volatile 等),或指向不同的类型 | 6.7.6.1 | 87 | | 75 | 数组声明中的长度表达式不是常量表达式且在运行时的结果不是正整数 | 6.7.6.2 | 88 | | 76 | 应该兼容的数组类型不具备兼容的元素类型,或者数组长度不同 | 6.7.6.2 | 89 | | 77 | 为由 static 限定长度的数组参数,提供未指向数组起始元素的指针,或实际数组的长度小于 static 限定的长度 | 6.7.6.3 | 90 | | 78 | 由存储类说明符或类型限定符修饰的 void 作为参数列表 | 6.7.6.3 | 91 | | 79 | 应该兼容的函数类型不具备兼容的返回类型或参数类型 | 6.7.6.3 | 92 | | 80 | 使用结构体或联合体匿名成员的值 | 6.7.9 | 93 | | 81 | 标量(scalar)的初始化器(initializer)既不是单独的表达式,也不是单独的由大括号括起来的表达式 | 6.7.9 | 94 | | 82 | 具有自动存储期的结构体或联合体对象,其初始化器(initializer)既不是初始化列表,也不是具有兼容类型的单独表达式 | 6.7.9 | 95 | | 83 | 除用字符串初始化字符数组之外,数组的初始化器(initializer)不是大括号形式的初始化列表 | 6.7.9 | 96 | | 84 | 具有外部链接性的标识符未被定义,或有多重定义 | 6.9 | 97 | | 85 | 定义函数时没有声明参数的类型 | 6.9.1 | 98 | | 86 | 被调整的参数类型(adjusted parameter type)在函数定义中不是完整的对象类型 | 6.9.1 | 99 | | 87 | 接受可变参数的函数在定义时没有用省略号作为参数列表的最后一个参数 | 6.9.1 | 100 | | 88 | 函数没有通过 return 语句返回明确的值,但函数返回值被使用 | 6.9.1 | 101 | | 89 | 试探性定义(tentative definition)具有内部链接性和不完整类型的对象 | 6.9.2 | 102 | | 90 | 在 \#if、 \#elif 的条件中,由宏展开产生了 defined 关键字,或 defined 关键字格式不正确 | 6.10.1 | 103 | | 91 | \#include 指令不符合标准格式 | 6.10.2 | 104 | | 92 | \#include 指令中头文件名称不是以字母开头 | 6.10.2 | 105 | | 93 | 宏的实参列表中出现预处理指令 | 6.10.3 | 106 | | 94 | 预处理运算符 \# 的结果不是有效的字符串 | 6.10.3.2 | 107 | | 95 | 预处理运算符 \#\# 的结果不是有效的符号 | 6.10.3.3 | 108 | | 96 | \#line 指令不符合标准格式,或指定的行号为 0,或指定的行号大于 2147483647 | 6.10.4 | 109 | | 97 | 非 STDC 的 \#pragma 指令导致翻译失败,或引入其他未定义的行为 | 6.10.6 | 110 | | 98 | \#pragma STDC 不符合标准格式 | 6.10.6 | 111 | | 99 | 定义或取消定义保留的宏或标识符名称 | 6.10.8 | 112 | | 100 | 在库函数不允许的情况下复制具有重叠区域的对象 | clause 7 | 113 | | 101 | 包含了与标准头文件名称相同,但未提供标准头文件相同功能的头文件 | 7.1.2 | 114 | | 102 | 在外部声明或定义中包含头文件 | 7.1.2 | 115 | | 103 | 在包含标准头文件之前使用其中的函数、对象、类型或宏 | 7.1.2 | 116 | | 104 | 定义与关键字同名的宏后包含标准头文件 | 7.1.2 | 117 | | 105 | 程序没有通过标准头文件而是自行声明了库函数,且该库函数的声明不具备外部链接性 | 7.1.2 | 118 | | 106 | 程序定义或声明了不被许可的保留标识符名称 | 7.1.3 | 119 | | 107 | 程序去除了以下划线加大写字母以及两个下划线开头的宏定义 | 7.1.3 | 120 | | 108 | 为有可变参数列表的库函数提供具有无效值或无效类型的参数 | 7.1.4 | 121 | | 109 | 对于传递给库函数的数组,不能保证其中所有地址计算和对象访问都是有效的 | 7.1.4 | 122 | | 110 | assert 宏被抑制 | 7.2 | 123 | | 111 | assert 宏的参数不具备标量类型(scalar type) | 7.2 | 124 | | 112 | 在不适当的场合进行与 CX\_LIMITED\_RANGE、FENV\_ACCESS、FP\_CONTRACT 相关的预处理设置 [\[注\]](#comment_112) | 7.3.4 | 125 | | 113 | 提供给字符处理函数的参数既不是 EOF 也不是能用 unsigned char 表示的值 | 7.4 | 126 | | 114 | errno 宏被抑制,或程序定义了同名标识符 | 7.5 | 127 | | 115 | FENV\_ACCESS 相关的预处理设置不当 | 7.6.1 | 128 | | 116 | exception\-mask 参数没有正确地与浮点异常相关的宏配合使用 | 7.6.2 | 129 | | 117 | fesetexceptflag 没有正确地与 fegetexceptflag 配合使用 | 7.6.2.4 | 130 | | 118 | fesetenv 没有正确地与 fegetenv 配合使用,feupdateenv 没有正确地与 feholdexcept 配合使用 [\[注\]](#comment_118) | 7.6.4.3 | 131 | | 119 | 整数运算或转换函数的结果不能被有效表示 [\[注\]](#comment_119) | 7.8.2.1 | 132 | | 120 | setlocale 函数返回的数据被修改 | 7.11.1.1 | 133 | | 121 | localeconv 函数返回的数据被修改 | 7.11.2.1 | 134 | | 122 | math\_errhandling 宏被抑制,或定义了与 math\_errhandling 相同的标识符 | 7.12 | 135 | | 123 | 为浮点数分类或比较相关的宏提供非真浮点类型的参数 [\[注\]](#comment_123) | 7.12.3 | 136 | | 124 | setjmp 宏被抑制,或程序定义了与 setjmp 相同的外部名称 | 7.13 | 137 | | 125 | 在不被允许的上下文中使用 setjmp | 7.13.2.1 | 138 | | 126 | 用 longjmp 跳向已不存在的执行环境 | 7.13.2.1 | 139 | | 127 | longjmp 后,访问具有自动存储期的,没有 volatile 类型限定的,且在 longjmp 和对应的 setjmp 之间被修改的对象 | 7.13.2.1 | 140 | | 128 | 用无效指针指定信号处理函数 | 7.14.1.1 | 141 | | 129 | 与计算异常(computational exception)对应的信号处理函数正常返回 | 7.14.1.1 | 142 | | 130 | SIGFPE、SIGILL、SIGSEGV 的信号处理函数,或其他由实现定义的与计算异常(computational exception)对应的信号处理函数正常返回 | 7.14.1.1 | 143 | | 131 | 处理 abort 或 raise 函数产生的信号时,在信号处理函数中调用 raise 函数 | 7.14.1.1 | 144 | | 132 | 处理程序外部产生的信号时引用非 lock\-free 并具有静态或线程存储期的对象,且这种对象不是 volatile sig\_atomic\_t 类型,相关操作也不是赋值,或者使用 abort、\_Exit、quick\_exit、signal 之外的库函数 [\[注\]](#comment_132) | 7.14.1.1 | 145 | | 133 | 处理程序外部产生的信号时调用 signal 函数,当其返回 SIG\_ERR 时访问 errno 的值 [\[注\]](#comment_133) | 7.14.1.1 | 146 | | 134 | 异步信号处理函数产生信号 | 7.14.1.1 | 147 | | 135 | 在多线程环境中使用 signal 函数 | 7.14.1.1 | 148 | | 136 | 没有正确地通过 va\_list 访问可变参数,或在使用 va\_start 之前访问可变参数 [\[注\]](#comment_136) | 7.16 | 149 | | 137 | va\_arg 的参数 ap 被传给一个函数,而在这个函数中又使用 va\_arg 处理 ap | 7.16 | 150 | | 138 | va\_start、va\_arg、va\_copy、va\_end 的宏定义被抑制,或者程序定义了与 va\_copy 或 va\_end 相同的外部名称 | 7.16.1 | 151 | | 139 | va\_start 或 va\_copy 没有正确地与 va\_end 配合使用 [\[注\]](#comment_139) | 7.16.1 | 152 | | 140 | 在 va\_arg 的类型参数名称后加 \* 号不能表示指针类型 | 7.16.1.1 | 153 | | 141 | 没有实际的下一个参数时使用 va\_arg 宏,或者下一个参数的类型与 va\_arg 声明的类型不兼容 | 7.16.1.1 | 154 | | 142 | 用 va\_copy 或 va\_start 重复初始化 va\_list,已被 va\_end 关闭的除外 [\[注\]](#comment_142) | 7.16.1.2 | 155 | | 143 | 可变参数列表中省略号的前一个参数为数组、函数,或具有寄存器存储期,以及与默认参数提升后不兼容的类型 | 7.16.1.4 | 156 | | 144 | offset 的第二个参数不是有效的 . 运算符右子表达式,或者是位域 | 7.19 | 157 | | 145 | integer\-constant 宏(如 INT32\_C、UINT64\_C 等)的参数不是合法的十进制、八进制或十六进制常量,或者参数的值超出相应类型的取值范围 | 7.20.4 | 158 | | 146 | 对面向宽字符的流使用面向字节的输入输出函数,或对面向字节的流使用面向宽字符的输入输出函数 | 7.21.2 | 159 | | 147 | 写入的宽字符覆盖了多节字字符的一部分,而后面的内容被使用 | 7.21.2 | 160 | | 148 | 文件对象在关闭后仍被使用 | 7.21.3 | 161 | | 149 | 对输入流使用 fflush 函数,或对更新流在执行输入操作后使用 fflush 函数 | 7.21.5.2 | 162 | | 150 | fopen 的 mode 参数字符顺序不符合要求 | 7.21.5.3 | 163 | | 151 | 写入流后未调用 fflush 也未调用文件定位函数便读取流,或者读取流后未调用定位函数便写入流 | 7.21.5.3 | 164 | | 152 | 程序显式访问提供给 setvbuf 的数组 | 7.21.5.6 | 165 | | 153 | 提供给格式化函数的参数数量不足,或者参数的类型不符合要求 [\[注\]](#comment_153) | 7.21.6.1 | 166 | | 154 | 与格式化函数 format 参数对应的不是符合要求的多字符序列 [\[注\]](#comment_154) | 7.21.6.1 | 167 | | 155 | 对 c、p、n、% 等无精度要求的占位符设定精度 [\[注\]](#comment_155) | 7.21.6.1 | 168 | | 156 | 设置了可变宽度或精度的格式化占位符,但并未提供相应的参数 [\[注\]](#comment_156) | 7.21.6.1 | 169 | | 157 | 用 \# 修饰 o、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符,用 0 修饰 d、i、o、u、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符 [\[注\]](#comment_157) | 7.21.6.1 | 170 | | 158 | 用 length modifier 修饰 d、i、o、u、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符 [\[注\]](#comment_158) | 7.21.6.1 | 171 | | 159 | 格式化输出占位符 s 对应的字符串参数不是以空字符结尾,除非对占位符 s 声明不需要空字符结尾的精度 [\[注\]](#comment_159) | 7.21.6.1 | 172 | | 160 | 对格式化占位符 n 声明任何宽度、精度、标志位、赋值抑制字符 [\[注\]](#comment_160) | 7.21.6.1 | 173 | | 161 | 格式化占位符 % 不符合 %% 这种格式 [\[注\]](#comment_161) | 7.21.6.1 | 174 | | 162 | 使用无效的格式化占位符 [\[注\]](#comment_162) | 7.21.6.1 | 175 | | 163 | 格式化输出函数参数过多,数量超过 INT\_MAX [\[注\]](#comment_163) | 7.21.6.1 | 176 | | 164 | 格式化输入函数参数过多,数量超过 INT\_MAX [\[注\]](#comment_164) | 7.21.6.2 | 177 | | 165 | 格式化占位符与对应的参数类型不匹配 [\[注\]](#comment_165) | 7.21.6.2 | 178 | | 166 | 格式化输入占位符 c、s、\[ 对应的参数没有足够的空间容纳输入的数据 [\[注\]](#comment_166) | 7.21.6.2 | 179 | | 167 | 由 l 修饰的格式化输入占位符 c、s、\[ 对应的输入不是符合要求的多字符序列 [\[注\]](#comment_167) | 7.21.6.2 | 180 | | 168 | 格式化输入占位符 %p 对应的参数不是同一个进程中的指针 [\[注\]](#comment_168) | 7.21.6.2 | 181 | | 169 | 未正确初始化的 va\_list 作为 vfprintf、vscanf 等函数的参数,或者在函数返回后继续使用该参数 [\[注\]](#comment_169) | 7.21.6.8 | 182 | | 170 | 在 fgets 或 fgetws 失败后继续使用传入的数组 [\[注\]](#comment_170) | 7.21.7.2 | 183 | | 171 | 在调用 ungetc 函数后使用二进制流的 file position indicator,而在调用之前其值为 0 | 7.21.7.10 | 184 | | 172 | 在 fread 或 fwrite 失败后使用 file position indicator [\[注\]](#comment_172) | 7.21.8.1 | 185 | | 173 | 使用由 fread 读取的不完整元素 | 7.21.8.1 | 186 | | 174 | fseek 没有正确地与 ftell 配合使用 | 7.21.9.2 | 187 | | 175 | fsetpos 没有正确地与 fgetpos 配合使用 | 7.21.9.3 | 188 | | 176 | 指针指向长度为 0 的内存空间,并通过该指针访问对象 | 7.22.3 | 189 | | 177 | 指针指向的空间被释放后,该指针的值仍被使用 | 7.22.3 | 190 | | 178 | aligned\_alloc 要求的对齐方式当前实现不支持,或申请的长度不是对齐方式的整数倍 | 7.22.3.1 | 191 | | 179 | free 或 realloc 的参数不是由匹配的分配函数返回,或是已被释放的地址 [\[注\]](#comment_179) | 7.22.3.3 | 192 | | 180 | 使用由 malloc 分配的且未初始化的数据 | 7.22.3.4 | 193 | | 181 | 使用由 realloc 分配的超出原对象大小且未初始化的数据 | 7.22.3.5 | 194 | | 182 | 调用 exit 或 quick\_exit 超过一次,或这两个函数都被调用 [\[注\]](#comment_182) | 7.22.4.4 | 195 | | 183 | 调用由 atexit 或 at\_quick\_exit 注册的函数时,调用 longjmp 结束了被注册函数的执行 [\[注\]](#comment_183) | 7.22.4.4 | 196 | | 184 | 程序修改了由 getenv 或 strerror 引入的字符串 [\[注\]](#comment_184) | 7.22.4.6 | 197 | | 185 | 在 quick\_exit 函数执行时产生了信号 | 7.22.4.7 | 198 | | 186 | 用 system 执行可以造成终止的命令,或命令包含未定义的行为 | 7.22.4.8 | 199 | | 187 | 无效指针或空数组作为搜索或排序库函数的参数 | 7.22.5 | 200 | | 188 | 供搜索或排序库函数调用的比较函数修改了数组元素,或返回不一致的结果 | 7.22.5 | 201 | | 189 | 供 bsearch 搜索的数组,其元素未按正确的顺序排列 | 7.22.5.1 | 202 | | 190 | 更改了 LC\_CTYPE 类别后仍使用之前获取的 mbstate\_t 等对象 | 7.22.7 | 203 | | 191 | 字符串或宽字符串库函数越界访问相关对象 [\[注\]](#comment_191) | 7.24.1 | 204 | | 192 | 无效指针或无效长度作为字符串或宽字符串库函数的参数 [\[注\]](#comment_192) | 7.24.1 | 205 | | 193 | strxfrm、strftime、wcsxfrm、wcsftime 的目标数组过小,无法存储以空字符结尾的完整结果 [\[注\]](#comment_193) | 7.24.4.5 | 206 | | 194 | 第一次调用 strtok 或 wcstok 时,第一个参数是空指针 [\[注\]](#comment_194) | 7.24.5.8 | 207 | | 195 | 为泛型宏提供的参数没有与其类型对应的函数 | 7.25 | 208 | | 196 | 为泛型宏提供 complex 参数,但没有与之对应的 complex 函数 | 7.25 | 209 | | 197 | 传递给 asctime 的参数包含超过正常范围的值,或年份超过 4 位数或小于 1000 | 7.27.3.1 | 210 | | 198 | 调用 fwprintf 函数时,如果 s 占位符没有用 l 修饰,与之对应的参数不是有效的多字节字符序列 | 7.29.2.11 | 211 | | 199 | 传递给 wcstok 的 ptr 参数不是来自针对同一个字符串的上一次 wcstok 调用 | 7.29.4.5.7 | 212 | | 200 | 不正确地使用 mbstate\_t 对象 | 7.29.6 | 213 | | 201 | 传递给宽字符分类或映射函数的 wint\_t 参数,其值即不是 WEOF 也不能用 wchar\_t 表示 | 7.30.1 | 214 | | 202 | iswctype 配合 wctype 使用时,作用于这两个函数的 LC\_CTYPE 类别不一致 | 7.30.2.2.1 | 215 | | 203 | towctrans 配合 wctrans 使用时,作用于这两个函数的 LC\_CTYPE 类别不一致 | 7.30.3.2.1 | 216 | 217 |
218 | 219 | ## 注释 220 | 221 | ### 11. 使用未初始化或已被销毁的具有自动存储期的对象 222 | 223 | 此问题在 6.2.4、6.7.9、6.8 中均有涉及。 224 |
225 |
226 |
227 | 228 | ### 112. 在不适当的场合进行与 CX\_LIMITED\_RANGE、FENV\_ACCESS、FP\_CONTRACT 相关的预处理设置 229 | 230 | 此问题在 7.3.4、7.6.1、7.12.2 中均有涉及。 231 |
232 |
233 |
234 | 235 | ### 118. fesetenv 没有正确地与 fegetenv 配合使用,feupdateenv 没有正确地与 feholdexcept 配合使用 236 | 237 | 此问题在 7.6.4.3、7.6.4.4 中均有涉及。 238 |
239 |
240 |
241 | 242 | ### 119. 整数运算或转换函数的结果不能被有效表示 243 | 244 | 此问题在 7.8.2.1、7.8.2.2、7.8.2.3、7.8.2.4、7.22.6.1、7.22.6.2、7.22.1 中均有涉及。 245 |
246 |
247 |
248 | 249 | ### 123. 为浮点数分类或比较相关的宏提供非真浮点类型的参数 250 | 251 | 此问题在 7.12.3、7.12.14 中均有涉及。 252 |
253 |
254 |
255 | 256 | ### 132. 处理程序外部产生的信号时引用非 lock\-free 并具有静态或线程存储期的对象,且这种对象不是 volatile sig\_atomic\_t 类型,相关操作也不是赋值,或者使用 abort、\_Exit、quick\_exit、signal 之外的库函数 257 | 258 | 程序外部产生的信号指不是 abort 也不是 raise 函数产生的信号。 259 |
260 |
261 |
262 | 263 | ### 133. 处理程序外部产生的信号时调用 signal 函数,当其返回 SIG\_ERR 时访问 errno 的值 264 | 265 | 程序外部产生的信号指不是 abort 也不是 raise 函数产生的信号。 266 |
267 |
268 |
269 | 270 | ### 136. 没有正确地通过 va\_list 访问可变参数,或在使用 va\_start 之前访问可变参数 271 | 272 | 此问题在 7.16、7.16.1.1、7.16.1.4 中均有涉及。 273 |
274 |
275 |
276 | 277 | ### 139. va\_start 或 va\_copy 没有正确地与 va\_end 配合使用 278 | 279 | 此问题在 7.16.1、7.16.1.2、7.16.1.3、7.16.1.4 中均有涉及。 280 |
281 |
282 |
283 | 284 | ### 142. 用 va\_copy 或 va\_start 重复初始化 va\_list,已被 va\_end 关闭的除外 285 | 286 | 此问题在 7.16.1.2、7.16.1.4 中均有涉及。 287 |
288 |
289 |
290 | 291 | ### 153. 提供给格式化函数的参数数量不足,或者参数的类型不符合要求 292 | 293 | 此问题在 7.21.6.1、7.21.6.2、7.29.2.1、7.29.2.2 中均有涉及。 294 |
295 |
296 |
297 | 298 | ### 154. 与格式化函数 format 参数对应的不是符合要求的多字符序列 299 | 300 | 此问题在 7.21.6.1、7.21.6.2、7.27.3.5、7.29.2.1、7.29.2.2、7.29.5.1 中均有涉及。 301 |
302 |
303 |
304 | 305 | ### 155. 对 c、p、n、% 等无精度要求的占位符设定精度 306 | 307 | 此问题在 7.21.6.1、7.29.2.1 中均有涉及。 308 |
309 |
310 |
311 | 312 | ### 156. 设置了可变宽度或精度的格式化占位符,但并未提供相应的参数 313 | 314 | 此问题在 7.21.6.1、7.29.2.1 中均有涉及。 315 |
316 |
317 |
318 | 319 | ### 157. 用 \# 修饰 o、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符,用 0 修饰 d、i、o、u、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符 320 | 321 | 此问题在 7.21.6.1、7.29.2.1 中均有涉及。 322 |
323 |
324 |
325 | 326 | ### 158. 用 length modifier 修饰 d、i、o、u、x、X、a、A、e、E、f、F、g、G 之外的格式化占位符 327 | 328 | 此问题在 7.21.6.1、7.21.6.2、7.29.2.1、7.29.2.2 中均有涉及。 329 |
330 |
331 |
332 | 333 | ### 159. 格式化输出占位符 s 对应的字符串参数不是以空字符结尾,除非对占位符 s 声明不需要空字符结尾的精度 334 | 335 | 此问题在 7.21.6.1、7.29.2.1 中均有涉及。 336 |
337 |
338 |
339 | 340 | ### 160. 对格式化占位符 n 声明任何宽度、精度、标志位、赋值抑制字符 341 | 342 | 此问题在 7.21.6.1、7.21.6.2、7.29.2.1、7.29.2.2 中均有涉及。 343 |
344 |
345 |
346 | 347 | ### 161. 格式化占位符 % 不符合 %% 这种格式 348 | 349 | 此问题在 7.21.6.1、7.21.6.2、7.29.2.1、7.29.2.2 中均有涉及。 350 |
351 |
352 |
353 | 354 | ### 162. 使用无效的格式化占位符 355 | 356 | 此问题在 7.21.6.1、7.21.6.2、7.27.3.5、7.29.2.1、7.29.2.2、7.29.5.1 中均有涉及。 357 |
358 |
359 |
360 | 361 | ### 163. 格式化输出函数参数过多,数量超过 INT\_MAX 362 | 363 | 此问题在 7.21.6.1、7.29.2.1 中均有涉及。 364 |
365 |
366 |
367 | 368 | ### 164. 格式化输入函数参数过多,数量超过 INT\_MAX 369 | 370 | 此问题在 7.21.6.2、7.29.2.2 中均有涉及。 371 |
372 |
373 |
374 | 375 | ### 165. 格式化占位符与对应的参数类型不匹配 376 | 377 | 此问题在 7.21.6.2、7.29.2.2 中均有涉及。 378 |
379 |
380 |
381 | 382 | ### 166. 格式化输入占位符 c、s、\[ 对应的参数没有足够的空间容纳输入的数据 383 | 384 | 此问题在 7.21.6.2、7.29.2.2 中均有涉及。 385 |
386 |
387 |
388 | 389 | ### 167. 由 l 修饰的格式化输入占位符 c、s、\[ 对应的输入不是符合要求的多字符序列 390 | 391 | 此问题在 7.21.6.2、7.29.2.2 中均有涉及。 392 |
393 |
394 |
395 | 396 | ### 168. 格式化输入占位符 %p 对应的参数不是同一个进程中的指针 397 | 398 | 此问题在 7.21.6.2、7.29.2.2 中均有涉及。 399 |
400 |
401 |
402 | 403 | ### 169. 未正确初始化的 va\_list 作为 vfprintf、vscanf 等函数的参数,或者在函数返回后继续使用该参数 404 | 405 | vfprintf、vfscanf、vprintf、vscanf、vsnprintf、vsprintf、vsscanf、vfwprintf、vfwscanf、vswprintf、vswscanf、vwprintf、vwscanf 等函数均面对同样的问题。 406 | 407 | 此问题在 7.21.6.8、7.21.6.9、7.21.6.10、7.21.6.11、7.21.6.12、7.21.6.13、7.21.6.14、7.29.2.5、7.29.2.6、7.29.2.7、7.29.2.8、7.29.2.9、7.29.2.10 中均有涉及。 408 |
409 |
410 |
411 | 412 | ### 170. 在 fgets 或 fgetws 失败后继续使用传入的数组 413 | 414 | 此问题在 7.21.7.2、7.29.3.2 中均有涉及。 415 |
416 |
417 |
418 | 419 | ### 172. 在 fread 或 fwrite 失败后使用 file position indicator 420 | 421 | 此问题在 7.21.8.1、7.21.8.2 中均有涉及。 422 |
423 |
424 |
425 | 426 | ### 179. free 或 realloc 的参数不是由匹配的分配函数返回,或是已被释放的地址 427 | 428 | 此问题在 7.22.3.3、7.22.3.5 中均有涉及。 429 |
430 |
431 |
432 | 433 | ### 182. 调用 exit 或 quick\_exit 超过一次,或这两个函数都被调用 434 | 435 | 此问题在 7.22.4.4、7.22.4.7 中均有涉及。 436 |
437 |
438 |
439 | 440 | ### 183. 调用由 atexit 或 at\_quick\_exit 注册的函数时,调用 longjmp 结束了被注册函数的执行 441 | 442 | 此问题在 7.22.4.4、7.22.4.7 中均有涉及。 443 |
444 |
445 |
446 | 447 | ### 184. 程序修改了由 getenv 或 strerror 引入的字符串 448 | 449 | 此问题在 7.22.4.6、7.24.6.2 中均有涉及。 450 |
451 |
452 |
453 | 454 | ### 191. 字符串或宽字符串库函数越界访问相关对象 455 | 456 | 此问题在 7.24.1、7.29.4 中均有涉及。 457 |
458 |
459 |
460 | 461 | ### 192. 无效指针或无效长度作为字符串或宽字符串库函数的参数 462 | 463 | 此问题在 7.24.1、7.29.4 中均有涉及。 464 |
465 |
466 |
467 | 468 | ### 193. strxfrm、strftime、wcsxfrm、wcsftime 的目标数组过小,无法存储以空字符结尾的完整结果 469 | 470 | 此问题在 7.24.4.5、7.27.3.5、7.29.4.4.4、7.29.5.1 中均有涉及。 471 |
472 |
473 |
474 | 475 | ### 194. 第一次调用 strtok 或 wcstok 时,第一个参数是空指针 476 | 477 | 此问题在 7.24.5.8、7.29.4.5.7 中均有涉及。 478 |
479 |
480 |
481 | 482 | 483 | 484 |
485 |
486 | -------------------------------------------------------------------------------- /cpp-ub-list.md: -------------------------------------------------------------------------------- 1 | # 附录:C++ 未定义行为成因列表 ![Version](https://img.shields.io/badge/version-1.4.0-brightgreen) 2 | 3 | > cppreference.com: "*Undefined behavior renders the entire program meaningless if certain rules of the language are violated.*" 4 | 5 |   未定义的行为(Undefined Behavior),指程序不可预测的执行效果,一般由错误的代码实现引起。出于效率、兼容性等多方面原因,语言标准不便于定义错误程序的明确行为,而是将其统称为“未定义”的行为,可以是崩溃,也可以是非预期的任意表现,有些问题在编译器和执行环境的特殊处理下也可能不会造成实质性的危害,但不具备可移植性。代码质量管理的一个重要目标就是消除会导致未定义行为的代码。 6 | 7 |   C\+\+ 标准声明了导致未定义行为的情况,本文梳理了 ISO/IEC 14882:2003 和 ISO/IEC 14882:2011 前 18 章的相关内容,后 12 章的主题为标准库实现,相关内容的主旨在前 18 章中已有体现,C 语言相关内容可参见《[C 未定义行为成因列表](https://github.com/Qihoo360/safe-rules/blob/main/c-ub-list.md)》。 8 | 9 |
10 | 11 | | ID | Circumstance | Provenance | 12 | | :----: | :---- | :---- | 13 | | 1 | [代码行以反斜杠结尾,且与下一行连接后生成通用字符名称](#_1) | [`11-2.2(2)`](#_1) | 14 | | 2 | [非空源文件未以换行符结尾,或以换行符结尾但换行符之前是反斜杠](#_2) | [`03-2.1(2)`](#_2) | 15 | | 3 | [连接预处理符号时产生了通用字符名称](#_3) | [`11-2.2(4)`](#_3) | 16 | | 4 | [存在不符合词法的单引号或双引号](#_4) | [`11-2.5(2)`](#_4) | 17 | | 5 | [在 \#include 指令中,'、"、\\、//、/\* 出现在定界符 < > 之间,或 '、\\、//、/\* 出现在定界符 " 之间](#_5) | [`03-2.8(2)`](#_5) | 18 | | 6 | [无后缀 10 进制整数字面常量超过 long int 取值范围](#_6) | [`03-2.13.1(2)`](#_6) | 19 | | 7 | [使用非标准转义字符](#_7) | [`03-2.13.2(3)`](#_7) | 20 | | 8 | [修改字符串字面常量](#_8) | [`11-2.14.5(12)`](#_8) | 21 | | 9 | [窄字符串与宽字符串连接](#_9) | [`03-2.13.4(3)`](#_9) | 22 | | 10 | [违反 One Definition Rule](#_10) | [`11-3.2(5)`](#_10) | 23 | | 11 | [具有静态或线程存储期的对象在析构函数中调用 std::exit 函数](#_11) | [`11-3.6.1(4)`](#_11) | 24 | | 12 | [函数内具有静态或线程存储期的对象已析构,之后该函数又被调用并引用到已析构的对象](#_12) | [`11-3.6.3(2)`](#_12) | 25 | | 13 | [在对象析构之后使用对象](#_13) | [`11-3.6.3(4)`](#_13) | 26 | | 14 | [指针指向长度为 0 的内存空间并被解引用](#_14) | [`11-3.7.4.1(2)`](#_14) | 27 | | 15 | [内存回收函数抛出异常](#_15) | [`11-3.7.4.2(3)`](#_15) | 28 | | 16 | [使用不匹配的方法分配回收资源](#_16) | [`11-3.7.4.2(3)`](#_16) | 29 | | 17 | [使用已被释放的指针](#_17) | [`11-3.7.4.2(4)`](#_17) | 30 | | 18 | [对象生命周期已结束,但未调用其有副作用的析构函数](#_18) | [`11-3.8(4)`](#_18) | 31 | | 19 | [在分配空间后,生命周期开始前,或在生命周期结束后,回收空间前,通过指针访问对象](#_19) | [`11-3.8(5)`](#_19) | 32 | | 20 | [在分配空间后,生命周期开始前,或在生命周期结束后,回收空间前,通过 glvalue 访问对象](#_20) | [`11-3.8(6)`](#_20) | 33 | | 21 | [具有静态、线程或自动存储期和 non\-trivial 析构函数的对象,其空间被非兼容类型的对象占据](#_21) | [`11-3.8(8)`](#_21) | 34 | | 22 | [常量对象的空间或曾属于常量对象的空间被其他对象占据](#_22) | [`11-3.8(9)`](#_22) | 35 | | 23 | [通过 glvalue 访问对象,但 glvalue 的类型不符合要求](#_23) | [`11-3.10(10)`](#_23) | 36 | | 24 | [通过 glvalue 引用不相关类型的对象或未初始化的对象](#_24) | [`11-4.1(1)`](#_24) | 37 | | 25 | [浮点类型转换产生的结果无法在相应的空间中表示](#_25) | [`11-4.8(1)`](#_25) | 38 | | 26 | [浮点类型转为整数类型时,整数类型无法存储浮点类型的整数部分](#_26) | [`11-4.9(1)`](#_26) | 39 | | 27 | [整数类型转为浮点类型时,浮点类型无法存储整数的值](#_27) | [`11-4.9(2)`](#_27) | 40 | | 28 | [表达式求值依赖无确定顺序的副作用](#_28) | [`03-5(4)`](#_28) | 41 | | 29 | [表达式的结果在数学上没有定义](#_29) | [`11-5(4)`](#_29) | 42 | | 30 | [被调用函数的语言链接性与该函数定义的语言链接性不符](#_30) | [`11-5.2.2(1)`](#_30) | 43 | | 31 | [将非 POD 对象传入可变参数列表](#_31) | [`03-5.2.2(7)`](#_31) | 44 | | 32 | [用 static\_cast 将基类引用转为派生类引用,基类为虚基类,或引用的实际对象并非派生类对象](#_32) | [`11-5.2.9(2)`](#_32) | 45 | | 33 | [用 static\_cast 将基类指针转为派生类指针,基类为虚基类,或指向的实际对象并非派生类对象](#_33) | [`11-5.2.9(11)`](#_33) | 46 | | 34 | [用 static\_cast 将成员指针转为基类成员指针时,基类中没有相关成员](#_34) | [`11-5.2.9(12)`](#_34) | 47 | | 35 | [函数指针被转为不兼容的类型并执行](#_35) | [`11-5.2.10(6)`](#_35) | 48 | | 36 | [类型转换时去掉对象 const 属性并修改对象](#_36) | [`11-5.2.11(7)`](#_36) | 49 | | 37 | [对不完整类型的对象取地址,但该对象的完整类型重载了 operator &](#_37) | [`11-5.3.1(5)`](#_37) | 50 | | 38 | [new 运算符第一个数组维度的参数为负数](#_38) | [`03-5.3.4(6)`](#_38) | 51 | | 39 | [用 delete 释放数组漏写中括号,用 delete 释放对象多写中括号,用 delete 释放非 new 表达式的结果](#_39) | [`11-5.3.5(2)`](#_39) | 52 | | 40 | [被 delete 释放的对象或数组类型不符合要求](#_40) | [`11-5.3.5(3)`](#_40) | 53 | | 41 | [用 delete 释放不完整类型的对象,但在对象完整类型声明中有 non\-trivial 析构函数](#_41) | [`11-5.3.5(5)`](#_41) | 54 | | 42 | [对象解引用成员指针时,对象的动态类型不包含成员指针引用的成员](#_42) | [`11-5.5(4)`](#_42) | 55 | | 43 | [对象解引用成员指针时,成员指针为空指针](#_43) | [`11-5.5(6)`](#_43) | 56 | | 44 | [/ 或 % 运算符第二个操作数的值为 0](#_44) | [`11-5.6(4)`](#_44) | 57 | | 45 | [指针加减整数,结果超出了指针指向数组的地址范围,使指针的值溢出](#_45) | [`11-5.7(5)`](#_45) | 58 | | 46 | [未指向同一数组的指针相减](#_46) | [`11-5.7(5)`](#_46) | 59 | | 47 | [移位运算符右操作数为负数或超过相关类型比特位的数量](#_47) | [`11-5.8(1)`](#_47) | 60 | | 48 | [对有符号整数进行超出取值范围的左移运算](#_48) | [`11-5.8(2)`](#_48) | 61 | | 49 | [将对象的值赋给具有部分重叠区域的另一个对象](#_49) | [`11-5.17(8)`](#_49) | 62 | | 50 | [有返回值的函数没有通过 return 语句返回](#_50) | [`11-6.6.3(2)`](#_50) | 63 | | 51 | [递归地定义和初始化静态对象](#_51) | [`11-6.7(4)`](#_51) | 64 | | 52 | [修改非 mutable 常量对象](#_52) | [`11-7.1.6.1(4)`](#_52) | 65 | | 53 | [使用没有 volatile 限定的 glvalue 访问有 volatile 限定的对象](#_53) | [`11-7.1.6.1(6)`](#_53) | 66 | | 54 | [具有 noreturn 属性的函数返回至调用方](#_54) | [`11-7.6.3(2)`](#_54) | 67 | | 55 | [空指针解引用](#_55) | [`11-8.3.2(5)`](#_55) | 68 | | 56 | [对象的实际类型与当前静态类型不相关,并调用其非静态成员函数](#_56) | [`11-9.3.1(2)`](#_56) | 69 | | 57 | [在构造函数或析构函数中调用纯虚函数](#_57) | [`11-10.4(6)`](#_57) | 70 | | 58 | [对象的实际类型与当前静态类型不相关,并调用其析构函数](#_58) | [`11-12.4(13)`](#_58) | 71 | | 59 | [在对象生命周期结束后调用其析构函数](#_59) | [`11-12.4(15)`](#_59) | 72 | | 60 | [基类对象构造完毕之前调用成员函数](#_60) | [`11-12.6.2(13)`](#_60) | 73 | | 61 | [对成员或基类对象的不合理引用](#_61) | [`11-12.7(1)`](#_61) | 74 | | 62 | [将对象指针转为其基类对象的指针时,基类对象尚未开始构造或已结束析构](#_62) | [`11-12.7(3)`](#_62) | 75 | | 63 | [正在构造或析构的对象通过 . 或 \-> 调用虚函数,而且该对象与当前构造或析构函数不属于同一个类或基类](#_63) | [`11-12.7(4)`](#_63) | 76 | | 64 | [typeid 作用于正在构造或析构的对象,而且该对象与当前构造或析构函数不属于同一个类或基类](#_64) | [`11-12.7(5)`](#_64) | 77 | | 65 | [dynamic\_cast 作用于正在构造或析构的对象,而且该对象与当前构造或析构函数不属于同一个类或基类](#_65) | [`11-12.7(6)`](#_65) | 78 | | 66 | [对模板函数进行非良构调用,或在模板定义和实例化上下文之外有更好的候选函数匹配](#_66) | [`11-14.6.4.2(1)`](#_66) | 79 | | 67 | [需要无限递归的模版实例化](#_67) | [`11-14.7.1(15)`](#_67) | 80 | | 68 | [构造或析构函数在 function\-try\-block 的 handler 中访问非静态成员](#_68) | [`11-15.2(10)`](#_68) | 81 | | 69 | [有返回值的函数在 function\-try\-block 的 handler 中没有正确返回](#_69) | [`11-15.3(15)`](#_69) | 82 | | 70 | [在 \#if、 \#elif 的条件中,由宏展开产生了 defined 表达式,或 defined 表达式格式不正确](#_70) | [`11-16.1(4)`](#_70) | 83 | | 71 | [\#include 指令经宏展开后不满足标准格式](#_71) | [`11-16.2(4)`](#_71) | 84 | | 72 | [宏的实参列表中出现预处理指令](#_72) | [`11-16.3(11)`](#_72) | 85 | | 73 | [预处理运算符 \# 的结果不是有效的字符串](#_73) | [`11-16.3.2(2)`](#_73) | 86 | | 74 | [预处理运算符 \#\# 的结果不是有效的符号](#_74) | [`11-16.3.3(3)`](#_74) | 87 | | 75 | [\#line 指定的行号为 0 或大于规定值](#_75) | [`11-16.4(3)`](#_75) | 88 | | 76 | [\#line 指令不符合标准格式](#_76) | [`11-16.4(5)`](#_76) | 89 | | 77 | [用 \#define 定义或用 \#undef 取消定义具有保留意义的名称](#_77) | [`11-16.8(4)`](#_77) | 90 | | 78 | [供语言机制调用的函数不符合要求](#_78) | [`11-17.3.21`](#_78) | 91 | | 79 | [程序实现了应由标准库提供的功能](#_79) | [`11-17.3.22`](#_79) | 92 | | 80 | [未经许可,向 std 命名空间添加声明或定义](#_80) | [`11-17.6.4.2.1(1)`](#_80) | 93 | | 81 | [对标准库,特化模板类成员函数,特化模板类成员模板函数,特化、偏特化成员类模版](#_81) | [`11-17.6.4.2.1(2)`](#_81) | 94 | | 82 | [未经许可,向 posix 命名空间添加声明或定义](#_82) | [`11-17.6.4.2.2(1)`](#_82) | 95 | | 83 | [声明或定义标准库保留名称](#_83) | [`11-17.6.4.3(2)`](#_83) | 96 | | 84 | [编译器未提供标准头文件,但编译时引入了非标准同名头文件](#_84) | [`11-17.6.4.4(1)`](#_84) | 97 | | 85 | [为标准库函数提供不符合要求的参数](#_85) | [`11-17.6.4.9(1)`](#_85) | 98 | | 86 | [多线程调用标准库函数造成数据竞争](#_86) | [`11-17.6.4.10(1)`](#_86) | 99 | | 87 | [违反标准库函数要求的前置条件,除非标准库函数声明了这种情况会抛出异常](#_87) | [`11-17.6.4.11(1)`](#_87) | 100 | | 88 | [offsetof 用于非 standard layout 类型,或用于计算静态成员以及成员函数的偏移量](#_88) | [`11-18.2(4)`](#_88) | 101 | | 89 | [可变参数列表中省略号的前一个形式参数为引用、数组、函数,或具有与默认参数提升后不兼容的类型](#_89) | [`11-18.10(3)`](#_89) | 102 | | 90 | [longjmp 跳转使应被执行的析构函数未被执行](#_90) | [`11-18.10(4)`](#_90) | 103 | 104 |
105 |
106 |
107 | 108 | ### 1. 代码行以反斜杠结尾,且与下一行连接后生成通用字符名称 109 |
110 | 111 | 如果代码行以反斜杠结尾,预处理器会删除行尾的反斜杠和换行符,使其与下一行连接,如果连接后产生了以 \\u 或 \\U 开头的通用字符名称(universal character name)会导致未定义的行为。 112 | 113 | 示例: 114 | ``` 115 | auto s = "\u54\ 116 | 0D"; // Undefined behavior 117 | 118 | auto \u54\ 119 | 0D = 'a'; // Undefined behavior 120 | ``` 121 | 这种代码可能不会通过编译,也可能不会造成实际问题,但一定不具备可移植性。 122 |
123 |
124 | 125 | #### 依据 126 | ISO/IEC 14882:2003 2.1(2)-undefined 127 | ISO/IEC 14882:2011 2.2(2)-undefined 128 |
129 | 130 | #### 规则 131 | [ID_badBackslash](./c-cpp-rules.md#badbackslash) 132 |
133 | 134 |
135 |
136 | 137 | ### 2. 非空源文件未以换行符结尾,或以换行符结尾但换行符之前是反斜杠 138 |
139 | 140 | 示例: 141 | ``` 142 | namespace NS { 143 | .... 144 | }\ // Undefined behavior if this is the last line 145 | ``` 146 | C\+\+03 声明了这种情况会导致未定义的行为,C\+\+11 规定在这种情况下编译器应补全所需的空行。 147 |
148 |
149 | 150 | #### 依据 151 | ISO/IEC 14882:2003 2.1(2)-undefined 152 |
153 | 154 | #### 规则 155 | [ID_missingNewLineFileEnd](./c-cpp-rules.md#missingnewlinefileend) 156 | [ID_badBackslash](./c-cpp-rules.md#badbackslash) 157 |
158 | 159 |
160 |
161 | 162 | ### 3. 连接预处理符号时产生了通用字符名称 163 |
164 | 165 | 预处理运算符 \#\# 可以将两个符号连接成一个符号,如果产生了以 \\u 或 \\U 开头的通用字符名称会导致未定义的行为。 166 | 167 | 示例: 168 | ``` 169 | #define M(a, b) a ## b 170 | 171 | int M(\u54, 0d) = 123; // Undefined behavior 172 | ``` 173 |
174 |
175 | 176 | #### 依据 177 | ISO/IEC 14882:2003 2.1(4)-undefined 178 | ISO/IEC 14882:2011 2.2(4)-undefined 179 |
180 | 181 |
182 |
183 | 184 | ### 4. 存在不符合词法的单引号或双引号 185 |
186 | 187 | 预处理器连接以反斜杠结尾的各行代码后将其转为预处理符号序列,在处理指令和展开宏之前,如果出现了不符合词法的单引号或双引号,会导致未定义的行为。 188 | 189 | 示例: 190 | ``` 191 | #defined X ' // Undefined behavior 192 | #defined Y " // Undefined behavior 193 | ``` 194 | 例中的引号无法与其他字符组成预处理符号,可能不会通过编译,也可能产生非预期的结果。 195 |
196 |
197 | 198 | #### 依据 199 | ISO/IEC 14882:2003 2.4(2)-undefined 200 | ISO/IEC 14882:2011 2.5(2)-undefined 201 |
202 | 203 |
204 |
205 | 206 | ### 5. 在 \#include 指令中,'、"、\\、//、/\* 出现在定界符 < > 之间,或 '、\\、//、/\* 出现在定界符 " 之间 207 |
208 | 209 | 示例: 210 | ``` 211 | #include <"foo"> // Undefined behavior in C++03 212 | #include "foo//bar" // Undefined behavior in C++03 213 | ``` 214 | 程序在这种情况下的行为在 C\+\+03 中是未定义的,在 C\+\+11 中是由实现定义的。 215 |
216 |
217 | 218 | #### 依据 219 | ISO/IEC 14882:2003 2.8(2)-undefined 220 | ISO/IEC 14882:2011 2.9(2)-implementation 221 |
222 | 223 | #### 规则 224 | [ID_nonStandardCharInHeaderName](./c-cpp-rules.md#nonstandardcharinheadername) 225 | [ID_forbidBackslashInHeaderName](./c-cpp-rules.md#forbidbackslashinheadername) 226 |
227 | 228 |
229 |
230 | 231 | ### 6. 无后缀 10 进制整数字面常量超过 long int 取值范围 232 |
233 | 234 | 示例: 235 | ``` 236 | cout << 2147483648; // Undefined behavior in C++03 237 | ``` 238 | 如果 long int 为 32 位有符号整数类型,字面常量 2147483648 超出了范围,其行为在 C\+\+03 中是未定义的,在 C\+\+11 中则会将 2147483648 归为 unsigned long int 类型。 239 |
240 |
241 | 242 | #### 依据 243 | ISO/IEC 14882:2003 2.13.1(2)-undefined 244 |
245 | 246 |
247 |
248 | 249 | ### 7. 使用非标准转义字符 250 |
251 | 252 | 标准转义字符: 253 | ``` 254 | \a // Alert 255 | \b // Backspace 256 | \f // Formfeed page break 257 | \n // New line 258 | \r // Carriage return 259 | \t // Horizontal tab 260 | \v // Vertical tab 261 | \\ // Backslash 262 | \? // Question mark 263 | \' // Single quotation mark 264 | \" // Double quotation mark 265 | \0 // Null character 266 | \ddd // Any character, ‘d’ is an octal number 267 | \xhh // Any character, ‘h’ is a hex number 268 | ``` 269 | 反斜杠后如果出现其他形式的字符或字符序列会导致未定义的行为。 270 |
271 |
272 | 273 | #### 依据 274 | ISO/IEC 14882:2003 2.13.2(3)-undefined 275 | ISO/IEC 14882:2011 2.14.3(3)-implementation 276 |
277 | 278 | #### 规则 279 | [ID_literal_nonStandardEsc](./c-cpp-rules.md#literal_nonstandardesc) 280 |
281 | 282 |
283 |
284 | 285 | ### 8. 修改字符串字面常量 286 |
287 | 288 | 示例: 289 | ``` 290 | *((char*)"oops") = 'O'; // Undefined behavior, may crash 291 | cout << "oops"; // If it doesn't crash, this might output ‘Oops’ 292 | ``` 293 | 修改字面常量是一种逻辑错误。多数通用系统会在运行时保护常量数据,相关修改会造成崩溃,在没有这种保护机制的系统中,字符串常量可能会被修改,但也可能会影响到其他相同的字符串常量,因为相同的字符串常量可能共用相同的存储空间。 294 |
295 |
296 | 297 | #### 依据 298 | ISO/IEC 14882:2003 2.13.4(2)-undefined 299 | ISO/IEC 14882:2011 2.14.5(12)-undefined 300 |
301 | 302 | #### 规则 303 | [ID_constStrToNonConstPtr](./c-cpp-rules.md#conststrtononconstptr) 304 |
305 | 306 |
307 |
308 | 309 | ### 9. 窄字符串与宽字符串连接 310 |
311 | 312 | 示例: 313 | ``` 314 | auto* x = L"123" "456"; // Undefined in C++03 315 | auto* y = L"123" "456"; // A wide string in C++11 316 | ``` 317 | C\+\+03 规定宽字符串与窄字符串连接会导致未定义的行为。C\+\+11 规定一个字符串有前缀一个没有的话,结果以有前缀的为准,其他情况由实现定义。 318 |
319 |
320 | 321 | #### 依据 322 | ISO/IEC 14882:2003 2.13.4(3)-undefined 323 | ISO/IEC 14882:2011 2.14.5(13)-implementation 324 |
325 | 326 | #### 规则 327 | [ID_literal_hybridConcat](./c-cpp-rules.md#literal_hybridconcat) 328 |
329 | 330 |
331 |
332 | 333 | ### 10. 违反 One Definition Rule 334 |
335 | 336 | 任何翻译单元不得包含变量、函数、类型、枚举或模板的多个定义,即 One Definition Rule,违反此规则会导致未定义的行为。 337 | 338 | 示例: 339 | ``` 340 | // In a.cpp 341 | struct T { 342 | int i; 343 | }; 344 | 345 | // In b.cpp 346 | struct T { 347 | long i; // Undefined behavior, violates One Definition Rule 348 | }; 349 | ``` 350 |
351 |
352 | 353 | #### 依据 354 | ISO/IEC 14882:2003 3.2(5)-undefined 355 | ISO/IEC 14882:2011 3.2(5)-undefined 356 |
357 | 358 |
359 |
360 | 361 | ### 11. 具有静态或线程存储期的对象在析构函数中调用 std::exit 函数 362 |
363 | 364 | 程序调用 exit 函数后,全局、静态或 thread\_local 对象开始析构,而这种对象的析构函数再调用 exit 会导致未定义的行为。 365 | 366 | 示例: 367 | ``` 368 | class T { 369 | .... 370 | public: 371 | ~T() { 372 | std::exit(1); // Dangerous 373 | } 374 | }; 375 | 376 | static T obj; // Undefined behavior during destruct 377 | ``` 378 | 例中 obj 对象在析构时会导致未定义的行为。 379 |
380 |
381 | 382 | #### 依据 383 | ISO/IEC 14882:2003 3.6.1(4)-undefined 384 | ISO/IEC 14882:2011 3.6.1(4)-undefined 385 |
386 | 387 | #### 规则 388 | [ID_exitCallInDestructor](./c-cpp-rules.md#exitcallindestructor) 389 |
390 | 391 |
392 |
393 | 394 | ### 12. 函数内具有静态或线程存储期的对象已析构,之后该函数又被调用并引用到已析构的对象 395 |
396 | 397 | 示例: 398 | ``` 399 | // In T.cpp 400 | void foo() { 401 | static T sObj; 402 | .... 403 | } 404 | 405 | // In U.cpp 406 | class U { 407 | .... 408 | public: 409 | ~U() { 410 | foo(); // Undefined behavior if ‘sObj’ is destructed 411 | } 412 | }; 413 | 414 | U gObj; // Problematic 415 | ``` 416 | 当例中全局对象 gObj 析构时会调用 foo 函数,如果这时 foo 函数中的静态对象 sObj 已析构,会导致未定义的行为,gObj 与 sObj 的析构顺序在标准中是不确定的。 417 |
418 |
419 | 420 | #### 依据 421 | ISO/IEC 14882:2003 3.6.3(2)-undefined 422 | ISO/IEC 14882:2011 3.6.3(2)-undefined 423 |
424 | 425 |
426 |
427 | 428 | ### 13. 在对象析构之后使用对象 429 |
430 | 431 | 示例: 432 | ``` 433 | extern T obj; 434 | 435 | void foo() { 436 | obj.fun(); // Undefined behavior if ‘obj’ is destructed 437 | } 438 | ``` 439 | 当全局对象 obj 析构之后,再调用 foo 函数会导致未定义的行为(如在另一个全局对象的析构函数中调用 foo 函数)。 440 |
441 |
442 | 443 | #### 依据 444 | ISO/IEC 14882:2011 3.6.3(4)-undefined 445 |
446 | 447 | #### 规则 448 | [ID_danglingDeref](./c-cpp-rules.md#danglingderef) 449 |
450 | 451 |
452 |
453 | 454 | ### 14. 指针指向长度为 0 的内存空间并被解引用 455 |
456 | 457 | 示例: 458 | ``` 459 | void foo(size_t n) { 460 | int* p = new int[n]; 461 | p[0] = 123; // Undefined behavior if ‘n’ is zero 462 | .... 463 | delete[] p; 464 | } 465 | ``` 466 | 如果例中参数 n 为 0,对 p 的解引用会导致未定义的行为。 467 |
468 |
469 | 470 | #### 依据 471 | ISO/IEC 14882:2003 3.7.3.1(2)-undefined 472 | ISO/IEC 14882:2011 3.7.4.1(2)-undefined 473 |
474 | 475 | #### 规则 476 | [ID_bufferOverflow](./c-cpp-rules.md#bufferoverflow) 477 |
478 | 479 |
480 |
481 | 482 | ### 15. 内存回收函数抛出异常 483 |
484 | 485 | 示例: 486 | ``` 487 | class A { 488 | .... 489 | public: 490 | void operator delete(void* p) { 491 | if (!p) { 492 | throw Exception(); // Undefined behavior 493 | } 494 | .... 495 | } 496 | }; 497 | ``` 498 | 在 delete 运算符中抛出异常会导致未定义的行为。 499 |
500 |
501 | 502 | #### 依据 503 | ISO/IEC 14882:2011 3.7.4.2(3)-undefined 504 |
505 | 506 | #### 规则 507 | [ID_throwInDelete](./c-cpp-rules.md#throwindelete) 508 |
509 | 510 |
511 |
512 | 513 | ### 16. 使用不匹配的方法分配回收资源 514 |
515 | 516 | 示例: 517 | ``` 518 | T* p = new T; 519 | .... 520 | free(p); // Undefined behavior 521 | ``` 522 | 例中用 free 释放由 new 分配的内存空间,会导致未定义的行为。 523 |
524 |
525 | 526 | #### 依据 527 | ISO/IEC 14882:2011 3.7.4.2(3)-undefined 528 |
529 | 530 | #### 规则 531 | [ID_incompatibleDealloc](./c-cpp-rules.md#incompatibledealloc) 532 |
533 | 534 |
535 |
536 | 537 | ### 17. 使用已被释放的指针 538 |
539 | 540 | 示例: 541 | ``` 542 | int* p = new int(1); 543 | 544 | delete p; // Well-defined 545 | delete p; // Undefined behavior 546 | cout << *p; // Undefined behavior 547 | ``` 548 | 指针指向的对象被回收后,指针的值和指针曾指向的对象均失效,继续访问会导致未定义的行为。 549 |
550 |
551 | 552 | #### 依据 553 | ISO/IEC 14882:2003 3.7.3.2(4)-undefined 554 | ISO/IEC 14882:2011 3.7.4.2(4)-undefined 555 |
556 | 557 | #### 规则 558 | [ID_illAccess](./c-cpp-rules.md#illaccess) 559 | [ID_doubleFree](./c-cpp-rules.md#doublefree) 560 | [ID_danglingDeref](./c-cpp-rules.md#danglingderef) 561 |
562 | 563 |
564 |
565 | 566 | ### 18. 对象生命周期已结束,但未调用其有副作用的析构函数 567 |
568 | 569 | 示例: 570 | ``` 571 | struct U { 572 | .... 573 | }; 574 | 575 | struct T { 576 | ~T(); // If it has side effects 577 | .... 578 | }; 579 | 580 | void* p = malloc(max(sizeof(T), sizeof(U))); 581 | T* pT = new (p) T; 582 | U* pU = new (p) U; // Undefined behavior 583 | ``` 584 | 例中第二个 new 表达式结束了 pT 所指对象的生命周期,但没有调用其析构函数,如果其析构函数存在副作用,则会导致未定义的行为。 585 |
586 |
587 | 588 | #### 依据 589 | ISO/IEC 14882:2003 3.8(4)-undefined 590 | ISO/IEC 14882:2011 3.8(4)-undefined 591 |
592 | 593 |
594 |
595 | 596 | ### 19. 在分配空间后,生命周期开始前,或在生命周期结束后,回收空间前,通过指针访问对象 597 |
598 | 599 | 在对象的生命周期之外,通过指针进行如下操作会导致未定义的行为: 600 | - 访问非静态成员函数 601 | - 将指针转为基类指针 602 | - 用 static\_cast 转换指针(转为 void\*、char\*、unsigned char\* 等情况除外) 603 | - 用 dynamic\_cast 转换指针 604 | 605 | 示例: 606 | ``` 607 | struct T { 608 | T(); 609 | ~T(); 610 | void fun(); 611 | }; 612 | 613 | T* p = (T*)malloc(sizeof(T)); 614 | p->fun(); // Undefined behavior, the lifetime has not yet started 615 | 616 | new (p) T(); 617 | p->fun(); // Well-defined 618 | 619 | p->~T(); 620 | p->fun(); // Undefined behavior, the lifetime has ended 621 | 622 | free(p); 623 | ``` 624 |
625 |
626 | 627 | #### 依据 628 | ISO/IEC 14882:2003 3.8(5)-undefined 629 | ISO/IEC 14882:2011 3.8(5)-undefined 630 |
631 | 632 |
633 |
634 | 635 | ### 20. 在分配空间后,生命周期开始前,或在生命周期结束后,回收空间前,通过 glvalue 访问对象 636 |
637 | 638 | 在对象的生命周期之外,通过 glvalue 进行如下操作会导致未定义的行为: 639 | - 进行 lvalue\-to\-rvalue 转换 640 | - 访问非静态成员函数 641 | - 向基类引用转换 642 | - 用 static\_cast 转换 glvalue(转为 char& 或 unsigned char& 等情况除外) 643 | - 用 dynamic\_cast 转换 glvalue 644 | - 将 typeid 作用于 glvalue 645 | 646 | 示例: 647 | ``` 648 | struct B { void foo(); }; 649 | struct D: B { .... }; 650 | 651 | void B::foo() { 652 | new (this) D; // Ends the lifetime of *this 653 | } 654 | 655 | void bar(B& b) { 656 | b.foo(); 657 | if (typeid(b) == ....) { // Undefined behavior 658 | .... 659 | } 660 | } 661 | ``` 662 | 例中 b.foo 执行后 b 的生命周期结束,之后再对 b 的访问会导致未定义的行为。 663 |
664 |
665 | 666 | #### 依据 667 | ISO/IEC 14882:2003 3.8(6)-undefined 668 | ISO/IEC 14882:2011 3.8(6)-undefined 669 |
670 | 671 |
672 |
673 | 674 | ### 21. 具有静态、线程或自动存储期和 non\-trivial 析构函数的对象,其空间被非兼容类型的对象占据 675 |
676 | 677 | 示例: 678 | ``` 679 | struct A { ~A(); }; 680 | struct B { ~B(); }; 681 | 682 | void foo() { 683 | A a; 684 | new (&a) B; 685 | } // Undefined behavior 686 | ``` 687 | 例中局部对象 a 的空间被不相关类型的对象占据,在 foo 函数返回前仍会调用 A 的析构函数,导致未定义的行为。 688 |
689 |
690 | 691 | #### 依据 692 | ISO/IEC 14882:2003 3.8(8)-undefined 693 | ISO/IEC 14882:2011 3.8(8)-undefined 694 |
695 | 696 |
697 |
698 | 699 | ### 22. 常量对象的空间或曾属于常量对象的空间被其他对象占据 700 |
701 | 702 | 示例: 703 | ``` 704 | struct T { 705 | T(); 706 | ~T(); 707 | }; 708 | 709 | const T obj; 710 | 711 | void foo() { 712 | obj.~T(); 713 | new (&obj) const T; // Undefined behavior 714 | } 715 | ``` 716 |
717 |
718 | 719 | #### 依据 720 | ISO/IEC 14882:2003 3.8(9)-undefined 721 | ISO/IEC 14882:2011 3.8(9)-undefined 722 |
723 | 724 |
725 |
726 | 727 | ### 23. 通过 glvalue 访问对象,但 glvalue 的类型不符合要求 728 |
729 | 730 | 只应通过以下类型的 glvalue 访问对象,否则导致未定义的行为: 731 | - 对象的动态类型 732 | - 用 const 或 volatile 限定的对象动态类型 733 | - 与对象动态类型相似的类型(参见 ISO/IEC 14882:2011 4.4) 734 | - 与对象动态类型对应的有符号或无符号类型 735 | - 用 const 或 volatile 限定的与对象动态类型对应的有符号或无符号类型 736 | - 包含上述类型的聚合或联合类型(不包括静态成员类型) 737 | - 对象动态类型的基类类型(也包括被 const 或 volatile 限定的基类类型) 738 | - char 或 unsigned char 739 | 740 | 示例: 741 | ``` 742 | int i = 0; 743 | 744 | cout << i; // Well-defined 745 | cout << (const int&)i; // Well-defined 746 | cout << (unsigned int&)i; // Well-defined 747 | cout << (char&)i; // Well-defined 748 | 749 | cout << (long&)i; // Undefined behavior 750 | cout << (float&&)i; // Undefined behavior 751 | ``` 752 |
753 |
754 | 755 | #### 依据 756 | ISO/IEC 14882:2003 3.10(15)-undefined 757 | ISO/IEC 14882:2011 3.10(10)-undefined 758 |
759 | 760 | #### 规则 761 | [ID_castNoInheritance](./c-cpp-rules.md#castnoinheritance) 762 |
763 | 764 |
765 |
766 | 767 | ### 24. 通过 glvalue 引用不相关类型的对象或未初始化的对象 768 |
769 | 770 | glvalue 的类型与其引用的对象类型不同且没有继承关系,或引用的对象未初始化,会导致未定义的行为。 771 | 772 | 示例: 773 | ``` 774 | struct A { int i; }; 775 | struct B { int i; }; 776 | 777 | int foo(A& a) { 778 | return ((B&)a).i; // Undefined behavior, unrelated type conversion 779 | } 780 | 781 | int foo() { 782 | int i; 783 | return i; // Undefined behavior, ‘i’ is not initialized 784 | } 785 | ``` 786 |
787 |
788 | 789 | #### 依据 790 | ISO/IEC 14882:2003 4.1(1)-undefined 791 | ISO/IEC 14882:2011 4.1(1)-undefined 792 |
793 | 794 | #### 规则 795 | [ID_castNoInheritance](./c-cpp-rules.md#castnoinheritance) 796 | [ID_localInitialization](./c-cpp-rules.md#localinitialization) 797 |
798 | 799 |
800 |
801 | 802 | ### 25. 浮点类型转换产生的结果无法在相应的空间中表示 803 |
804 | 805 | 示例: 806 | ``` 807 | double d = FLT_MAX; 808 | d = d * 10; 809 | float f = d; // Non-defined behavior 810 | ``` 811 | 例中 d 的值超过了 float 的取值范围,将 d 的值转为 float 会导致未定义的行为。 812 |
813 |
814 | 815 | #### 依据 816 | ISO/IEC 14882:2003 4.8(1)-undefined 817 | ISO/IEC 14882:2011 4.8(1)-undefined 818 |
819 | 820 | #### 规则 821 | [ID_narrowCast](./c-cpp-rules.md#narrowcast) 822 |
823 | 824 |
825 |
826 | 827 | ### 26. 浮点类型转为整数类型时,整数类型无法存储浮点类型的整数部分 828 |
829 | 830 | 示例: 831 | ``` 832 | double f = 1e60; 833 | signed i = f; // Undefined behavior 834 | ``` 835 | 例中 1e60 无法被整型变量存储,导致未定义的行为。 836 |
837 |
838 | 839 | #### 依据 840 | ISO/IEC 14882:2003 4.9(1)-undefined 841 | ISO/IEC 14882:2011 4.9(1)-undefined 842 |
843 | 844 | #### 规则 845 | [ID_narrowCast](./c-cpp-rules.md#narrowcast) 846 |
847 | 848 |
849 |
850 | 851 | ### 27. 整数类型转为浮点类型时,浮点类型无法存储整数的值 852 |
853 | 854 | 示例: 855 | ``` 856 | long n = 1234567890L; 857 | float f0 = n; // Cannot be represented exactly 858 | double f1 = n; // OK 859 | ``` 860 | 例中 1234567890 无法被 float 变量存储,但可被 double 变量存储。 861 |
862 |
863 | 864 | #### 依据 865 | ISO/IEC 14882:2011 4.9(2)-undefined 866 |
867 | 868 | #### 规则 869 | [ID_narrowCast](./c-cpp-rules.md#narrowcast) 870 |
871 | 872 |
873 |
874 | 875 | ### 28. 表达式求值依赖无确定顺序的副作用 876 |
877 | 878 | 示例: 879 | ``` 880 | int a = 0; 881 | int b = a + a++; // Undefined behavior 882 | ``` 883 | 例中加法运算符左右子表达式无明确的求值顺序,如果左子表达式先求值,b 的值是 0,如果右子表达式先求值,b 的值可能是 1 也可能是 0,因为 a\+\+ 的值是 0,但 a\+\+ 的副作用在表达式求值过程中何时生效也是不确定的。 884 | 885 | 又如: 886 | ``` 887 | volatile int* p = foo(); 888 | int n = *p + *p; // Undefined behavior 889 | ``` 890 | 例中 n 的值在数学上应是 \*p 的二倍,但由于 p 指向 volatile 数据,结果可能不符合预期的数学关系。 891 |
892 |
893 | 894 | #### 依据 895 | ISO/IEC 14882:2003 5(4)-undefined 896 |
897 | 898 | #### 规则 899 | [ID_evaluationOrderReliance](./c-cpp-rules.md#evaluationorderreliance) 900 | [ID_confusingAssignment](./c-cpp-rules.md#confusingassignment) 901 |
902 | 903 |
904 |
905 | 906 | ### 29. 表达式的结果在数学上没有定义 907 |
908 | 909 | 如除 0、有符号整数溢出、负数位运算、浮点异常等均会导致未定义的行为。 910 | 911 | 示例: 912 | ``` 913 | signed s = INT_MAX + 1; // Undefined 914 | unsigned u = UINT_MAX + 1; // Well-defined 915 | ``` 916 | 例中变量 u 的值一定是 0,而 s 的值是标准未定义的,往往由编译器和执行环境决定。 917 | 918 | 设无符号整数的最大值为 M,无符号整数运算在程序中的结果是数学上的结果与 (M \+ 1) 取模的结果,这在数学上是有明确定义的,而对于有符号整数,将符号位移出相关比特位,或将非符号位移入符号位,在数学上是没有意义的,故称无符号整数不存在溢出问题,有符号整数存在溢出问题,溢出会导致未定义的行为。 919 |
920 |
921 | 922 | #### 依据 923 | ISO/IEC 14882:2003 5(5)-undefined 924 | ISO/IEC 14882:2011 5(4)-undefined 925 |
926 | 927 | #### 规则 928 | [ID_divideByZero](./c-cpp-rules.md#dividebyzero) 929 | [ID_evalOverflow](./c-cpp-rules.md#evaloverflow) 930 |
931 | 932 |
933 |
934 | 935 | ### 30. 被调用函数的语言链接性与该函数定义的语言链接性不符 936 |
937 | 938 | 示例: 939 | ``` 940 | // In a.c 941 | int foo() { 942 | return 0; 943 | } 944 | 945 | // In b.cpp 946 | int foo(); // Missing extern "C" 947 | 948 | int bar() { 949 | return foo(); // Undefined behavior 950 | } 951 | ``` 952 |
953 |
954 | 955 | #### 依据 956 | ISO/IEC 14882:2003 5.2.2(1)-undefined 957 | ISO/IEC 14882:2011 5.2.2(1)-undefined 958 |
959 | 960 |
961 |
962 | 963 | ### 31. 将非 POD 对象传入可变参数列表 964 |
965 | 966 | 可变参数列表是 C 语言的概念,C\+\+ 中具有拷贝构造或析构函数的对象难以与其兼容,如果将非 POD 对象传入可变参数列表,程序的行为在 C\+\+03 中是未定义的,在 C\+\+11 中是部分由实现定义的。 967 | 968 | 示例: 969 | ``` 970 | string str; 971 | void foo(int, ...); 972 | 973 | foo(1, str); // Undefined behavior 974 | ``` 975 |
976 |
977 | 978 | #### 依据 979 | ISO/IEC 14882:2003 5.2.2(7)-undefined 980 | ISO/IEC 14882:2011 5.2.2(7)-implementation 981 |
982 | 983 | #### 规则 984 | [ID_nonPODVariadicArgument](./c-cpp-rules.md#nonpodvariadicargument) 985 | [ID_badVaArgType](./c-cpp-rules.md#badvaargtype) 986 | [ID_forbidVariadicFunction](./c-cpp-rules.md#forbidvariadicfunction) 987 |
988 | 989 |
990 |
991 | 992 | ### 32. 用 static\_cast 将基类引用转为派生类引用,基类为虚基类,或引用的实际对象并非派生类对象 993 |
994 | 995 | 示例: 996 | ``` 997 | struct A {}; 998 | struct B: A {}; 999 | struct C: virtual B {}; 1000 | 1001 | A a; 1002 | C c; 1003 | A& ra = a; 1004 | A& rc = c; 1005 | 1006 | static_cast(ra); // Undefined behavior 1007 | static_cast(rc); // Undefined behavior 1008 | ``` 1009 | 例中 ra 引用的是基类对象,将其转为派生类引用会导致未定义的行为,A 和 B 是 C 的虚基类,需要运行时数据体现虚基类对象和派生类对象的空间关系,static\_cast 不考虑与运行时相关的转换逻辑,无法正确转换。 1010 |
1011 |
1012 | 1013 | #### 依据 1014 | ISO/IEC 14882:2003 5.2.9(5)-undefined 1015 | ISO/IEC 14882:2011 5.2.9(2)-undefined 1016 |
1017 | 1018 | #### 规则 1019 | [ID_downCast](./c-cpp-rules.md#downcast) 1020 |
1021 | 1022 |
1023 |
1024 | 1025 | ### 33. 用 static\_cast 将基类指针转为派生类指针,基类为虚基类,或指向的实际对象并非派生类对象 1026 |
1027 | 1028 | 示例: 1029 | ``` 1030 | struct A {}; 1031 | struct B: A {}; 1032 | struct C: virtual B {}; 1033 | 1034 | A a; 1035 | C c; 1036 | A* pa = &a; 1037 | A* pc = &c; 1038 | 1039 | static_cast(pa); // Undefined behavior 1040 | static_cast(pc); // Undefined behavior 1041 | ``` 1042 | 例中 pa 指向基类对象,将其转为派生类指针会导致未定义的行为,A 和 B 是 C 的虚基类,需要运行时数据体现虚基类对象和派生类对象的空间关系,static\_cast 不考虑与运行时相关的转换逻辑,无法正确转换。 1043 |
1044 |
1045 | 1046 | #### 依据 1047 | ISO/IEC 14882:2003 5.2.9(8)-undefined 1048 | ISO/IEC 14882:2011 5.2.9(11)-undefined 1049 |
1050 | 1051 | #### 规则 1052 | [ID_downCast](./c-cpp-rules.md#downcast) 1053 |
1054 | 1055 |
1056 |
1057 | 1058 | ### 34. 用 static\_cast 将成员指针转为基类成员指针时,基类中没有相关成员 1059 |
1060 | 1061 | 示例: 1062 | ``` 1063 | struct B { int b; }; 1064 | struct D: B { int d; }; 1065 | 1066 | int D::* mpb = &D::b; 1067 | int D::* mpd = &D::d; 1068 | 1069 | static_cast(mpb); // OK 1070 | static_cast(mpd); // Undefined behavior 1071 | ``` 1072 | 例中基类没有成员 d,将指向成员 d 的成员指针转为基类成员指针会导致未定义的行为。 1073 |
1074 |
1075 | 1076 | #### 依据 1077 | ISO/IEC 14882:2003 5.2.9(9)-undefined 1078 | ISO/IEC 14882:2011 5.2.9(12)-undefined 1079 |
1080 | 1081 | #### 规则 1082 | [ID_downCast](./c-cpp-rules.md#downcast) 1083 |
1084 | 1085 |
1086 |
1087 | 1088 | ### 35. 函数指针被转为不兼容的类型并执行 1089 |
1090 | 1091 | 示例: 1092 | ``` 1093 | void foo(); 1094 | 1095 | return ((int(*)())(&foo))(); // Undefined behavior 1096 | ``` 1097 | 例中 foo 函数没有返回值,将其强转为有返回值的函数并调用,会导致未定义的行为。 1098 |
1099 |
1100 | 1101 | #### 依据 1102 | ISO/IEC 14882:2003 5.2.10(6)-undefined 1103 | ISO/IEC 14882:2011 5.2.10(6)-undefined 1104 |
1105 | 1106 | #### 规则 1107 | [ID_functionPointerCast](./c-cpp-rules.md#functionpointercast) 1108 |
1109 | 1110 |
1111 |
1112 | 1113 | ### 36. 类型转换时去掉对象 const 属性并修改对象 1114 |
1115 | 1116 | 示例: 1117 | ``` 1118 | const int ci = 0; 1119 | const_cast(ci) = 1; // Undefined behavior 1120 | ``` 1121 |
1122 |
1123 | 1124 | #### 依据 1125 | ISO/IEC 14882:2003 5.2.11(7)-undefined 1126 | ISO/IEC 14882:2011 5.2.11(7)-undefined 1127 |
1128 | 1129 | #### 规则 1130 | [ID_qualifierCastedAway](./c-cpp-rules.md#qualifiercastedaway) 1131 |
1132 | 1133 |
1134 |
1135 | 1136 | ### 37. 对不完整类型的对象取地址,但该对象的完整类型重载了 operator & 1137 |
1138 | 1139 | 示例: 1140 | ``` 1141 | struct T; // Incomplete type 1142 | 1143 | T* foo(T& obj) { 1144 | return &obj; // Undefined behavior 1145 | } 1146 | 1147 | struct T { 1148 | T* operator &(); // Overload 1149 | }; 1150 | ``` 1151 | 在 foo 函数中参数 obj 的类型是不完整的,但其完整类型重载了 operator &,导致未定义的行为。 1152 |
1153 |
1154 | 1155 | #### 依据 1156 | ISO/IEC 14882:2003 5.3.1(4)-undefined 1157 | ISO/IEC 14882:2011 5.3.1(5)-undefined 1158 |
1159 | 1160 | #### 规则 1161 | [ID_overloadAddressOperator](./c-cpp-rules.md#overloadaddressoperator) 1162 |
1163 | 1164 |
1165 |
1166 | 1167 | ### 38. new 运算符第一个数组维度的参数为负数 1168 |
1169 | 1170 | 示例: 1171 | ``` 1172 | int* foo(int n) { 1173 | return new int[n]; // Undefined in C++03 if ‘n’ is negative 1174 | } 1175 | ``` 1176 | 如果 n 为负数,程序的行为在 C\+\+03 中是未定义的,C\+\+11 去掉了这项未定义行为的声明。 1177 |
1178 |
1179 | 1180 | #### 依据 1181 | ISO/IEC 14882:2003 5.3.4(6)-undefined 1182 |
1183 | 1184 |
1185 |
1186 | 1187 | ### 39. 用 delete 释放数组漏写中括号,用 delete 释放对象多写中括号,用 delete 释放非 new 表达式的结果 1188 |
1189 | 1190 | 示例: 1191 | ``` 1192 | string object; 1193 | 1194 | delete &object; // Undefined behavior 1195 | delete "string"; // Undefined behavior 1196 | delete new int[n]; // Undefined behavior 1197 | delete[] new int(n); // Undefined behavior 1198 | ``` 1199 |
1200 |
1201 | 1202 | #### 依据 1203 | ISO/IEC 14882:2003 5.3.5(2)-undefined 1204 | ISO/IEC 14882:2011 5.3.5(2)-undefined 1205 |
1206 | 1207 | #### 规则 1208 | [ID_excessiveDelete](./c-cpp-rules.md#excessivedelete) 1209 | [ID_insufficientDelete](./c-cpp-rules.md#insufficientdelete) 1210 | [ID_incompatibleDealloc](./c-cpp-rules.md#incompatibledealloc) 1211 | [ID_illDealloc](./c-cpp-rules.md#illdealloc) 1212 |
1213 | 1214 |
1215 |
1216 | 1217 | ### 40. 被 delete 释放的对象或数组类型不符合要求 1218 |
1219 | 1220 | 用 delete 释放对象时,对象的静态类型应与动态类型兼容,如果静态类型是动态类型的基类,静态类型应提供虚析构函数,否则导致未定义的行为;用 delete\[\] 释放数组时,对象的静态类型应与动态类型一致,否则导致未定义的行为。 1221 | 1222 | 示例: 1223 | ``` 1224 | struct B { ~B(); }; 1225 | struct D: B { ~D(); }; 1226 | 1227 | B* pDObj = new D; 1228 | B* pDArr = new D[123]; 1229 | 1230 | delete pDObj; // Undefined behavior 1231 | delete[] pDArr; // Undefined behavior 1232 | ``` 1233 | 例中基类 B 缺少虚析构函数,pDArr 的类型应为派生类指针。 1234 |
1235 |
1236 | 1237 | #### 依据 1238 | ISO/IEC 14882:2003 5.3.5(3)-undefined 1239 | ISO/IEC 14882:2011 5.3.5(3)-undefined 1240 |
1241 | 1242 | #### 规则 1243 | [ID_missingVirtualDestructor](./c-cpp-rules.md#missingvirtualdestructor) 1244 | [ID_arrayPointerCast](./c-cpp-rules.md#arraypointercast) 1245 |
1246 | 1247 |
1248 |
1249 | 1250 | ### 41. 用 delete 释放不完整类型的对象,但在对象完整类型声明中有 non\-trivial 析构函数 1251 |
1252 | 1253 | 示例: 1254 | ``` 1255 | struct T; 1256 | 1257 | void foo(T* p) { 1258 | delete p; // Undefined behavior 1259 | } 1260 | 1261 | struct T { 1262 | ~T(); // Non-trivial destructor 1263 | }; 1264 | ``` 1265 | 例中 delete 作用于不完整类型的指针 p,会导致未定义的行为。 1266 |
1267 |
1268 | 1269 | #### 依据 1270 | ISO/IEC 14882:2003 5.3.5(5)-undefined 1271 | ISO/IEC 14882:2011 5.3.5(5)-undefined 1272 |
1273 | 1274 | #### 规则 1275 | [ID_deleteIncompleteType](./c-cpp-rules.md#deleteincompletetype) 1276 |
1277 | 1278 |
1279 |
1280 | 1281 | ### 42. 对象解引用成员指针时,对象的动态类型不包含成员指针引用的成员 1282 |
1283 | 1284 | 示例: 1285 | ``` 1286 | struct A { int a; }; 1287 | struct B { int a, b; }; 1288 | 1289 | int foo(A* pa) { 1290 | B* pb = (B*)pa; 1291 | int B::* m = &B::b; 1292 | return pb->*m; // Undefined behavior 1293 | } 1294 | ``` 1295 |
1296 |
1297 | 1298 | #### 依据 1299 | ISO/IEC 14882:2003 5.5(4)-undefined 1300 | ISO/IEC 14882:2011 5.5(4)-undefined 1301 |
1302 | 1303 |
1304 |
1305 | 1306 | ### 43. 对象解引用成员指针时,成员指针为空指针 1307 |
1308 | 1309 | 示例: 1310 | ``` 1311 | struct T { int i; }; 1312 | T obj{0}; 1313 | int T::* mp = nullptr; 1314 | 1315 | cout << obj.*mp; // Undefined behavior 1316 | cout << (&obj)->*mp; // Undefined behavior 1317 | ``` 1318 |
1319 |
1320 | 1321 | #### 依据 1322 | ISO/IEC 14882:2003 5.5(6)-undefined 1323 | ISO/IEC 14882:2011 5.5(6)-undefined 1324 |
1325 | 1326 | #### 规则 1327 | [ID_nullDerefInScp](./c-cpp-rules.md#nullderefinscp) 1328 |
1329 | 1330 |
1331 |
1332 | 1333 | ### 44. / 或 % 运算符第二个操作数的值为 0 1334 |
1335 | 1336 | 示例: 1337 | ``` 1338 | int foo(int a, int b) try 1339 | { 1340 | return a / b; // Undefined behavior if ‘b’ is zero 1341 | } 1342 | catch (...) 1343 | { 1344 | return 0; // Unreachable 1345 | } 1346 | ``` 1347 | 除数为 0 会导致未定义的行为,且不受语言的异常机制控制。 1348 |
1349 |
1350 | 1351 | #### 依据 1352 | ISO/IEC 14882:2003 5.6(4)-undefined 1353 | ISO/IEC 14882:2011 5.6(4)-undefined 1354 |
1355 | 1356 | #### 规则 1357 | [ID_divideByZero](./c-cpp-rules.md#dividebyzero) 1358 |
1359 | 1360 |
1361 |
1362 | 1363 | ### 45. 指针加减整数,结果超出了指针指向数组的地址范围,使指针的值溢出 1364 |
1365 | 1366 | 设数组 a 的元素个数为 N,a 至 a \+ N 均为有效值(但不可对 a \+ N 解引用),超出这个范围的指针加减运算可能会使指针的值溢出,导致未定义的行为。 1367 | 1368 | 示例: 1369 | ``` 1370 | int a[N]; // Let ‘N’ be the array size 1371 | int* p; 1372 | 1373 | p = a; // OK 1374 | p = a + N; // OK, won't overflow 1375 | 1376 | p = a - 1; // May overflow, resulting in undefined behavior 1377 | p = a + N + 1; // May overflow, resulting in undefined behavior 1378 | ``` 1379 |
1380 |
1381 | 1382 | #### 依据 1383 | ISO/IEC 14882:2003 5.7(5)-undefined 1384 | ISO/IEC 14882:2011 5.7(5)-undefined 1385 |
1386 | 1387 | #### 规则 1388 | [ID_arrayIndexOverflow](./c-cpp-rules.md#arrayindexoverflow) 1389 |
1390 | 1391 |
1392 |
1393 | 1394 | ### 46. 未指向同一数组的指针相减 1395 |
1396 | 1397 | 示例: 1398 | ``` 1399 | int a[10]; 1400 | int b[10]; 1401 | ptrdiff_t d = a - b; // Undefined behavior 1402 | ``` 1403 |
1404 |
1405 | 1406 | #### 依据 1407 | ISO/IEC 14882:2003 5.7(5)-undefined 1408 | ISO/IEC 14882:2011 5.7(5)-undefined 1409 |
1410 | 1411 | #### 规则 1412 | [ID_illPtrDiff](./c-cpp-rules.md#illptrdiff) 1413 |
1414 | 1415 |
1416 |
1417 | 1418 | ### 47. 移位运算符右操作数为负数或超过相关类型比特位的数量 1419 |
1420 | 1421 | 示例: 1422 | ``` 1423 | int a = 1 << -1; // Undefined behavior 1424 | int b = 1 << 100; // Undefined behavior 1425 | ``` 1426 |
1427 |
1428 | 1429 | #### 依据 1430 | ISO/IEC 14882:2003 5.8(1)-undefined 1431 | ISO/IEC 14882:2011 5.8(1)-undefined 1432 |
1433 | 1434 | #### 规则 1435 | [ID_illShiftCount](./c-cpp-rules.md#illshiftcount) 1436 |
1437 | 1438 |
1439 |
1440 | 1441 | ### 48. 对有符号整数进行超出取值范围的左移运算 1442 |
1443 | 1444 | 将符号位移出相关比特位,或将非符号位移入符号位,在数学上是没有意义的。 1445 | 1446 | 示例: 1447 | ``` 1448 | int b = -1 << 1; // Undefined 1449 | ``` 1450 | 编译器可能会用乘法或除法实现有符号整数的移位,但不属于标准行为。 1451 |
1452 |
1453 | 1454 | #### 依据 1455 | ISO/IEC 14882:2011 5.8(2)-undefined 1456 |
1457 | 1458 | #### 规则 1459 | [ID_bitwiseOperOnSigned](./c-cpp-rules.md#bitwiseoperonsigned) 1460 |
1461 | 1462 |
1463 |
1464 | 1465 | ### 49. 将对象的值赋给具有部分重叠区域的另一个对象 1466 |
1467 | 1468 | 示例: 1469 | ``` 1470 | int a[3] {1, 2, 3}; 1471 | 1472 | struct A { 1473 | int x, y; 1474 | }; 1475 | 1476 | A* p = reinterpret_cast(a); 1477 | A* q = reinterpret_cast(a + 1); 1478 | 1479 | *p = *q; // Undefined behavior 1480 | ``` 1481 | 例中 p 和 q 指向的对象具有部分重叠的区域,\*p = \*q 或 \*q = \*p 均会导致未定义的行为。 1482 |
1483 |
1484 | 1485 | #### 依据 1486 | ISO/IEC 14882:2003 5.17(8)-undefined 1487 | ISO/IEC 14882:2011 5.17(8)-undefined 1488 |
1489 | 1490 | #### 规则 1491 | [ID_overlappingAssignment](./c-cpp-rules.md#overlappingassignment) 1492 |
1493 | 1494 |
1495 |
1496 | 1497 | ### 50. 有返回值的函数没有通过 return 语句返回 1498 |
1499 | 1500 | 示例: 1501 | ``` 1502 | bool foo(int i) { 1503 | if (i >= 0) { 1504 | return true; // OK 1505 | } 1506 | } // Undefined behavior if i < 0 1507 | ``` 1508 | 例中 foo 函数有返回值,当 i 的值小于 0 时没有通过 return 语句返回,导致未定义的行为。 1509 |
1510 |
1511 | 1512 | #### 依据 1513 | ISO/IEC 14882:2003 6.6.3(2)-undefined 1514 | ISO/IEC 14882:2011 6.6.3(2)-undefined 1515 |
1516 | 1517 | #### 规则 1518 | [ID_notAllBranchReturn](./c-cpp-rules.md#notallbranchreturn) 1519 |
1520 | 1521 |
1522 |
1523 | 1524 | ### 51. 递归地定义和初始化静态对象 1525 |
1526 | 1527 | 示例: 1528 | ``` 1529 | int foo(int i) { 1530 | if (i < 100) { 1531 | static int s = foo(2 * i); // Undefined behavior 1532 | return s; 1533 | } 1534 | return i; 1535 | } 1536 | ``` 1537 | 例中静态局部变量 s 的初始化需要递归调用所属函数,是否会产生多个 s 的实例以及是否能正常结束递归,均是未定义的。 1538 |
1539 |
1540 | 1541 | #### 依据 1542 | ISO/IEC 14882:2003 6.7(4)-undefined 1543 | ISO/IEC 14882:2011 6.7(4)-undefined 1544 |
1545 | 1546 |
1547 |
1548 | 1549 | ### 52. 修改非 mutable 常量对象 1550 |
1551 | 1552 | 示例: 1553 | ``` 1554 | struct T { 1555 | mutable int i; 1556 | int j; 1557 | }; 1558 | 1559 | const T obj {}; 1560 | T* p = const_cast(&obj); 1561 | 1562 | obj.i++; // Well-defined 1563 | obj.j++; // Ill-formed, compile error 1564 | 1565 | p->i = 0; // Well-defined 1566 | p->j = 1; // Undefined behavior 1567 | ``` 1568 |
1569 |
1570 | 1571 | #### 依据 1572 | ISO/IEC 14882:2003 7.1.5.1(4)-undefined 1573 | ISO/IEC 14882:2011 7.1.6.1(4)-undefined 1574 |
1575 | 1576 | #### 规则 1577 | [ID_qualifierCastedAway](./c-cpp-rules.md#qualifiercastedaway) 1578 |
1579 | 1580 |
1581 |
1582 | 1583 | ### 53. 使用没有 volatile 限定的 glvalue 访问有 volatile 限定的对象 1584 |
1585 | 1586 | 示例: 1587 | ``` 1588 | volatile int v; // Changed by other devices 1589 | 1590 | void wait(int& r) { 1591 | r = 0; 1592 | while (r != 100); 1593 | } 1594 | 1595 | int main() { 1596 | wait((int&)v); // Undefined behavior 1597 | } 1598 | ``` 1599 | 例中变量 v 在定义处由 volatile 限定,但通过无 volatile 限定的引用访问,导致未定义的行为。 1600 |
1601 |
1602 | 1603 | #### 依据 1604 | ISO/IEC 14882:2003 7.1.5.1(7)-undefined 1605 | ISO/IEC 14882:2011 7.1.6.1(6)-undefined 1606 |
1607 | 1608 | #### 规则 1609 | [ID_qualifierCastedAway](./c-cpp-rules.md#qualifiercastedaway) 1610 |
1611 | 1612 |
1613 |
1614 | 1615 | ### 54. 具有 noreturn 属性的函数返回至调用方 1616 |
1617 | 1618 | 示例: 1619 | ``` 1620 | [[noreturn]] void foo() { 1621 | throw Exception(); // OK 1622 | } 1623 | 1624 | [[noreturn]] int bar() { 1625 | return 0; // Undefined behavior 1626 | } 1627 | 1628 | [[noreturn]] void baz(int i) { // Undefined behavior if ‘i’ != 0 1629 | if (i == 0) { 1630 | throw Exception(); 1631 | } 1632 | } 1633 | ``` 1634 | 例中 bar 可以正常返回,baz 在某条件下正常返回,会导致未定义的行为。 1635 |
1636 |
1637 | 1638 | #### 依据 1639 | ISO/IEC 14882:2011 7.6.3(2)-undefined 1640 |
1641 | 1642 | #### 规则 1643 | [ID_unsuitableReturn](./c-cpp-rules.md#unsuitablereturn) 1644 |
1645 | 1646 |
1647 |
1648 | 1649 | ### 55. 空指针解引用 1650 |
1651 | 1652 | 空指针未指向任何对象或函数,解引用空指针会导致未定义的行为。 1653 | 1654 | 示例: 1655 | ``` 1656 | int* a = nullptr; 1657 | *a = 1; // Undefined behavior 1658 | a[5] = 2; // Undefined behavior 1659 | memset(a, 0, 10); // Undefined behavior 1660 | 1661 | struct T { 1662 | int i; 1663 | int f(); 1664 | }; 1665 | T* p = nullptr; 1666 | p->i = 0; // Undefined behavior 1667 | p->f(); // Undefined behavior 1668 | 1669 | using F = void (*)(); 1670 | F fp = nullptr; 1671 | fp(); // Undefined behavior 1672 | ``` 1673 |
1674 |
1675 | 1676 | #### 依据 1677 | ISO/IEC 14882:2003 8.3.2(4)-undefined 1678 | ISO/IEC 14882:2011 8.3.2(5)-undefined 1679 |
1680 | 1681 | #### 规则 1682 | [ID_nullDerefInScp](./c-cpp-rules.md#nullderefinscp) 1683 | [ID_nullDerefInExp](./c-cpp-rules.md#nullderefinexp) 1684 |
1685 | 1686 |
1687 |
1688 | 1689 | ### 56. 对象的实际类型与当前静态类型不相关,并调用其非静态成员函数 1690 |
1691 | 1692 | 示例: 1693 | ``` 1694 | struct A { int foo(); }; 1695 | struct B { .... }; 1696 | 1697 | int bar(void* p) { 1698 | return static_cast(p)->foo(); 1699 | } 1700 | 1701 | int main() { 1702 | B b; 1703 | return bar(&b); // Undefined behavior 1704 | } 1705 | ``` 1706 | 例中 A 与 B 是不同且没有继承关系的类,通过不合理的类型转换调用非静态成员函数会导致未定义的行为。 1707 |
1708 |
1709 | 1710 | #### 依据 1711 | ISO/IEC 14882:2003 9.3.1(1)-undefined 1712 | ISO/IEC 14882:2011 9.3.1(2)-undefined 1713 |
1714 | 1715 | #### 规则 1716 | [ID_castNoInheritance](./c-cpp-rules.md#castnoinheritance) 1717 | [ID_forbidMemberVoidPtr](./c-cpp-rules.md#forbidmembervoidptr) 1718 | [ID_forbidFunctionVoidPtr](./c-cpp-rules.md#forbidfunctionvoidptr) 1719 |
1720 | 1721 |
1722 |
1723 | 1724 | ### 57. 在构造函数或析构函数中调用纯虚函数 1725 |
1726 | 1727 | 示例: 1728 | ``` 1729 | struct T { 1730 | virtual ~T() { 1731 | release(); // Undefined behavior 1732 | } 1733 | virtual void release() = 0; 1734 | }; 1735 | ``` 1736 |
1737 |
1738 | 1739 | #### 依据 1740 | ISO/IEC 14882:2003 10.4(6)-undefined 1741 | ISO/IEC 14882:2011 10.4(6)-undefined 1742 |
1743 | 1744 | #### 规则 1745 | [ID_virtualCallInConstructor](./c-cpp-rules.md#virtualcallinconstructor) 1746 | [ID_virtualCallInDestructor](./c-cpp-rules.md#virtualcallindestructor) 1747 |
1748 | 1749 |
1750 |
1751 | 1752 | ### 58. 对象的实际类型与当前静态类型不相关,并调用其析构函数 1753 |
1754 | 1755 | 示例: 1756 | ``` 1757 | struct T { .... }; 1758 | struct U { .... }; 1759 | 1760 | void foo(void* p) { 1761 | delete static_cast(p); // Undefined behavior 1762 | } 1763 | 1764 | void bar() { 1765 | foo(new U); 1766 | } 1767 | ``` 1768 | 例中 p 实际指向的不是 T 类型的对象,程序的行为是未定义的。 1769 |
1770 |
1771 | 1772 | #### 依据 1773 | ISO/IEC 14882:2003 12.4(12)-undefined 1774 | ISO/IEC 14882:2011 12.4(13)-undefined 1775 |
1776 | 1777 | #### 规则 1778 | [ID_forbidFunctionVoidPtr](./c-cpp-rules.md#forbidfunctionvoidptr) 1779 | [ID_forbidMemberVoidPtr](./c-cpp-rules.md#forbidmembervoidptr) 1780 | [ID_castNoInheritance](./c-cpp-rules.md#castnoinheritance) 1781 |
1782 | 1783 |
1784 |
1785 | 1786 | ### 59. 在对象生命周期结束后调用其析构函数 1787 |
1788 | 1789 | 示例: 1790 | ``` 1791 | struct T { ~T(); }; 1792 | 1793 | void foo() { 1794 | T obj; 1795 | obj.~T(); // End of lifetime 1796 | } // Undefined behavior 1797 | ``` 1798 | 显式调用 obj 的析构函数后,obj 的生命周期结束,但函数返回前还会调用 obj 的析构函数,导致未定义的行为。 1799 | 1800 | 又如: 1801 | ``` 1802 | struct B { .... }; 1803 | struct D: B { .... }; 1804 | 1805 | D* p = new D; 1806 | p->B::~B(); // End of lifetime of base class object 1807 | delete p; // Undefined behavior 1808 | ``` 1809 | 调用 p\->B::\~B() 后,基类对象的生命周期结束,但在 delete p 时基类析构函数仍会被执行,导致未定义的行为。 1810 |
1811 |
1812 | 1813 | #### 依据 1814 | ISO/IEC 14882:2003 12.4(14)-undefined 1815 | ISO/IEC 14882:2011 12.4(15)-undefined 1816 |
1817 | 1818 | #### 规则 1819 | [ID_explicitDtorCall](./c-cpp-rules.md#explicitdtorcall) 1820 | [ID_this_deleteInDestructor](./c-cpp-rules.md#this_deleteindestructor) 1821 |
1822 | 1823 |
1824 |
1825 | 1826 | ### 60. 基类对象构造完毕之前调用成员函数 1827 |
1828 | 1829 | 示例: 1830 | ``` 1831 | struct A { 1832 | A(int); 1833 | }; 1834 | 1835 | struct B: A { 1836 | int i; 1837 | int fun(); 1838 | 1839 | B(): A(fun()), // Undefined 1840 | i(fun()) { // Well-defined 1841 | } 1842 | }; 1843 | ``` 1844 | 例中成员函数 fun 的返回值是基类构造函数的参数,但基类尚未开始构造,会导致未定义的行为,用成员函数 fun 初始化成员 i 则没有问题,因为此时基类对象已构造完毕。 1845 |
1846 |
1847 | 1848 | #### 依据 1849 | ISO/IEC 14882:2003 12.6.2(8)-undefined 1850 | ISO/IEC 14882:2011 12.6.2(13)-undefined 1851 |
1852 | 1853 | #### 规则 1854 | [ID_illMemberCall](./c-cpp-rules.md#illmembercall) 1855 |
1856 | 1857 |
1858 |
1859 | 1860 | ### 61. 对成员或基类对象的不合理引用 1861 |
1862 | 1863 | 对具有 non\-trivial 构造函数的对象,在构造函数执行之前引用其非静态成员或基类对象,或者在析构函数执行之后引用其非静态成员或基类对象,会导致未定义的行为。 1864 | 1865 | 示例: 1866 | ``` 1867 | struct A { // Trivial 1868 | int a; 1869 | }; 1870 | 1871 | struct B: A { // Non-trivial 1872 | B(); 1873 | int b; 1874 | }; 1875 | 1876 | extern B obj; // The object is defined later 1877 | B* p = &obj; // OK 1878 | int* p1 = &obj.a; // Undefined, refers to base class member 1879 | int* p2 = &obj.b; // Undefined, refers to member 1880 | 1881 | A* pa = &obj; // Undefined, upcast to a base class type 1882 | 1883 | extern A trvlObj; // The object is defined later 1884 | int* p3 = &trvlObj.a; // OK, A is a trivial class 1885 | 1886 | B obj; // Define the object 1887 | A trvlObj; // Define the object 1888 | ``` 1889 |
1890 |
1891 | 1892 | #### 依据 1893 | ISO/IEC 14882:2003 12.7(1)-undefined 1894 | ISO/IEC 14882:2011 12.7(1)-undefined 1895 |
1896 | 1897 |
1898 |
1899 | 1900 | ### 62. 将对象指针转为其基类对象的指针时,基类对象尚未开始构造或已结束析构 1901 |
1902 | 1903 | 示例: 1904 | ``` 1905 | struct A { 1906 | .... 1907 | }; 1908 | 1909 | struct B: A { 1910 | B(); 1911 | B(A*); 1912 | }; 1913 | 1914 | struct C: B { 1915 | C(): B(this) { // Undefined behavior 1916 | } 1917 | }; 1918 | ``` 1919 | 例中将 C 的 this 指针作为基类 B 构造函数的参数,相当于将 C\* 转为 A\*,而这时基类 B 和 A 均未开始构造,会导致未定义的行为。 1920 | 1921 | 又如(各项声明接上例): 1922 | ``` 1923 | struct X { 1924 | X(A*); 1925 | }; 1926 | 1927 | struct D: B, X { 1928 | D(): B(), X(this) { // OK 1929 | } 1930 | }; 1931 | ``` 1932 | 将 D 的 this 指针作为基类 X 构造函数的参数,相当于将 D\* 转为 A\*,这时 B 的构造函数已经执行完毕,所以这种情况没有问题。 1933 |
1934 |
1935 | 1936 | #### 依据 1937 | ISO/IEC 14882:2003 12.7(2)-undefined 1938 | ISO/IEC 14882:2011 12.7(3)-undefined 1939 |
1940 | 1941 |
1942 |
1943 | 1944 | ### 63. 正在构造或析构的对象通过 . 或 \-> 调用虚函数,而且该对象与当前构造或析构函数不属于同一个类或基类 1945 |
1946 | 1947 | 示例: 1948 | ``` 1949 | struct V { 1950 | virtual void foo(); 1951 | virtual void bar(); 1952 | }; 1953 | 1954 | struct A: virtual V { 1955 | void foo() override; 1956 | }; 1957 | 1958 | struct B: virtual V { 1959 | B(V*, A*); 1960 | void bar() override; 1961 | }; 1962 | 1963 | struct C: A, B { 1964 | C(): B((A*)this, this) { 1965 | } 1966 | void foo() override; 1967 | void bar() override; 1968 | }; 1969 | 1970 | B::B(V* v, A* a) { 1971 | v->bar(); // Well-defined, V is the base of B 1972 | a->foo(); // Undefined behavior, A is not a base of B 1973 | } 1974 | ``` 1975 |
1976 |
1977 | 1978 | #### 依据 1979 | ISO/IEC 14882:2003 12.7(3)-undefined 1980 | ISO/IEC 14882:2011 12.7(4)-undefined 1981 |
1982 | 1983 |
1984 |
1985 | 1986 | ### 64. typeid 作用于正在构造或析构的对象,而且该对象与当前构造或析构函数不属于同一个类或基类 1987 |
1988 | 1989 | 示例: 1990 | ``` 1991 | struct V { 1992 | virtual void foo(); 1993 | }; 1994 | 1995 | struct A: virtual V {}; 1996 | struct B: virtual V { B(V*, A*); }; 1997 | 1998 | struct C: A, B { 1999 | C(): B((A*)this, this) {} 2000 | }; 2001 | 2002 | B::B(V* v, A* a) { 2003 | typeid(*v); // Well-defined, V is the base of B 2004 | typeid(*a); // Undefined behavior, A is not a base of B 2005 | } 2006 | ``` 2007 |
2008 |
2009 | 2010 | #### 依据 2011 | ISO/IEC 14882:2003 12.7(4)-undefined 2012 | ISO/IEC 14882:2011 12.7(5)-undefined 2013 |
2014 | 2015 |
2016 |
2017 | 2018 | ### 65. dynamic\_cast 作用于正在构造或析构的对象,而且该对象与当前构造或析构函数不属于同一个类或基类 2019 |
2020 | 2021 | 示例: 2022 | ``` 2023 | struct V { 2024 | virtual void foo(); 2025 | }; 2026 | 2027 | struct A: virtual V {}; 2028 | struct B: virtual V { B(V*, A*); }; 2029 | 2030 | struct C: A, B { 2031 | C(): B((A*)this, this) {} 2032 | }; 2033 | 2034 | B::B(V* v, A* a) { 2035 | dynamic_cast(v); // Well-defined, V is the base of B 2036 | dynamic_cast(a); // Undefined behavior, A is not a base of B 2037 | } 2038 | ``` 2039 |
2040 |
2041 | 2042 | #### 依据 2043 | ISO/IEC 14882:2003 12.7(5)-undefined 2044 | ISO/IEC 14882:2011 12.7(6)-undefined 2045 |
2046 | 2047 |
2048 |
2049 | 2050 | ### 66. 对模板函数进行非良构调用,或在模板定义和实例化上下文之外有更好的候选函数匹配 2051 |
2052 | 2053 | 示例: 2054 | ``` 2055 | namespace N 2056 | { 2057 | struct A {}; 2058 | 2059 | template 2060 | void foo(T&, double) { .... } 2061 | } 2062 | 2063 | int main() 2064 | { 2065 | N::A a; 2066 | foo(a, 1); 2067 | } 2068 | 2069 | namespace N 2070 | { 2071 | template 2072 | void foo(T&, int) { .... } // Undefined behavior, a better match for ‘foo(a, 1)’ 2073 | } 2074 | ``` 2075 |
2076 |
2077 | 2078 | #### 依据 2079 | ISO/IEC 14882:2003 14.6.4.2(1)-undefined 2080 | ISO/IEC 14882:2011 14.6.4.2(1)-undefined 2081 |
2082 | 2083 |
2084 |
2085 | 2086 | ### 67. 需要无限递归的模版实例化 2087 |
2088 | 2089 | 示例: 2090 | ``` 2091 | template 2092 | class A 2093 | { 2094 | A* p; // OK 2095 | A a; // Undefined behavior 2096 | }; 2097 | ``` 2098 | 如果实例化 A,需要实例化 A,如果实例化 A,需要实例化 A,依次类推,形成了无限递归。 2099 |
2100 |
2101 | 2102 | #### 依据 2103 | ISO/IEC 14882:2003 14.7.1(14)-undefined 2104 | ISO/IEC 14882:2011 14.7.1(15)-undefined 2105 |
2106 | 2107 |
2108 |
2109 | 2110 | ### 68. 构造或析构函数在 function\-try\-block 的 handler 中访问非静态成员 2111 |
2112 | 2113 | 示例: 2114 | ``` 2115 | class T { 2116 | int err; 2117 | 2118 | public: 2119 | T() try { .... } catch (...) { log(err); } // Undefined behavior 2120 | ~T() try { .... } catch (...) { log(err); } // Undefined behavior 2121 | }; 2122 | ``` 2123 | 流程进入 function\-try\-block 的 handler 时,非静态成员的生命周期已结束,不可再被访问。 2124 |
2125 |
2126 | 2127 | #### 依据 2128 | ISO/IEC 14882:2003 15.3(10)-undefined 2129 | ISO/IEC 14882:2011 15.2(10)-undefined 2130 |
2131 | 2132 | #### 规则 2133 | [ID_illMemberAccess](./c-cpp-rules.md#illmemberaccess) 2134 |
2135 | 2136 |
2137 |
2138 | 2139 | ### 69. 有返回值的函数在 function\-try\-block 的 handler 中没有正确返回 2140 |
2141 | 2142 | 示例: 2143 | ``` 2144 | int foo() try { // Function-try-block 2145 | return bar(); 2146 | } 2147 | catch (...) { // Undefined behavior if there is no return statement 2148 | } 2149 | ``` 2150 | 例中 function\-try\-block 的 handler 没有 return 语句,导致未定义的行为。 2151 |
2152 |
2153 | 2154 | #### 依据 2155 | ISO/IEC 14882:2003 15.3(16)-undefined 2156 | ISO/IEC 14882:2011 15.3(15)-undefined 2157 |
2158 | 2159 | #### 规则 2160 | [ID_notAllBranchReturn](./c-cpp-rules.md#notallbranchreturn) 2161 |
2162 | 2163 |
2164 |
2165 | 2166 | ### 70. 在 \#if、 \#elif 的条件中,由宏展开产生了 defined 表达式,或 defined 表达式格式不正确 2167 |
2168 | 2169 | 示例: 2170 | ``` 2171 | #define DEFINED(x) defined(x) 2172 | 2173 | #if DEFINED(__cplusplus) // Undefined behavior 2174 | .... 2175 | #endif 2176 | ``` 2177 |
2178 |
2179 | 2180 | #### 依据 2181 | ISO/IEC 14882:2003 16.1(4)-undefined 2182 | ISO/IEC 14882:2011 16.1(4)-undefined 2183 |
2184 | 2185 | #### 规则 2186 | [ID_macro_defineReserved](./c-cpp-rules.md#macro_definereserved) 2187 | [ID_illFormedDirective](./c-cpp-rules.md#illformeddirective) 2188 |
2189 | 2190 |
2191 |
2192 | 2193 | ### 71. \#include 指令经宏展开后不满足标准格式 2194 |
2195 | 2196 | 示例: 2197 | ``` 2198 | #define STDIO_H 2199 | #define STDLIB_H stdlib.h 2200 | 2201 | #include STDIO_H // OK 2202 | #include STDLIB_H // Undefined behavior 2203 | ``` 2204 |
2205 |
2206 | 2207 | #### 依据 2208 | ISO/IEC 14882:2003 16.2(4)-undefined 2209 | ISO/IEC 14882:2011 16.2(4)-undefined 2210 |
2211 | 2212 | #### 规则 2213 | [ID_illFormedDirective](./c-cpp-rules.md#illformeddirective) 2214 |
2215 | 2216 |
2217 |
2218 | 2219 | ### 72. 宏的实参列表中出现预处理指令 2220 |
2221 | 2222 | 示例: 2223 | ``` 2224 | #define PRINT(s) printf(#s) 2225 | 2226 | PRINT( 2227 | #ifdef MAC // Undefined behavior 2228 | rabbit 2229 | #else 2230 | hamster 2231 | #endif 2232 | ); 2233 | ``` 2234 |
2235 |
2236 | 2237 | #### 依据 2238 | ISO/IEC 14882:2003 16.3(10)-undefined 2239 | ISO/IEC 14882:2011 16.3(11)-undefined 2240 |
2241 | 2242 | #### 规则 2243 | [ID_directiveInMacroArgument](./c-cpp-rules.md#directiveinmacroargument) 2244 | [ID_macro_function](./c-cpp-rules.md#macro_function) 2245 |
2246 | 2247 |
2248 |
2249 | 2250 | ### 73. 预处理运算符 \# 的结果不是有效的字符串 2251 |
2252 | 2253 | 示例: 2254 | ``` 2255 | #define M(x) #x 2256 | 2257 | cout << M(); // Undefined behavior 2258 | ``` 2259 | 例中宏 M 的参数不足,无法生成有效的字符串,这可能不会通过编译,也可能产生空串,或其他非预期的结果。 2260 |
2261 |
2262 | 2263 | #### 依据 2264 | ISO/IEC 14882:2003 16.3.2(2)-undefined 2265 | ISO/IEC 14882:2011 16.3.2(2)-undefined 2266 |
2267 | 2268 | #### 规则 2269 | [ID_macro_complexConcat](./c-cpp-rules.md#macro_complexconcat) 2270 |
2271 | 2272 |
2273 |
2274 | 2275 | ### 74. 预处理运算符 \#\# 的结果不是有效的符号 2276 |
2277 | 2278 | 示例: 2279 | ``` 2280 | #define M(a, b) a ## b 2281 | 2282 | int M(x, 0) = 0; // OK, ‘x0’ is a valid token 2283 | int M(0, x) = 0; // Undefined behavior, ‘0x’ is not a valid token 2284 | ``` 2285 |
2286 |
2287 | 2288 | #### 依据 2289 | ISO/IEC 14882:2003 16.3.3(3)-undefined 2290 | ISO/IEC 14882:2011 16.3.3(3)-undefined 2291 |
2292 | 2293 | #### 规则 2294 | [ID_macro_complexConcat](./c-cpp-rules.md#macro_complexconcat) 2295 |
2296 | 2297 |
2298 |
2299 | 2300 | ### 75. \#line 指定的行号为 0 或大于规定值 2301 |
2302 | 2303 | C\+\+03 规定行号不可大于 32767,C\+\+11 规定不可大于 2147483647,否则导致未定义的行为。 2304 | 2305 | 示例: 2306 | ``` 2307 | #line 0 // Undefined behavior 2308 | #line 2147483648 // Undefined behavior 2309 | ``` 2310 |
2311 |
2312 | 2313 | #### 依据 2314 | ISO/IEC 14882:2003 16.4(3)-undefined 2315 | ISO/IEC 14882:2011 16.4(3)-undefined 2316 |
2317 | 2318 | #### 规则 2319 | [ID_illFormedDirective](./c-cpp-rules.md#illformeddirective) 2320 |
2321 | 2322 |
2323 |
2324 | 2325 | ### 76. \#line 指令不符合标准格式 2326 |
2327 | 2328 | \#line 后只应为整数常量或整数常量和文件名称字符串,其他形式均会导致未定义的行为。 2329 | 2330 | 示例: 2331 | ``` 2332 | #line "name.cpp" // Undefined behavior 2333 | #line NUM + 1234 // Undefined behavior 2334 | ``` 2335 |
2336 |
2337 | 2338 | #### 依据 2339 | ISO/IEC 14882:2003 16.4(5)-undefined 2340 | ISO/IEC 14882:2011 16.4(5)-undefined 2341 |
2342 | 2343 | #### 规则 2344 | [ID_illFormedDirective](./c-cpp-rules.md#illformeddirective) 2345 |
2346 | 2347 |
2348 |
2349 | 2350 | ### 77. 用 \#define 定义或用 \#undef 取消定义具有保留意义的名称 2351 |
2352 | 2353 | 如 \_\_LINE\_\_、\_\_FILE\_\_、\_\_DATE\_\_、\_\_TIME\_\_、\_\_STDC\_\_、\_\_cplusplus、defined 等名称被定义或取消定义。 2354 | 2355 | 示例: 2356 | ``` 2357 | #undef __cplusplus // Undefined behavior 2358 | #define __STDC__ 0 // Undefined behavior 2359 | ``` 2360 |
2361 |
2362 | 2363 | #### 依据 2364 | ISO/IEC 14882:2003 16.8(3)-undefined 2365 | ISO/IEC 14882:2011 16.8(4)-undefined 2366 |
2367 | 2368 | #### 规则 2369 | [ID_macro_defineReserved](./c-cpp-rules.md#macro_definereserved) 2370 | [ID_macro_undefReserved](./c-cpp-rules.md#macro_undefreserved) 2371 |
2372 | 2373 |
2374 |
2375 | 2376 | ### 78. 供语言机制调用的函数不符合要求 2377 |
2378 | 2379 | 如 replacement functions 或 handler functions 不符合要求(required behavior),会导致未定义的行为。 2380 | 2381 | 示例: 2382 | ``` 2383 | struct T { 2384 | void* operator new(size_t) { // A replacement function 2385 | return nullptr; // Undefined behavior 2386 | } 2387 | }; 2388 | ``` 2389 | 标准要求 operator new 返回非空地址,分配失败则抛出 bad\_alloc 异常(见 ISO/IEC 14882:2011 18.6.1.1),否则导致未定义的行为。 2390 | 2391 | 又如: 2392 | ``` 2393 | void my_handler() { // A handler function 2394 | return; // Do nothing 2395 | } 2396 | 2397 | int main() { 2398 | set_terminate(my_handler); // Undefined behavior 2399 | .... 2400 | } 2401 | ``` 2402 | 标准要求 terminate handler 结束进程的执行,不可返回至调用方(见 ISO/IEC 14882:2011 18.8.3.1),否则导致未定义的行为。 2403 |
2404 |
2405 | 2406 | #### 依据 2407 | ISO/IEC 14882:2003 17.1.15-undefined 2408 | ISO/IEC 14882:2011 17.3.21-undefined 2409 |
2410 | 2411 |
2412 |
2413 | 2414 | ### 79. 程序实现了应由标准库提供的功能 2415 |
2416 | 2417 | 示例: 2418 | ``` 2419 | namespace std 2420 | { 2421 | const int& max(const int& a, const int& b) { // Undefined behavior 2422 | .... 2423 | } 2424 | } 2425 | ``` 2426 | 示例代码实现的 max 会干扰标准库中的 max,导致未定义的行为。 2427 |
2428 |
2429 | 2430 | #### 依据 2431 | ISO/IEC 14882:2003 17.1.17-undefined 2432 | ISO/IEC 14882:2011 17.3.22-undefined 2433 |
2434 | 2435 |
2436 |
2437 | 2438 | ### 80. 未经许可,向 std 命名空间添加声明或定义 2439 |
2440 | 2441 | 示例: 2442 | ``` 2443 | namespace std 2444 | { 2445 | using tstring = basic_string< 2446 | TCHAR, char_traits, allocator // Undefined behavior 2447 | >; 2448 | } 2449 | ``` 2450 | 对特殊命名空间的修改是否会生效,以及是否会造成非预期的影响,均是未定义的。 2451 |
2452 |
2453 | 2454 | #### 依据 2455 | ISO/IEC 14882:2011 17.6.4.2.1(1)-undefined 2456 |
2457 | 2458 | #### 规则 2459 | [ID_stdNamespaceModified](./c-cpp-rules.md#stdnamespacemodified) 2460 |
2461 | 2462 |
2463 |
2464 | 2465 | ### 81. 对标准库,特化模板类成员函数,特化模板类成员模板函数,特化、偏特化成员类模版 2466 |
2467 | 2468 | 示例: 2469 | ``` 2470 | template <> 2471 | void std::vector::push_back(const int&) { // Undefined behavior 2472 | .... 2473 | } 2474 | ``` 2475 | 示例代码特化了 vector 类的 push\_back 函数,其产生的影响是未定义的。 2476 |
2477 |
2478 | 2479 | #### 依据 2480 | ISO/IEC 14882:2011 17.6.4.2.1(2)-undefined 2481 |
2482 | 2483 |
2484 |
2485 | 2486 | ### 82. 未经许可,向 posix 命名空间添加声明或定义 2487 |
2488 | 2489 | 示例: 2490 | ``` 2491 | namespace posix { // Undefined behavior 2492 | .... 2493 | } 2494 | ``` 2495 | posix 命名空间是 C\+\+ 的保留命名空间,自定义代码中不应出现名为 posix 的命名空间。 2496 |
2497 |
2498 | 2499 | #### 依据 2500 | ISO/IEC 14882:2011 17.6.4.2.2(1)-undefined 2501 |
2502 | 2503 |
2504 |
2505 | 2506 | ### 83. 声明或定义标准库保留名称 2507 |
2508 | 2509 | 示例: 2510 | ``` 2511 | #define override // Undefined behavior 2512 | #define new new(nothrow) // Undefined behavior 2513 | #define string vector // Undefined behavior 2514 | ``` 2515 |
2516 |
2517 | 2518 | #### 依据 2519 | ISO/IEC 14882:2003 17.4.3.1(1)-undefined 2520 | ISO/IEC 14882:2011 17.6.4.3(2)-undefined 2521 |
2522 | 2523 | #### 规则 2524 | [ID_reservedName](./c-cpp-rules.md#reservedname) 2525 | [ID_macro_defineReserved](./c-cpp-rules.md#macro_definereserved) 2526 |
2527 | 2528 |
2529 |
2530 | 2531 | ### 84. 编译器未提供标准头文件,但编译时引入了非标准同名头文件 2532 |
2533 | 2534 | 应选用健全的编译器,并保证编译环境的安全,否则程序的行为是未定义的。 2535 |
2536 |
2537 | 2538 | #### 依据 2539 | ISO/IEC 14882:2003 17.4.3.2(1)-undefined 2540 | ISO/IEC 14882:2011 17.6.4.4(1)-undefined 2541 |
2542 | 2543 |
2544 |
2545 | 2546 | ### 85. 为标准库函数提供不符合要求的参数 2547 |
2548 | 2549 |
2550 |
2551 | 2552 | #### 依据 2553 | ISO/IEC 14882:2003 17.4.3.7(1)-undefined 2554 | ISO/IEC 14882:2011 17.6.4.9(1)-undefined 2555 |
2556 | 2557 |
2558 |
2559 | 2560 | ### 86. 多线程调用标准库函数造成数据竞争 2561 |
2562 | 2563 | 某些库函数与共享数据相关,但并未提供安全的同步机制,在多线程环境中使用会导致未定义的行为。 2564 | 2565 | 示例: 2566 | ``` 2567 | void foo() { 2568 | while (true) { 2569 | auto r = rand(); // Undefined behavior 2570 | .... 2571 | } 2572 | } 2573 | 2574 | int main() { 2575 | thread t0(foo), t1(foo); 2576 | .... 2577 | } 2578 | ``` 2579 | 多线程并发调用 rand、srand 等函数会造成数据竞争,导致未定义的行为。 2580 |
2581 |
2582 | 2583 | #### 依据 2584 | ISO/IEC 14882:2011 17.6.4.10(1)-undefined 2585 |
2586 | 2587 | #### 规则 2588 | [ID_dataRaces](./c-cpp-rules.md#dataraces) 2589 |
2590 | 2591 |
2592 |
2593 | 2594 | ### 87. 违反标准库函数要求的前置条件,除非标准库函数声明了这种情况会抛出异常 2595 |
2596 | 2597 | 示例: 2598 | ``` 2599 | string s{"abc"}; 2600 | 2601 | cout << s[8]; // Undefined behavior 2602 | cout << s.at(8); // Well-defined, throw out_of_range 2603 | ``` 2604 | 标准规定 string::at 在参数超出范围时抛出 out\_of\_range 异常,而 string::operator \[\] 的参数超出范围会导致未定义的行为。 2605 |
2606 |
2607 | 2608 | #### 依据 2609 | ISO/IEC 14882:2003 17.4.3.8(1)-undefined 2610 | ISO/IEC 14882:2011 17.6.4.11(1)-undefined 2611 |
2612 | 2613 |
2614 |
2615 | 2616 | ### 88. offsetof 用于非 standard layout 类型,或用于计算静态成员以及成员函数的偏移量 2617 |
2618 | 2619 | 示例: 2620 | ``` 2621 | struct T { 2622 | int* m; 2623 | 2624 | T(); 2625 | virtual ~T(); 2626 | }; 2627 | 2628 | size_t s = offsetof(T, m); // Undefined behavior 2629 | ``` 2630 | 例中 T 不是 standard layout 类型,用 offsetof 求成员 m 的偏移量会导致未定义的行为。 2631 |
2632 |
2633 | 2634 | #### 依据 2635 | ISO/IEC 14882:2003 18.1(5)-undefined 2636 | ISO/IEC 14882:2011 18.2(4)-undefined 2637 |
2638 | 2639 | #### 规则 2640 | [ID_deprecatedOffsetof](./c-cpp-rules.md#deprecatedoffsetof) 2641 |
2642 | 2643 |
2644 |
2645 | 2646 | ### 89. 可变参数列表中省略号的前一个形式参数为引用、数组、函数,或具有与默认参数提升后不兼容的类型 2647 |
2648 | 2649 | 示例: 2650 | ``` 2651 | void foo(int& n, ...); // Undefined behavior 2652 | void foo(int f(), ...); // Undefined behavior 2653 | void foo(int a[10], ...); // Undefined behavior 2654 | void foo(char c, ...); // Undefined behavior 2655 | ``` 2656 | 例中参数 n、f、a 分别为引用、函数和数组,会导致未定义的行为,c 与默认参数提升后的类型不符,也会导致未定义的行为。 2657 |
2658 |
2659 | 2660 | #### 依据 2661 | ISO/IEC 14882:2003 18.7(3)-undefined 2662 | ISO/IEC 14882:2011 18.10(3)-undefined 2663 |
2664 | 2665 | #### 规则 2666 | [ID_badParmN](./c-cpp-rules.md#badparmn) 2667 | [ID_forbidVariadicFunction](./c-cpp-rules.md#forbidvariadicfunction) 2668 |
2669 | 2670 |
2671 |
2672 | 2673 | ### 90. longjmp 跳转使应被执行的析构函数未被执行 2674 |
2675 | 2676 | 示例: 2677 | ``` 2678 | void foo() { 2679 | string obj; 2680 | .... 2681 | longjmp(buf, x); // Undefined behavior 2682 | } 2683 | ``` 2684 | 例中局部对象 obj 的析构函数不会被执行,由此引发的问题会导致未定义的行为。 2685 |
2686 |
2687 | 2688 | #### 依据 2689 | ISO/IEC 14882:2003 18.7(4)-undefined 2690 | ISO/IEC 14882:2011 18.10(4)-undefined 2691 |
2692 | 2693 | #### 规则 2694 | [ID_forbidLongjmp](./c-cpp-rules.md#forbidlongjmp) 2695 |
2696 | 2697 |
2698 |
2699 | 2700 |
2701 |
2702 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qihoo360/safe-rules/ee49cb0e5a885fff4f146b720e44d80c213bcb31/logo.png -------------------------------------------------------------------------------- /main-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Qihoo360/safe-rules/ee49cb0e5a885fff4f146b720e44d80c213bcb31/main-logo.png --------------------------------------------------------------------------------