c - C 编译器如何确定有效的左值?
问题描述
我试图弄清楚 C 如何确定表达式是否是有效的 LVALUE。
我知道声明变量会给它一个命名的内存空间,这是变量名。变量名可以是 RVALUE 或 LVALUE。如果用于表示值,则使用其内容,但如果将其用作 LVALUE,则使用其地址来告知右侧的表达式存储在此地址中。我看到的这个操作的图片是这样ADDRESS=VALUE
的:这就是赋值运算符的左右表达式的评估方式。
那么为什么我不能定义一个变量 like int a;
,然后使用运算符的地址将值存储在该地址中,比如&a = 5;
?
我知道&a
返回一个常量指针,但这意味着我不能更改地址或者我不能更改存储在地址中的值?如果它的内容不能改变,那为什么要使用*&a=5
呢?
为什么我不能以这种方式赋值,尽管左手表达式总是被评估为我理解的地址?也许我的理解有问题?
解决方案
自动左值转换
C 2018 6.3.2.1 2 涵盖了这一点,其中说:
除非是运算符的操作数
sizeof
,一元&
运算符,运算符,++
运算--
符,或运算符的左操作数.
或赋值运算符,将不具有数组类型的左值转换为存储在指定对象中的值(并且不再是左值);这称为左值转换……</p>
考虑表达式x = y + z
:
y
是 的操作数+
。该+
运算符不在上述例外列表中。所以y
转换为它的值。z
是 的操作数+
。该+
运算符不在上述例外列表中。所以z
转换为它的值。x
是 的左操作数=
,即赋值运算符。这在上面的例外列表中。所以x
仍然是一个左值。
关于&a = 5
关于int a;
后跟&a = 5;
:
- 运算符的结果
&
只是一个地址——它只是一个值;没有对象持有这个值,所以它不是左值。 - 赋值运算符必须有一个左值作为其左操作数。C 2018 6.5.16 2 是一个约束,它表示“赋值运算符应有一个可修改的左值作为其左操作数。”</li>
因此&a = 5;
违反了约束,需要 C 编译器为其生成诊断消息。=
运算符不能将纯值作为其左操作数。
可以设计一种编程语言,以便赋值运算符接受&a = 5;
并使用它将右侧的值存储在左侧给定的位置。BLISS 语言就是这样做的。在 BLISS 中,变量的名称总是提供它的地址。要获取该值,您必须在变量前面加上句点(其作用类似于 C 的一元运算*
符)。所以你会写z = .x + .y
. 因此,C 不这样做的事实是关于美学和便利性的选择,而不是关于逻辑必要性的选择。在 C 中,左值在大多数地方会自动转换为值,但对对象而不是值起作用的运算符除外。在 BLISS 中,您必须明确指定每个左值到值的转换。
关于*a = 5
在*&a=5
:
- 运算符根据
*
C 2018 6.5.3.2 4 生成一个左值:“一元运算*
符表示间接。如果操作数指向一个函数,则结果是一个函数指示符;如果它指向一个对象,则结果是一个指定该对象的左值……”</li>
因此*&a
提供了赋值运算符所需的左值。
推荐阅读
- java - 将带有 Println 语句的 void 函数导出到 TextArea(JavaFx)
- wordpress - WordPress:分类档案中按帖子类型分开的帖子
- scala - 作业因阶段失败而中止:阶段 0.0 中的任务 0 失败 1 次,最近一次失败:SPARK 中的 java.io.EOFException
- regex - 正则表达式多行在 regexr 但不是 PowerShell
- c# - botbuilder v 4,动态自适应卡,带有下拉菜单并在提示时捕获值
- c# - 在 Core 2.1 中向现有的 Microsoft.Extensions.Logging.LoggerExtensions 添加方法
- api - 谷歌地理编码 API 返回错误的邮编/城市
- java - 在旋转和阻塞时间短或没有旋转和阻塞时间的安全点操作期间,什么可能导致非常高的同步时间?
- c - 传递参数以更改 #define 变量
- ruby-on-rails - 虾 PDF 导致 heroku rails 应用程序中的内存膨胀