C语言指针和左值

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。我们就用这个地址来做演示。


值直接存储到想当然的地址里

我们想把一个值存放到一个地址里面去都先想的是这样:

#include<stdio.h>
int main(void)
{
    6356760=77;//我们想把6356760想成是一个地址,而编译器只会认为这是一个常量,所以会报错。
    return 0;
}
把一个值存入我们想当然的地址
把一个值存入我们想当然的地址标题

当然上面这段代码肯定是错误的,因为编译器根本不知道6356764是一个地址,它只会认为这是一个常量。

上面报错信息为:lvalue required as left operand of assignment。翻译过来就是:左值必须作为赋值的左操作数。


值直接存储到指针里

那我们就强制转换为地址,也就是指针类型,代码如下:

#include<stdio.h>
int main(void)
{
    (int*)6356760=77;//这里强制转换成了整型指针类型。
    printf("%d\n",(int*)6356760);//我们想要输出这个地址的值,也就是77。
    return 0;
}
把想当然的地址强制转换为指针
把想当然的地址强制转换为指针

有些朋友可能会认为这样就可以运行了,但其实不然。

它还是会报错:lvalue required as left operand of assignment。翻译过来就是:左值必须作为赋值的左操作数。


值直接存储到指针的解引用里

是不是觉得还差了啥,没错差个解引用,没有这个符号怎么能给内存赋值呢。代码如下:

#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 这个词。一般出现在各种书籍中,更频繁遇到的是在你的错误提示中。

一般你会看到这个错误是因为你的代码类似这么写:

#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&#64257;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)来理解是不全面的。请尝试执行下边代码: 

#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,即任何可读取的值都被认为是右值(非左值)。


欢迎大家来加我自己的编程语言交流QQ群群号:387618421

  • 59
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学编程的闹钟

自愿打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值