为啥这个转换的结果不是左值?

Posted

技术标签:

【中文标题】为啥这个转换的结果不是左值?【英文标题】:Why isn't the result of this cast an lvalue?为什么这个转换的结果不是左值? 【发布时间】:2014-01-27 16:42:58 【问题描述】:

对于这种奇怪的行为,我需要一些建议——让我们来看看这段代码:

int ** p;

这编译没有任何问题:

p++;

但是这个:

((int**)p)++;

给我这个错误信息:“error: lvalue required as increment operand”

我将 p 转换为它已经是的类型,没有任何变化,那么问题是什么?这是我在尝试编译一个旧版本时遇到的问题的简化版本 gdb 的版本。所以我想,这行得通,并且发生了一些变化。知道第二个例子有什么问题吗?

【问题讨论】:

"什么都没有改变" - 实际上,很多事情都发生了变化,其中之一是强制转换表达式的结果不是左值而是右值(它不指定存储),所以它没有增加它没有意义。 是的。 “如果你这样做的时候很痛,也许你不应该那样做。” 强制转换的结果从来都不是左值。从旧版本的 gdb 中查看 exact 代码会很有趣;我怀疑您的简化隐藏了实际问题。 IIRC 某些版本的 VC++ 允许这样做(可能是 VC++ 7.1)。 我很难想象gbd 实际使用过这样的东西。就像碳酸指出的那样,这完全是错误的。 【参考方案1】:

旧版本的 gcc 支持称为“左值强制转换”的东西——如果你强制转换为左值的东西,结果就是一个左值,可以这样对待。它的主要用途是允许您将指针增加对应于不同大小的数量:

int *p;
++(char *)p;  /* increment p by one byte, resulting in an unaligned pointer */

这个扩展在 gcc v3.0 前后被弃用了一段时间,并在 gcc v4.0 中被删除

要在最新版本的 gcc 中做同样的事情,你需要做一个加法和赋值(而不是增量),将指针转换为加法类型的指针,然后返回赋值:

p = (int *)((char *)p + 1);

请注意,在此之后尝试取消引用指针是未定义的行为,因此不要指望它会做任何有用的事情。

【讨论】:

'旧版本的 gcc 支持所谓的“左值强制转换”'。不,这确实是优化造成的BUG!例如,当pint* 类型时,++(int *)p; 编译时,尝试++(char *)p; 给你一个错误。 @GrijeshChauhan:两者都可以在 4.0 之前的 gcc 版本上正常工作。你的 link 使用 gcc 4.1,所以还不够老。 没有第一个不是错误,而第二个是Check this - 第一个有效,因为++(int*)p; 由于优化转换为++p;++(char*)p; 没有。我在过去一段时间观察过这种行为。看到这个++( i | i)将被编译,编译++(int*)p;【参考方案2】:

当您对表达式进行类型转换时,该表达式的结果是右值而不是左值。直观地说,类型转换说“给我这个表达式如果它有其他类型的”,所以将一个变量类型转换成它自己的类型仍然会产生一个右值而不是一个左值。因此,将++ 运算符应用于类型转换的结果是不合法的,因为++ 需要一个左值,而您提供的是一个右值。

也就是说,原则上可以重新定义 C 语言,以便如果原始表达式是左值,则将值转换为自己的类型会产生左值,但为了简单和一致性,我想语言设计者没有这样做。

希望这会有所帮助!

【讨论】:

这个词是“cast”,而不是“typecast”。 (类型转换是发生在演员身上的事情。) @KeithThompson 我已经看到“类型转换”这个术语被广泛用于这个术语,“转换”只是它的一个短期术语。 Wikipedia 似乎也支持这一点。 术语“类型转换”在 K&R2 或 ISO C 标准中没有出现(在***文章中只有一次,该文章继续将显式 C 类型转换称为“转换”)。跨度> 顺便说一句,我刚刚编辑了那个***页面,但我没有触及“类型转换”的引用。 @KeithThompson 我怀疑在 C/C++ 用语中,该术语是“cast”,而在更广泛的 CS 上下文中,该术语是“typecast”。这可以解释事情。 :-)【参考方案3】:

在 C 语言中,所有转换(包括显式转换)总是产生右值。没有例外。您将其转换为相同类型的事实并不能使其不受该规则的约束。 (实际上,期望它做出如此不一致的异常会很奇怪。)

事实上,整个 C 语言的基本特性之一是它总是尽可能快地将表达式中的左值转换为右值。 C 表达式中的左值就像门捷列夫表的第 115 个元素:它们的寿命通常很短,很快衰减为右值。这是 C 和 C++ 之间的主要区别,后者总是试图尽可能长时间地保留表达式中的左值(尽管在 C++ 中这种特定的强制转换也会产生右值)。

【讨论】:

那么如果你想转换一些东西然后将它用作左值,有什么办法吗?我可以考虑将右值分配给一个适当声明的 pointer - 这是一个好方法,还有更好的方法吗? @PhilPerry:由于我们不知道您的实际目标是什么,因此很难告诉您如何更好地实现它。您可能不是为了自己的缘故而试图从演员表中获取左值;你有更大的目标。这个目的不会通过强制转换和期望得到一个左值来实现,所以不要继续走这条错误的道路,描述你的真正的问题并寻求帮助来解决它。 @Phil Perry:将某物用作不同类型的左值并不是真正的类型转换,而是原始内存重新解释。在 C 中,原始内存重新解释是通过向上一级间接实现(使用& 运算符),在那里执行转换,然后使用* 运算符返回到原始级别。在 OP 的情况下,它看起来如下:(*(int ***) &p)++。当然,这个例子有纯粹的学术价值,因为p已经是int **了。 @Eric,我手头没有具体的问题要解决。只是@AndreyT 的回答引发了这样一种想法,即在未来的某个时候,我可能想要转换,比如说,一个指向其他东西的指针,并将其用作左值,以及如何最好地做到这一点?比如说,我有一个void * 数组,我想递增到下一个整数,或者类似的东西。 @Phil Perry:如果你有一个void *p,并且你想将它转移到下一个int,最好的方法是完全避免记忆重新解释并使用p = (int *) p + 1。通过记忆重新解释,它看起来像++(*(int **) &p),但它不如前一个。【参考方案4】:

为什么这个转换的结果不是左值?

我提请您注意 C99 规范的第 6.5.4 节,第 4 行,脚注 86,其中指出:

强制转换不会产生左值。

你有一个演员表。

结果不是左值。

++ 运算符需要一个左值。

因此你的程序是错误的。

【讨论】:

Eric 这是我添加的旧编译器中的一个错误a comment to question

以上是关于为啥这个转换的结果不是左值?的主要内容,如果未能解决你的问题,请参考以下文章

应用间接时,标准是不是要求指针变量的左值到右值转换?

为啥非 const 引用必须用左值初始化?

为啥数组不是左值? [复制]

将文字转换为左值

避免左值转换警告和错误的批准方法?

无序映射的左值到右值转换