我有以下代码段
#include <iostream>
using namespace std;
class foobar
{
public:
unsigned int a = 97;
unsigned int b = 98;
unsigned int c = 99;
};
class barfoo
{
public:
char a:32,b:32,c;
};
int main(){
foobar * f = new foobar();
barfoo * b = (reinterpret_cast<barfoo *>(f));
cout << b->a << "-" << b->b << "-" << b->c;
}
输出:
a-b-c
但是,如果我在barfoo中指定c的宽度,则它不起作用(所有值都为0)。观察
#include <iostream>
using namespace std;
class foobar
{
public:
unsigned int a = 97;
unsigned int b = 98;
unsigned int c = 99;
};
class barfoo
{
public:
char a:32,b:32,c:32;
};
int main(){
foobar * f = new foobar();
barfoo * b = (reinterpret_cast<barfoo *>(f));
cout << b->a << "-" << b->b << "-" << b->c;
}
输出:
--
字符都是0
知道为什么会这样吗?
链接到ideone first snippet - 工作
Linke to ideone second snippet - 不工作
编译器是gcc-5.1 谢谢!
答案 0 :(得分:4)
此reinterpret_cast
不符合已定义行为的要求,因此虽然可以接受b
的任何后续访问,但其结果的分配是未定义的行为:
barfoo * b = (reinterpret_cast<barfoo *>(f));
reinterpret_cast
必须满足以下条件之一,其中foobar
是DynamicType,barfoo
是AliasedType:
- AliasedType是(可能是cv限定的)DynamicType
- AliasedType和DynamicType都是(可能是多级,可能是每个级别的cv限定)指向相同类型的指针T
- AliasedType是DynamicType
的(可能是cv限定的)有符号或无符号变体- AliasedType是一种聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,包含联合的子聚合和非静态数据成员的元素):这使得它在给定指向其非静态成员或元素的指针的情况下,可以安全地获取指向结构或联合的可用指针。
- AliasedType是(可能是cv限定的)DynamicType
的基类- AliasedType为
char
或unsigned char
:这允许将任何对象的对象表示检查为unsigned char数组。
由于AliasedType不属于任何这些条件,因此访问reinterpret_cast
的结果是未定义的行为。
答案 1 :(得分:2)
首先,检查2个类是否大小相同。可以想象,填充和对齐可能使位域a,b和c的位置与正常成员a,b和c等的位置不同。为什么使用char作为32位位域?我的编译器警告位域超出类型的大小。
否则(如果你为barfoo类更改了char为unsigned int),无论你有什么标准-UB,reinterpret_cast和access都有很好的工作机会。如果你通过一个联盟使用良好支持和完善的类型惩罚成语,可能更是如此。
通过联合进行类型惩罚是告诉编译器'这些不同类型的2个指针可能是别名的一种方式,不要进行根本优化,假设它们可能不是别名'。