我偶然沿着下面的代码:
#include <bitset>
#include <iostream>
int main() {
int x = 8;
void *w = &x;
bool val = *reinterpret_cast<const unsigned char*>(&x);
bool *z = static_cast<bool *>(w);
std::cout << "z (" << z << ") is " << *z << ": " << std::bitset<8>(*z) << "\n";
std::cout << "val is " << val << ": " << std::bitset<8>(val) << "\n";
}
使用-O3时,这将生成输出:
z (0x7ffcaef0dba4) is 8: 00001000
val is 1: 00000001
但是,如果使用-O 0,则会生成以下输出:
z (0x7ffe8c6c914c) is 0: 00000000
val is 1: 00000001
我知道解引用z
会调用未定义的行为,这也是为什么我们会看到不一致的结果。但是,将reinterpret_cast
解引用为val
似乎不会调用未定义的行为,并且可靠地生成{0,1}值。
通过(https://godbolt.org/z/f6s11Kr96),我们看到用于x86的gcc产生:
lea rax, [rbp-16]
movzx eax, BYTE PTR [rax]
test al, al
setne al
mov BYTE PTR [rbp-9], al
test
setne
指令的作用是将非0值转换为1(并将0值保持为0)。是否有某种规则规定reinterpret_cast
从void *
到const unsigned char *
应该具有此行为?
4条答案
按热度按时间toiithl61#
阅读
*z
的值会导致未定义的行为,这是由于严格的别名冲突(C++20 [basic.lval]/11)。表达式的类型为bool
,但内存位置的对象的类型为int
。只有特定的类型对允许别名,bool到int不允许。代码的
val
部分不是UB,因为const unsigned char
允许别名其他类型。val
的初始化器将产生一个unsigned char
值,其内存表示与x
的第一个字节的内容相同。然后,将该结果转换(不重新解释)为
bool
,如果为0则生成false
,否则生成true
。unhi4e5o2#
z
的值(不仅仅是解引用自身)会导致未定义的行为,因为这是一个别名冲突。(z
指向int
类型的对象,但访问是通过bool
类型的左值进行的)通过类型为
unsigned char
的左值进行的访问被明确排除在别名冲突之外。(参见[basic.lval]/11.3)但是,从技术上讲,仍然没有规定通过
unsigned char
左值访问int
对象的结果应该是什么,其意图是给出int
对象的对象表示的第一个字节,但是目前的标准在没有规定该行为方面存在缺陷,本文P1839试图解决该缺陷。从对象表示中阅读第一个字节作为
unsigned char
值后,在初始化bool val
时将其隐式转换为bool
。从unsigned char
到bool
的转换是 values 的转换,不重新解释对象表示。指定零值转换为false
,其他值转换为true
。(参见conv.bool(https://timsong-cpp.github.io/cppwp/n4868/conv.bool))无论您是显式地强制转换
void*
还是直接将int*
强制转换为unsigned char*
或bool*
,这都无关紧要。指针之间的reinterpret_cast
实际上被指定为等效于static_cast<void*>
,然后将static_cast
指定为目标指针类型。(在您的代码中,static_cast
和reinterpret_cast
是可以互换的。)xlpyo6sf3#
这是未定义的行为。
根据
-fsanitize=undefined
:https://godbolt.org/z/vM5MxT4Md
特别是,
-fsanitize=undefined
抱怨bool
应该保存true
或false
,其他任何内容都是UB(我相信true
和false
的位表示是由实现定义的)。u7up0aaq4#
与expr.reinterpret.cast相同
reinterpret_cast<T>(v)
1.一个对象指针可以显式地转换成不同类型的对象指针。当对象指针类型的纯右值v转换成对象指针类型“指针指向cv
T
“时,结果是static_cast<cv T*>(static_cast<cv void*>(v))
。[Note 6:将指向
T1
类型对象的“指向T1
的指针“类型的指针转换为“指向T2
的指针“类型(其中T2
是对象类型,T2
的对齐要求并不比T1
的对齐要求更严格),并转换回其原始类型,得到原始指针值。表达式
bool val = *reinterpret_cast<const unsigned char*>(&x);
是有效代码。