为什么(int*) p = &x;
是无效的声明?
而*(int*) p = &x;
是没有警告的有效语句吗?
我知道强制转换是 rvalue ,但是如何在没有警告的情况下编译第二条语句?
将指针强制转换为左值
yanxianfeng_5188 回答:将指针强制转换为左值
(int*) p = &x;
这是无效的,因为强制转换的结果不是左值。它只是一个表达式,其类型是强制转换中指定的类型。
*(int*) p = &x;
这是有效的,因为取消引用运算符*
的结果是一个左值。取消引用指针可以为您提供指针指向的对象。
从直觉上讲,左值是表示“这是诚实的对象”的表达式,而右值是表示“这不是对象的值”的表达式。 (区别稍微有些暗了,但是现在是一个合理的简化。)也就是说,一个左值是一个可以放入一些东西的盒子,而一个右值只是盒子中的值。
例如,表达式137
是一个右值,因为它是一个纯数字,而不是包含数字的盒子。如果x
是变量,则表达式x
是左值,因为它既表示框又表示其中的数字,而表达式x + 137
是右值,因为它仅表示数字,而不是装有数字的盒子。
如何将其转化为因素?假设x
是int
。那么(float)x
是一个右值,因为它纯粹表示一个值-具体来说,它是“如果您使用x
并做了最糟糕的事情来将其表示为{{1} }。”这不是可以放东西的盒子。
这就是为什么
float
不起作用。 (int *)p = &x;
是一个右值-一个纯值,不是您可以分配给它的值-并且您试图将其视为可以放入某些东西的盒子。
另一方面,指针取消引用的结果是一个左值,因为它表示“在那儿看!这是可以在其中放入东西的盒子”。所以从这个意义上讲,您可以写
(int *)p
因为那意味着
- 计算表达式
*(int *)p = x;
。好的,我们现在有了一个右值,它是指向内存中某个(int *)p
的指针。 - 取消引用该指针以获取
int
。好的,我们现在有一个表示该整数的左值。 - 将
*(int *)p
塞入该位置。很好-x
是一个盒子。
此代码可能还有其他问题:
*(int *)p
在此,此表达式的RHS具有类型*(int *)p = &x;
,它是一个指针。该表达式的LHS的类型为(int *)
(您已将指针解引用为整数),因此要将指针转换为整数。因此,这意味着要么缺少演员表,要么您正在使用此代码执行错误的事情。
第二个问题是
int
表示“将*(int *)p
解释为好像告诉您可以在内存中找到p
的地方,然后在其中写一些东西。”如果int
不是指针,则几乎可以肯定会导致代码崩溃,因为您将在内存中随机写入某处。而且如果p
是一个指针,那么最好是投射p
,而不是&x
?
p
其中p = (T*) &x;
是T
指向的事物的类型。
技术术语有时会令人困惑,尤其是当读者对事物的工作原理不太了解时。
什么是左值?有地址的东西。 在第一种情况下,您有一个指针。在第二种情况下,您将取消引用指针。
什么是指针?指针是一个地址。地址本身没有地址(好吧,我们在这里不要太过专业)。在您的示例中,假设p
是常数1235。它的地址是什么?没有,这只是一个常量,它可能不存在于内存中。也许在寄存器中。也许编译器完全优化了它。
而且,您的表达式是(int*)p = &x
说:“获取地址1235并为其分配x的地址,可能类似于6789。”换句话说,“将地址1235更改为6789”。什么?!更改地址?!
什么是解引用指针?它是指针的内容,即指针指向的地址中的值。现在,根据定义,有地址!这就是第二条语句中发生的事情。就是说“将内容设置在p
指向&x
的地址上”。因此,在我的示例中,它说“将内存地址1235的值设置为6789”。现在这很有意义!这就是为什么它是左值的原因。
我希望这是有道理的。