为啥字符串文字是左值,而所有其他文字都是右值?

Posted

技术标签:

【中文标题】为啥字符串文字是左值,而所有其他文字都是右值?【英文标题】:Why are string literals l-value while all other literals are r-value?为什么字符串文字是左值,而所有其他文字都是右值? 【发布时间】:2012-04-17 18:30:14 【问题描述】:

C++03 5.1 主要表达式 §2 说:

文字是主要的表达式。它的类型取决于它的形式(2.13)。字符串文字是左值;所有其他文字都是右值。

同样,C99 6.5.1 §4 说:

字符串文字是主要表达式。它是一个左值,其类型详见 6.4.5。

这背后的原理是什么?

据我了解,字符串文字是对象,而所有其他文字都不是。而左值总是指一个对象。

但是问题是为什么字符串文字是对象而所有其他文字都不是?在我看来,这个理由更像是鸡蛋或鸡肉的问题。

我知道这个问题的答案可能与硬件架构有关,而不是 C/C++ 作为编程语言,但我想听听同样的。

【问题讨论】:

左值不是对象。左值是可以出现在赋值左侧的值,例如变量、结构成员和数组元素查找。 (L = 左。) @duskwuff:委员会要求不同。根据 6.3.2.1,“左值是具有对象类型或除 void 之外的不完整类型的表达式;如果左值在评估时未指定对象,则行为未定义。”根据该引文中引用的脚注 (53),左值应被视为“对象定位器值”。 @JohnCalsbeek C++11 '修复',例如alias<T[N]> 现在可以了。如果arrU 的类定义中声明为这样,U .arr 也是数组类型的右值。 顺便说一句,左值的更好近似是“& 运算符的语法有效操作数”。我怀疑定义实际上等同于标准的定义,除非我遗漏了什么...... 更新:这只是近似值。寄存器存储类对象作为& 的操作数无效,但它们是左值。另外,我不清楚为什么将& 应用于指定为对象类型的函数的返回值(可能)是无效的...... 【参考方案1】:

字符串字面量是具有数组类型的字面量,在 C 语言中,数组类型除了作为左值之外无法存在于表达式中。可以将字符串文字指定为具有指向字符串“内容”的指针类型(而不是通常衰减为指针的数组类型),但这会使它们的用处不大;特别是,sizeof 运算符无法应用于它们。

请注意,C99 引入了复合字面量,它也是左值,因此让字面量成为左值不再是一个特殊的例外;它更接近成为常态。

【讨论】:

puts("hello")int 类型的表达式。 "hello" 不是右值。它是一个左值数组,它衰减为指针到char 类型的表达式。 文字不能有数组类型而不是左值,因为数组衰减到指针的工作方式。如果它没有对象类型,就没有它的初始元素的地址可以衰减到。正如我(稍作修改)的回答所述,语言可以设计使得字符串文字最初是指针类型的,没有任何衰减,然后它们就不需要是左值。但这在实践中用处不大。 可能有右值数组类型——例如,如果你有struct x int a[2]; ; struct x foo(void);,那么foo().a 就是一个右值数组。另外,给定struct x bar, quux; 那么(1 ? bar : quux).a 是一个右值数组。 @R.. 你能在下面评论我的回答吗?似乎有一种强烈的观点认为我是不正确的,但我认为这可能是 C 和 C++ 不同的地方。我想在删除答案之前检查一下:)【参考方案2】:

字符串字面量是 数组 - 本质上无法预测大小的对象(即用户定义的并且可能很大的大小)。在一般情况下,除了作为内存中的对象(即lvalues)之外,没有其他方法可以表示此类文字。在 C99 中,这也适用于 复合文字,它们也是 lvalues

任何试图人为地隐藏字符串文字在语言级别是lvalues 的事实都会产生相当多的完全不必要的困难,因为使用指针指向字符串文字的能力以及访问的能力它作为一个数组严重依赖于它的左值性在语言级别是可见的。

同时,标量类型的文字具有固定的编译时大小。同时,这些文字很可能直接嵌入到给定硬件架构上的机器命令中。例如,当您编写类似i = i * 5 + 2 的内容时,文字值52 成为生成的机器代码的显式(甚至隐式)部分。它们不存在,也不需要作为数据存储中的独立位置存在。将值 52 存储在数据存储器中根本没有意义。

还值得注意的是,在许多(如果不是大多数或全部)硬件架构上,浮点文字实际上是作为“隐藏的”lvalues 实现的(即使该语言没有公开它们)。在 x86 等平台上,来自浮点组的机器命令不支持嵌入式立即操作数。这意味着几乎每个浮点文字都必须由编译器存储在数据存储器中(并从中读取)。例如。当你写像i = i * 5.5 + 2.1 这样的东西时,它会被翻译成类似的东西

const double unnamed_double_5_5 = 5.5;
const double unnamed_double_2_1 = 2.1;
i = i * unnamed_double_5_5 + unnamed_double_2_1;

换句话说,floating-point literals 经常在内部变成“非官方”lvalues。然而,语言规范没有尝试公开这个实现细节是完全合理的。在语言层面上,arithmetic literals 更适合 rvalues

【讨论】:

因此源代码中的'x'5 等表达式在编译期间被“吞噬”在可执行文件中并“成为其中的一部分”,而内存为"x" 和@ 保留987654339@ 在运行时,以便它们由可执行文件创建,存储在内存中,但不是可执行文件本身的一部分。我完全没有抓住重点吗? 有趣的事实:x * 2.0 通常会编译为x+x。这确实强调了“隐藏的左值”实际上只是一个 asm 实现细节,而不是基本的,甚至与语言规则无关。更多有趣的事实,但有趣的是要指出。 (尽管 as-if 规则甚至允许编译器修改字符串文字,例如将 printf("hello\n") 转换为 puts("hello")。)【参考方案3】:

我猜最初的动机主要是务实的:一根绳子 文字必须驻留在内存中并具有地址。字符串的类型 字面量是一个数组类型(C 中的char[],C++ 中的char const[]),并且 在大多数情况下,数组类型转换为指针。语言可以 已经找到了其他方法来定义它(例如,字符串文字可能有 以指针类型开头,并带有关于它的特殊规则 指向),但只是使文字成为左值可能是 定义具体需要什么的最简单方法。

【讨论】:

为什么对几乎可以肯定是正确答案的投反对票? 不是我的反对意见。因此,如果我正确理解您的回答,委员会只是接受了可能提出的建议,而没有深入研究这是否是最好的方法,只是当时选择似乎更灵活? 不管有什么价值,C99 标准只是从 C89 标准中提取了文本,而在 C89 标准化过程中,我记得(从阅读会议记录来看,我从未参加过任何实际会议)关于这个的一些小争论,但它从来没有去任何地方。激烈的争论是关于制作字符串文字const @Als 甚至在委员会之前,C 的规范就已经强烈地出于实用考虑,而不是语言理论或更抽象的考虑。从美学上讲,如果所有文字类型都是右值,那会更优雅。实际上,字符串字面量具有数组类型,数组类型的工作方式与其他类型不同,并且使它们成为左值可以使用最少数量的其他特殊规则来排序。 @torek IIRC,区别已经出现在 K&R C(第一版)中,尽管我的副本不方便检查。务实地说,说它们是左值比编写几段特殊规则更容易,这样它们就可以是右值,但仍然可以正常工作。务实地说,说它们是非常量的(但不能修改)比定义特殊的转换规则(如 C++)以避免破坏代码更容易。 K&R 和 C 委员会对事情一直都很务实。【参考方案4】:

C++ 中的lvalue 并不总是引用一个对象。它也可以引用一个函数。此外,lvalues 不必引用对象。它们可能由rvalues 引用,包括数组(在 C++ 和 C 中)。但是,在旧 C89 中,数组到指针的转换不适用于 rvalues 数组。

现在,rvalue 表示没有、有限或即将过期。然而,一个字符串文字存在于整个程序中。

所以string literalslvalues 是完全正确的。

【讨论】:

整型字面量的生命周期如何?如果无法获取他们的地址,怎么会推荐他们? 整数字面量不引用对象,因此无需考虑生命周期。

以上是关于为啥字符串文字是左值,而所有其他文字都是右值?的主要内容,如果未能解决你的问题,请参考以下文章

c++指针笔记一

C语言 啥叫做左值?右值?

指针是左值还是右值?

格式工厂 右值引用

完美转发

左值与右值引用 详解