您的位置:首页 > 编程语言 > C语言/C++

c++ -- Undefined behavior

2015-09-24 11:16 351 查看

c++ – Undefined behavior

c++ 标准里定义了未定义行为(undefined behavior),即:

1.3.24 undefined behavior

behavior for which this International Standard imposes no requirements

[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed.

— end note ]

也就是说,标准对未定义行为产生的结果不做任何要求。于是,含有未定义行为的程序可以做任何事情。有些比较极端的说法,认为未定义行为可以格式化你的硬盘。这很恐怖,但是在极端的情况下也并非不可能。当然标准中描述的未定义行为的可能结果是:

忽略这一情况,产生未知的结果(很不幸,这可能真的包含格式化硬盘)

按照编译环境约定的方式编译或运行(可以有也可以没有编译warning/error)

终止编译或运行(并提供warning /error)

所以,包含未定义行为的程序的结果是不确定的,并且很可能是有害的。包含未定义行为的程序应该被认为是错误的程序。

那么C++中有哪些未定义行为呢?下面有一个不完全的列表(翻译有可能不准确,望见谅):

1.9 6 在 signal handler 里修改非
volatile std::sig_atomatic_t
类型,且非 lock-free atomic objects 的对象的值。

1.9 15 如果对一个 scalar object 的副作用,与对同一个对象的另一个副作用或读操作是无序的。(代替了C++03的序列点,经典的 ++)

1.10 21 data race 。在一个线程对某内存地址有写操作时,另一个线程对同一个内存地址有读或写操作。两者至少有一个不是原子操作,并且两者无序。

2.2 1 2 在 translation phase 2, 删除 \ 与紧邻的换行符时,如果出现了新的可以解释为 universal-character-name 的串。

2.2 1 4 在 translation phase 4,处理预编译命令时,如果在宏展开的 token concatenation (##) 中出现了新的可以解释为 universal-character-name 的串。

2.5 2 在划分 preprocessing token 时,如果 ’ 或 ” 落入了“其它”类。(所有类别包括:header-name, identifier, pp-number, character-literal, user-defined-character-literal, string-literal, user-defined-string-literal, preprocessing-op-or-punc, 其它)

2.14.5 12 修改字符串常量

3.2 5 不同编译单元中定义的同名模板不一致

3.6.1 4 在一个 static 或 thread storage duration 的对象析构时,调用了 std::exit

3.6.3 2 引用了已析构(已超过生存期的)局部变量。

3.6.3 4 如果不能用于 signal handler 库静态变量或函数的使用不在全局变量析构结束与调用 atexit() 注册的函数被调用之前。

3.7.4.1 2 对开辟 0 字节内存返回的指针解引用。

3.7.4.1 3 释放内存函数抛出异常。delete 的 指针不来自 new 的结果。delete[] 的指针 不来自 new[] 的结果。

3.7.4.1 4 使用指向非法的(已被释放的)内存地址的指针的值。

3.8 4 对象的生存期结束但析构函数没有被调用,同时程序依赖与析构函数的副作用。

3.8 5 对象开辟内存后,构造前,说析构后,释放内存前,如果其指针:

用做 delete 的对象

用于引用非静态成员变量,或调用非静态成员函数

被隐式转换为基类指针

用作 static_cast 的对象(除非转换为 void * ,或通过 void * 转换为 char * 或 unsingned char *)

用作 dynamic_cast 的对象

3.8 6 对象开辟内存后,构造前,说析构后,释放内存前,如果其glvalue:

经历 lvalue-rvalue conversion

用于引用非静态成员变量,或调用非静态成员函数

被隐式转换为基类对象

用作 static_cast 的对象(除非最终装换为 char & 或 unsinged c- har&)

用作 dynamic_cast 或 typeid 的操作数

3.8 8 对 static thread 或 automatic storage duration 的对象,如果它有 non-trivial destructor ,隐式析构时原始内存位置的对象类型与对象构造是类型不同。

3.8 9 在一个 const staic/thread/automatic storage duration 的对象所在内存位置创建新的对象。

3.10 10 使用下列方式以外的方式访问一个对象的 glvalue

通过对象的动态类型

cv-qualified 动态类型

通过与对象动态类型相似的类型

对象动态类型对应的有符号或无符号类型

cv-qualified 对象动态类型对应的有符号或无符号类型

包含该类型成员的 aggregate 或 union

(cv-qualified) 动态类型的基类

char 或 unsigned char

4.1 1 在 lvalue to rvalue conversion 中,被转换的对象不是目标类型的对象。

4.8 1 在浮点类型装换中,值不在目标啊类型的可表示区间

4.9 1 浮点类型转换为整型,截断或的值目标类型不能表示

4.9 2 整型转换为浮点类型,值不在目标类型的可表示区间

5 4 表达式求值过程中,出现数学上无意义的结果,或结果在目标类型无法表示

5.2.2 1 函数调用时的,调用表达式生成的函数 linkage 与函数本身的 linage 不一致。

5.2.9 2 类类型对象引用通过 static_cast 装换为其派生类的引用,但对象本身并非该派生类的对象。

5.2.9 11 指向类的指针通过 static_cast 转换为其派生类的指针,但是对象并非该派生类的对象。

5.2.9 12 指向类成员的的指针通过 static_cast 转换为其基类成员的指针,但是基类并不包含原指针所指向的成员。

5.2.10 6 通过与原始函数类型不同的函数指针调用函数

5.3.1 5 对不完整的类类型对象使用 operator& ,同时类重载了 operator&

5.3.5 2 delete 表达式的操作数不是null、 operator new 的结果 或 指向 operator new 的结果的基类的指针;operator[] 表达式的操作数不是null 或 operator new[] 的结果。

5.3.5 3 delete 的操作数是静态类型与动态类型不一致,静态类型不是动态类型的基类或是基类但其析构函数不是虚的。delete[] 的操作数的静态类型与动态类型不一致。

5.3.5 5 delete 时类不完整,同时类有 non-trivial 析构函数或 deallocation function 。

5.5 4 在E1.*E2 中,E1 的动态类型不包含 E2 所表示的成员。

5.5 6 .* 运算符的第二个操作数是 null

5.6 4 / % 的后一个操作数是 0

5.7 5 指针与整数进行加减运算,结果超出原指针所指向的数组范围(数组范围包括数组后的一个元素)

5.7 6 指针相减的结果 ptrdiff_t 无法比表示。两个并不指向同一数组对象的指针相减

5.8 1 << >> 的有操作数为负,或大于或等于提升后的左操作数的位数

5.8 2 << 的左操作数为负,或结果在结果类型不能表示

5.17 8

6.6.3 2 对一个有返回值的函数,不经 return 语句结束。

6.7 4 在静态变量初始化过程中,程序运行递归地回到了同一个静态的定义

7.1.6.1 4 修改一个常量对象,除非修改的是类的 mutable 成员。

7.1.6.1 6 通过一个非 volatile glvalue 引用(refer)一个 volatile 对象

7.6.3 2 一个带有 noreturn 属性函数返回了

9.3.1 2 对非类对象或其派生类对象调用类的非静态成员函数

10.4 6 在构造或析构函数中调用纯虚函数

12.4 13 显示调用析构函数是,对象类型与析构函数所对应的类名不一致,且对象不是析构函数所对应的类的派生类

12.4 15 对已经结束生存期的对象调用析构函数。

12.6.2 13 在类全部基类初始化完成之前,对类进行如下操作:调用类的成员函数(包括虚函数),将类对象用于type_id或dynamic_cast的操作数

12.7 1 对于一个有 non-trivial constructor 的类,在类对象的构造函数开始执行之前使用给对象的非静态成员变量或其基类。

12.7 3 将派生类X的指针或引用转换为基类B的指针或引用 发生在X,B或任一派生自的B的X的直接或间接基类构造函数开始执行之前,或析构函数结束执行之后。对类的非静态成员的使用发生在类的构造函数开始执行之前,或析构函数结束执行之后。

12.7 4 在构造、析构中使用explicit class member access调用虚函数,但引用的类型不是正在构造、析构的类(或子类)对象或其基类

12.7 5 在构造、析构过程中对正在构造或析构的对象使用 typeid ,但对象的静态类型不是构造、析构的类或其基类。

12.7 6 在构造、析构过程中对正在构造或析构的对象使用 dynamic_cast ,但对象的静态类型不是构造、析构的类或其基类。

14.6.4.2 1 对与依赖模板参数的 function name lookup, 如果 function 是一个 unqualified-id,如果在所有编译单元中搜索的结果与仅在模板定义与实例化上下文中搜索结果不同。

14.7.1 15 在模板实例化过程中出现无限递归

15.2 10 在构造或析构函数的 function-try-block handler 中使用类的非静态成员变量

15.3 15 在一个有返回值的函数中,程序运行离开 function-try-block handler(相当于没有返回值)

16.1 4 在 #if / #elif 后的条件中,经宏替换生成了 defined 。#if / #elif 条件中 defined 格式错误

16.2 4 #include (经过宏替换后)格式错误

16.3 11 使用有参数宏的时候,参数表中出现预编译指令(preprocessing directives)

16.3.2 2 #在宏替换后生成了非法的字符串常量

16.3.3 3 ##在宏替换后生成了非法的预编译符号

16.4 3 #line 指示的行号等于0或大于2147483647

16.4 5 #line 格式(在宏替换后)不正确

16.8 4 (条件)预定义宏或 defined 被用于 #define 或 #undef (预定义宏包括:
__cplusplus
__DATE__
__FILE__
__LINE__
__STDC_HOSTED__
__TIME__ __STDC__
__STDC_MB_MIGHT_NEQ_WC__
__STDC__VERSION__
__STDC_ISO_10646__
__STDCPP_STRICT_POINTER_SAFETY__
__STDCPP_THREADS__


17.3.21 提供给库函数的(回调)函数未满足规定的条件

17.3.22 程序提供了规定由编译器实现的库函数

17.6.4.2.1 1 在未被明确允许的情况下,向名字空间std或其子名字空间加入声明或定义

17.6.4.2.1 2 声明:标准库类模板的成员函数的特化;标准库类或类模板的成员函数模板的特化;标准类或类模板的成员类模板的特化或偏特化

17.6.4.2.2 1 在没有特别声明的情况下向名字空间posix或其子名字空间里加入声明或定义

17.6.4.3 2 声明或定义被标准库保留的符号

17.6.4.4 编译器没有提供库函数头文件,但是在搜索路径里存在

17.6.4.8 2 replacement functions 没有提供需要的语义;handler functions 没有提供需要的语义;用于实例化模板的类型没有提供需要的语义;replacement functions, handler functions, destructor operation 抛出异常(除非被允许);非完整类型用于实例化类(除非类模板明确允许)

17.6.4.9 1 为库函数提供一个非法的值

17.6.4.10 1 多线程调用库函数引起了 data race

17.6.4.11 1 函数的参数未满足前置条件,除非函数明确说明在这种情况下可以跑出异常

18.2 4 用于 offsetof 宏的类不是 standard layout class,或成员是静态成员或成员函数

18.10 2 … 前的类型是函数、数组或引用

18.10 4 如果将 setjmp longjmp 调用替换为 try throw 会引起 non-trivial destructor 的调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: