9

在优秀的博客文章What Every Programmer Should Know About Undefined Behavior中,“违反类型规则”部分说:

将 int* 转换为 float* 并取消引用它是未定义的行为(访问“int”,就好像它是“float”一样)。C 要求这些类型的类型转换通过 memcpy 发生:使用指针转换是不正确的,并且会导致未定义的行为。这方面的规则非常微妙,我不想在这里详细介绍(char* 有一个例外,向量具有特殊属性,联合改变事物等)。

我想了解规则的全部细微差别。它们在 C++11 规范中的什么位置?还是失败了,C 规范(C90、C99、C11)?

在从这个 Stack Overflow 问题N3485 链接的 C++11 规范中,我正在查看 5.2.10“重新解释演员表”,但没有看到 char* 或联合异常的语言。所以这可能不是正确的地方。那么正确的地方在哪里呢?

4

3 回答 3

4

您正在寻找的规则在 §3.10/10 中(在 C++11 中):

如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义: — 对象的动态类型,

— 对象动态类型的 cv 限定版本,

— 与对象的动态类型类似(如 4.4 中定义)的类型,

— 对应于对象动态类型的有符号或无符号类型, — 对应于对象动态类型的 cv 限定版本的有符号或无符号类型,

— 在其元素或非静态数据成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的元素或非静态数据成员),

— 作为对象动态类型的(可能是 cv 限定的)基类类型的类型,

— char 或 unsignedchar 类型。

未定义的行为有不同的类型(或动机)。

在强制转换int*float*然后取消引用它的情况下,很明显标准无法定义它,因为可能发生的情况取决于架构和int. 另一方面,引用的段落是完全错误的——使用memcpy来进行转换也是未定义的行为,原因大致相同。

未定义行为的动机之一是允许实现以对目标架构有意义的方式定义它,如果存在的话。这是这样一种情况。故意导致它失败的编译器是有缺陷的。当然,如果我们假设 32 位 2 的补码 int和 32 位 IEEE float,我们可能会期望 的某些值int对应于捕获 NaN,这将导致程序失败。这是行为未定义的部分原因;允许这样的事情发生。但是如果我们熟悉硬件的底层细节,它应该可以按预期工作,前提是编译器可以看到演员表。如果没有,这是编译器的 QoI 问题,对于此类工作应避免使用此类编译器。

正如上面所暗示的,这种特殊情况,事实上,在所有涉及类型双关语的情况下(例如,写入联合的一个成员,并从另一个成员读取)确实会造成问题,标准尚未解决找到适当的措辞。出现问题是因为通常允许编译器假定指向不同类型(字符类型除外)的指针没有别名;aint*永远不能和 a 指向同一个对象float*. 并且证明两个指针不能别名对于优化很重要。即使标准说它是未定义的行为,在指针强制转换或联合清晰可见的地方破坏代码的编译器也会被破坏。一个编译器在它看到的只是两个指向不相关类型的指针的情况下中断代码是可以理解的,即使在标准规定行为定义良好的情况下也是如此。

Usingmemcpy通过使用两个不同的对象来避免这个问题,这两个对象没有别名。它仍然会遇到未定义的行为,因为将 a 的位模式int放入 a float,然后访问浮点数,没有任何已定义的行为。(反之亦然;我知道至少有一台机器将 a 的位复制float到 anint可能会导致非法int值。)

于 2013-02-06T15:27:55.487 回答
1

C++ 标准规定,如果某个行为未明确描述为定义,则它是隐式未定义的。由于标准没有定义强制转换为的行为int*float*因此它是隐式未定义的。

于 2013-02-06T14:05:56.920 回答
0

reinterpret_caston 指针是根据static_castonvoid指针定义的

5.2.10 重新解释演员表 [expr.reinterpret.cast]>

7 对象指针可以显式转换为不同类型的对象指针。70 当“pointer to T1”类型的prvalue v转换为“pointer to cv T2”类型时,结果是static_cast<cv T2*>(static_cast<cv void*>(v))如果T1和T2都是标准布局类型(3.9)并且T2的对齐要求不严格T1 的那些,或者如果任何一种类型都是无效的。将“指向 T1 的指针”类型的纯右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型会产生原始类型指针值。未指定任何其他此类指针转换的结果。

5.2.9 静态转换 [expr.static.cast]

13 “指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中 T 是对象类型,而 cv2 与 cv1 相同的 cv 限定或大于 cv1 的 cv 限定. 空指针值转换为目标类型的空指针值。指向对象的指针类型的值转换为“指向 cv void 的指针”并返回,可能具有不同的 cv 限定,应具有其原始值。

于 2013-02-06T15:18:39.200 回答