为什么允许将一个指针转换为引用? [英] Why is it allowed to cast a pointer to a reference?

查看:161
本文介绍了为什么允许将一个指针转换为引用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最初是这个问题的主题,发现OP只是忽略了引用。同时,此答案让我和其他人思考 - 为什么是否允许使用C风格的转型或 reinterpret_cast

来投射指针指向

  int main(){
char c ='A';
char * pc =& c;

char& c1 =(char&)pc;
char& c2 = reinterpret_cast< char&>(pc);
}

上面的代码编译时没有任何警告或错误Studio,而GCC只会向您发出警告,如此处所示。




$我的第一个想法是,该指针以某种方式被自动地取消引用(我正常使用MSVC,所以我没有得到GCC显示的警告),并尝试以下:

  #include< iostream> 

int main(){
char c ='A';
char * pc =& c;

char& c1 =(char&)pc;
std :: cout<< * pc < \\\
;

c1 ='B';
std :: cout<< * pc < \\\
;
}

显示非常有趣的输出这里。所以看来你访问指向的变量,但同时,你不是。



想法?说明?标准报价?

解决方案

这是 reinterpret_cast 的目的!顾名思义,该转换的目的是将内存区域重新解释为另一种类型的值。因此,使用 reinterpret_cast ,您总是可以将一个类型的左值转换为另一个类型的引用。



这在语言规范的5.2.10 / 10中有所描述。它还说, reinterpret_cast< T&>(x)是与 * reinterpret_cast< T *>(& x)



在这种情况下,您正在投射指针这一事实是完全不重要的。不,指针不会被自动解引用(考虑到 * reinterpret_cast< T *>(& x)解释),甚至可以说相反是真的:该指针的地址被自动采用)。在这种情况下,指针仅作为占据存储器中的一些区域的一些变量。该变量的类型没有什么区别。它可以是 double ,指针, int 或任何其他lvalue。



对于C风格的转换 - 它只是被解释为 reinterpret_cast 在此上下文中,因此上述内容会立即适用于它。



c $ c> c 到指针变量 pc 占用的内存。当你执行 c ='B'时,你强制将值'B'写入该内存,原始指针值(通过覆盖该值的一个字节)。现在被破坏的指针指向一些不可预测的位置。后来你试图取消引用那个销毁的指针。在这种情况下发生的是纯粹的运气问题。程序可能会崩溃,因为指针通常是不可引用的。或者你可能会幸运,并使你的指针指向一些不可预测但有效的位置。在这种情况下,程序将输出一些东西。



可以将第二个程序重写为一个等价的程序,不需要引用

  int main(){
char * pc = new char('A');
char * c =(char *)& pc;
std :: cout<< * pc < \\\
;
* c ='B';
std :: cout<< * pc < \\\
;
}



从实际的角度看,在小端平台上,覆盖指针的最低有效字节。这样的修改不会使指针指向太远离其原始位置。所以,代码更可能打印东西,而不是崩溃。在一个big-endian平台上,你的代码会破坏指针的最高有效字节,因此肆无忌惮地指向一个完全不同的位置,从而使你的程序更容易崩溃。


Originally being the topic of this question, it emerged that the OP just overlooked the dereference. Meanwhile, this answer got me and some others thinking - why is it allowed to cast a pointer to a reference with a C-style cast or reinterpret_cast?

int main() {
    char  c  = 'A';
    char* pc = &c;

    char& c1 = (char&)pc;
    char& c2 = reinterpret_cast<char&>(pc);
}

The above code compiles without any warning or error (regarding the cast) on Visual Studio while GCC will only give you a warning, as shown here.


My first thought was that the pointer somehow automagically gets dereferenced (I work with MSVC normally, so I didn't get the warning GCC shows), and tried the following:

#include <iostream>

int main() {
    char  c  = 'A';
    char* pc = &c;

    char& c1 = (char&)pc;
    std::cout << *pc << "\n";

    c1 = 'B';
    std::cout << *pc << "\n";
}

With the very interesting output shown here. So it seems that you are accessing the pointed-to variable, but at the same time, you are not.

Ideas? Explanations? Standard quotes?

解决方案

Well, that's the purpose of reinterpret_cast! As the name suggests, the purpose of that cast is to reinterpret a memory region as a value of another type. For this reason, using reinterpret_cast you can always cast an lvalue of one type to a reference of another type.

This is described in 5.2.10/10 of the language specification. It also says there that reinterpret_cast<T&>(x) is the same thing as *reinterpret_cast<T*>(&x).

The fact that you are casting a pointer in this case is totally and completely unimportant. No, the pointer does not get automatically dereferenced (taking into account the *reinterpret_cast<T*>(&x) interpretation, one might even say that the opposite is true: the address of that pointer is automatically taken). The pointer in this case serves as just "some variable that occupies some region in memory". The type of that variable makes no difference whatsoever. It can be a double, a pointer, an int or any other lvalue. The variable is simply treated as memory region that you reinterpret as another type.

As for the C-style cast - it just gets interpreted as reinterpret_cast in this context, so the above immediately applies to it.

In your second example you attached reference c to the memory occupied by pointer variable pc. When you did c = 'B', you forcefully wrote the value 'B' into that memory, thus completely destroying the original pointer value (by overwriting one byte of that value). Now the destroyed pointer points to some unpredictable location. Later you tried to dereference that destroyed pointer. What happens in such case is a matter of pure luck. The program might crash, since the pointer is generally non-defererencable. Or you might get lucky and make your pointer to point to some unpredictable yet valid location. In that case you program will output something. No one knows what it will output and there's no meaning in it whatsoever.

One can rewrite your second program into an equivalent program without references

int main(){
    char* pc = new char('A');
    char* c = (char *) &pc;
    std::cout << *pc << "\n";
    *c = 'B';
    std::cout << *pc << "\n";
}

From the practical point of view, on a little-endian platform your code would overwrite the least-significant byte of the pointer. Such a modification will not make the pointer to point too far away from its original location. So, the code is more likely to print something instead of crashing. On a big-endian platform your code would destroy the most-significant byte of the pointer, thus throwing it wildly to point to a totally different location, thus making your program more likely to crash.

这篇关于为什么允许将一个指针转换为引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆