问题描述
我听说不允许将指针从一种类型转换为另一种类型,但是我不明白为什么。
例如,在下面的代码中,我所要做的就是读取强制转换的值,如果情况允许,怎么会出错?
scores
解决方法
您没有破坏严格的混叠,也从未打过任何东西。
您确实违反了指针算术规则。指针算术只能在数组上执行,否则是UB。这些规则的原因都相似,以促进某种优化。
void frobnicate(int*);
int bar()
{
Foo foo = {1,2,42,4};
frobnicate(&foo.b);
return foo.c;
}
编译器是否只允许return 42;
?是的,据推测是在标准规定的规则下。
但这也是标准成为精神分裂症的地方。有一个特别笨拙的宏offsetof
,它是成员到结构的偏移量。在严格阅读该标准的情况下,它没有合法的用例:您不能使用它进行指针算术。
偶然地,编译器决定他们允许对结构进行指针运算,并禁用这些种类的优化。实际上,需要来编译Linux内核(C具有类似规则)。
那么,如果可能的话,怎么会出错呢?
在大多数情况下,只要您确保两者之间没有填充,它们就不会。
,C ++派生自C,因此C89标准的作者不想禁止编译器,例如[其示例,逐字引用]:
int a;
void f( double * b )
{
a = 1;
*b = 2.0;
g(a); // Presumably g is declared as e.g. `void g(int);` somewhere?
}
从生成的代码传递g
写入a
的值,而不是在写入a
并读取*b
之后传递g
值被读取。在对象两次访问的情况下,这种优化是完全合理的,并且编译器在处理此类访问时不需要看到的任何内容都不会暗示对象类型与这些访问之间使用的任何其他类型之间的任何关系。>
就像标准的作者看到不需要强制执行实现能够处理任何特定的函数调用嵌套一样,他们也不需要强制编译器花费任何特定的工作量来识别不同指针和左值之间的关系。类型。给出类似的东西:
long get_float_bits( float * b )
{
return *(long*)b;
}
float q;
long test(void)
{
q = 1.0f;
long result = get_float_bits(&q);
q = 2.0f;
}
我不认为委员会成员会想象任何编译器会认真地争辩说,由于get_float_bits
无法访问类型为float
的对象,因此高质量的编译器应该省略对q
。这样的逻辑将被视为类似于编译器作者的决定,因为该标准并未强制要求实现支持两个以上级别的函数嵌套,因此高质量的编译器应通过省略对可能证明嵌套的函数的调用来优化程序。至少四个深。该标准未禁止此类行为的原因并不是作者想要邀请该行为,而是因为他们认为这种行为非常明显地令人发指,以至于没有人会购买以这种方式表现的编译器。
不幸的是,C89的作者不遗余力地避免暗示某些实现可能被认为比其他实现更好,因此避免了提及在没有绝对要求的情况下高质量实现应该做的任何事情,而C89的作者gcc将这种避免解释为意味着,任何依赖于标准未规定的内容的代码都应被视为“破损”。
,实际上,您在做什么不是演员。可以使用以下C样式来进行实际转换:
int *abcd = (int *)&f;
您还可以使用f
以C ++样式将(int *)
转换为reinterpret_cast
:
int *abcd = reinterpret_cast<int *>(&f);