通常,当赋值运算符存在时,左操作数应该是变量而不是表达式,但是当我使用指针使左侧成为表达式时,代码不会产生任何错误。
Jdoodle在线编译器/ C
尽管编译成功,它应该抛出一个错误。
https://www.jdoodle.com/c-online-compiler
#include <stdio.h>
int main()
{
int x = 30, *y, *z;
y = &x; // Assume address of x is 1000 and integer is 4 byte size */
z = y;
*y++ = *z++;
x++;
return 0;
}
答案 0 :(得分:2)
赋值的左操作数不必是变量。例如,以下分配应该并且确实可以很好地工作(我假设您知道这一点,并且只是弄错了):
array[index] = value;
*ptr = value;
我认为让*y++ = *z++;
感到困惑的是,您认为它是分配给增量操作的结果,这的确没有任何意义。但这不是该表达式的优先级:*y++
等效于*(y++)
,而不是(*y)++
。因此,您要取消引用y++
的结果,然后将一个值分配给该已取消引用的内存位置,就像您编写了一样:
int *ptr = y++;
*ptr = *z++;
答案 1 :(得分:2)
通常,当赋值运算符存在时,左操作数应 是变量而不是表达式...
赋值运算符的左操作数总是 一个表达式。限制是它必须是 lvalue ,它是(简化一点)指定对象的表达式。
左值最简单的情况是声明的对象/变量的名称,例如:
int n;
n = 42;
n
是一个表达式,特别是一个左值,它指定名为n
的对象。
在您的示例中,您有:
*y++ = *z++;
运算符优先级表示++
运算符的绑定比*
运算符更紧密,因此等效于:
*(y++) = *(z++);
y++
和z++
不是值-它们是产生指针值的表达式,但它们不指定任何对象。但是,一元*
运算符会为您提供左值,即使其操作数不是左值也是如此。 y++
产生一个指针值,*(y++)
为您提供该指针指向的对象。
从这个意义上说,C标准不使用术语“变量”。它讨论了 objects ,它可以被声明为具有简单名称的对象,也可以不被声明为对象。 an_object
,*pointer_value
,array[index]
和structure_object.member
都是对象,它们可以出现在作业的左侧。
答案 2 :(得分:2)
左操作数应该是变量而不是表达式
这是一个误会。您要查找的术语是 lvalue 。这是源自术语“左侧值”的C标准乱码。术语 lvalue 的定义大致是:一个指定对象的表达式。
这里讨论的各种运算符的规则是:
*
运算符的结果被定义为始终是左值,因此您可以像使用它一样使用它,我们可以为其赋值。 示例:
int x; int* y;
x = 5; // Here x is both an object and an lvalue, so the code is valid
y = &x; // y is both a (pointer) object and an lvalue, code is valid.
*y = 0; // *y is an lvalue designating the object y, code is valid
y++ = 1; // y++ is not an lvalue, the code is invalid
关于*y++
起作用的原因,这只是运算符优先级的问题。首先应用++,但是由于它是后缀,因此直到表达式末尾才进行更改。因此,将*
应用于y
,结果是一个左值。
如果您编写了++*y = 0;
,则运算符的关联性导致*
首先执行,结果*y
是左值。然后,在该左值上使用++
时,++的结果不是左值,因此代码无效。 ++*y
本身是有效的代码,但不能是赋值的左操作数。
答案 3 :(得分:1)
假设x
的地址是1000
和sizeof(int) = 4
(对于32-bit
编译器),那么当您对表达式求值时:
*y++=*z++;
z的地址递增
(因为后递增(x ++)的优先级高于(* x))
,但在此之前右值,即z
存储的地址被分配给指针y
。
之后,仅进行地址增加和取消引用。
*y++
的情况类似,首先分配地址,后递增发生,然后被取消引用。如果您对左值是变量感到困惑,则没有必要例如:使用有效地址初始化指针后,在代码中添加类似代码的语句
*y++;
也是正确的,并且不会标记任何错误。