本帖最后由 学编程的闹钟 于 2024-3-8 14:49 编辑
首先我们要知道内存是如何存放变量的:通过变量名对变量进行访问和存储是为了方便程序员而设计的,其实在内存中完全没有存储变量名的必要。因为编译器知道具体每一个变量名对应的存放地址,所以当你读取某个变量的时候,编译器就会找到变量名所在的地址,并根据变量的类型读取相应范围的数据。通俗的来说就是变量名就是个地址,存这个地址存放的是一个不同类型的值。
我们先通过变量名来访问一个整型变量的地址,代码如下:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
int a=77;//当然这里不被赋值也可以。
printf("%d",&a);//这里在a前面加了个&取地址符,得到的是a的地址。
printf("%p",&a);//这里%p是通过十六进制输出地址,上面一行代码则是十进制。
return 0;
}
上面这段代码十进制的地址是:6356764。十六进制地址是:0060FF1C。有些怀疑的朋友可以通过计算器转换试试哈。每次地址都不会一样,所以最好先通过查找一个int变量的地址,在用那个地址-4得到我们要存放值的地址,这样能最少与当前地址之前的存放的东西冲突,就会报错。
好,接下来我们就用十进制的地址的方法给大家演示。上面我们知道了一个十进制地址:6356764。因为int类型占4字节,所以我们就6356764-4=6356760。我们就用这个地址来做演示。
我们想把一个值存放到一个地址里面去都先想的是这样:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
6356760=77;//我们想把6356760想成是一个地址,而编译器只会认为这是一个常量,所以会报错。
return 0;
}
当然上面这段代码肯定是错误的,因为编译器根本不知道6356764是一个地址,它只会认为这是一个常量。
上面报错信息为:lvalue required as left operand of assignment。翻译过来就是:左值必须作为赋值的左操作数。
那我们就强制转换为地址,也就是指针类型,代码如下:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
(int*)6356760=77;//这里强制转换成了整型指针类型。
printf("%d\n",(int*)6356760);//我们想要输出这个地址的值,也就是77。
return 0;
}
有些朋友可能会认为这样就可以运行了,但其实不然。
它还是会报错:lvalue required as left operand of assignment。翻译过来就是:左值必须作为赋值的左操作数。
是不是觉得还差了啥,没错差个解引用,没有这个符号怎么能给内存赋值呢。代码如下:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
*(int*)6356760=77;//这里强制转换成了整型指针类型,再用取地址符分配内存。
printf("%d\n",*(int*)6356760);//我们想要输出这个地址的值,也就是77。
return 0;
}
这样代码即没报错,也没失败,成功的打印出了77。
所以这里没有变量名来分配地址也能成功分配地址。所以不要死脑筋。不过有时那个地址被占用了,要多多换换地址就行。
上面有两个错误的代码中的报错都是:lvalue required as left operand of assignment。翻译过来就是:左值必须作为赋值的左操作数。
那么到底什么是左值(lvalue)和右值(rvalue)呢?
你会经常看到 lvalue 这个词。一般出现在各种书籍中,更频繁遇到的是在你的错误提示中。
一般你会看到这个错误是因为你的代码类似这么写:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
int i;
5=i;
return 0;
}
一些朋友可能想当然的就觉得左值(lvalue)指的就是赋值运算符左边的那个值,而右值(rvalue)当然就是右边那个值啦。所以我们叫它们为“左值”和“右值”。
事实上这样理解并不全面,并且常常容易犯思想上的错误。
首先,我们找到 C 语言的作者问问究竟,他是这么说的:
《The C Programming Language》翻译:《C程序设计语言》
"An object is a manipulatable region of storage; an lvalue is an expression referring to an object.
The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression."
上面书中内容翻译为:
对象是可操纵的存储区域;左值是指对象的表达式。
名称“左值”来自赋值表达式E1=E2,其中左操作数E1必须是左值表达式
因为C语言是在不断发展的,毕竟老头的这本书说的是C语言的原型,自1988年第二版之后就没有再出新版了。而C的标准则经历了 K&RC,C89,C90,C99,C11 的迭代。
那我们找来了目前最广泛使用的 C99 标准:
The name "lvalue" comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modi?able) lvalue. It is perhaps better considered as representing an object "locator value". What is sometimes called "rvalue" is in this International Standard described as the "value of an expression".
上面书中内容翻译为:
“左值”的名称最初来自赋值表达式E1=E2,其中左操作数E1必须是(可调整的、可修改的)左值。它可能更被认为是表示对象“特定位置的值”。有时所谓的“左值”在这个国际标准中被称为“表达式的值”。
所以,单纯用左边(left-value)和右边(right-value)来理解是不全面的。请尝试执行下边代码:
[C++] 纯文本查看 复制代码 #include<stdio.h>
int main(void)
{
int a=5;
++(a++);
return 0;
}
你会得到下边错误提示:lvalue required as increment operand。翻译为:递增操作数需要左值。
在这里如果你认为lvalue只是赋值运算符左边那个值,那这个错误提示就无法理解了。但是如果你知道lvalue是用于识别或定位存储位置的标识符,那么就好解释了:
(a++)是先将变量a的值(5)做为整个表达式的值返回,再将a自增(类似于a=a+1)。
所以这里++(a++);,相当于++(5),a=a+1;
这个当然要报错,5是一个常量,当然不能执行5=5+1。
C 语言的术语lvalue指用于识别或定位一个存储位置的标识符。(注意:左值同时还必须是可改变的)。
除了左值其实还有右值吧,没错,其实rvalue的发明完全是为了搭配lvalue,rvalue你可以理解为readable value,即任何可读取的值都被认为是右值(非左值)。
|