当前位置: > 华清远见教育科技集团 > 嵌入式学习 > 讲师博文 > 左值、右值和序列点
左值、右值和序列点
时间:2016-12-12作者:华清远见

先对三个概念作出说明,左值为能出现在赋值符左边的值,右值为能出现在赋值符右边的值,序列点为时间点。

这些概念容易被忽视,这是由于遇到这种问题的概率比较低,但只要你是玩C的程序员迟早都会遇到,本文分成两部分,第一部分说明左值和右值,第二部分说明序列点。

左值和右值,在赋值表达式中左值意味着一个可写入的可寻址的合法的存储空间,右值意味着一个值,总的来说,就是把右值的值保存到左值的存储空间中。是时候举个例子了,如下:

1. int a = 10;
        2. a = a+5;

第1行为申请可以写入的合法的存储空间,并且初始化为10,第2行为右值a+5的值 15赋值给左值a,现在对上面的例子做个简单的修改,如下:

3. int a = 10;
        4. a+5 = 15;

修改后的例子编译时会出现错误,错误提示是:error: lvalue required as left operand of assignment,提示的意思是a+5不能为左值,那这是为什么呢?我们现在分析一下,程序中a+5的结果肯定要保存到一个存储空间,但这个存储空间是不可寻址的,所以在编译的时候直接报错。那到底什么可以作为左值呢?根据左值的定义它应该是可以写入的可寻址的合法的存储空间,在C中符合这个要求的只有3种用法,它们分别是普通变量、数组下表引用和指针间接访问,上面的例子是普通变量,其他两种举例如下:

5. int a[64];
        6. a[0] = 10;

7. int a, *p = &a;
        8. *p = 10;

只有这3种用法才能做为左值,其他的所有用法都不能,至于有人说还可以是结构体中 用到的访问结构成员"."和访问结构指针成员"->",这2种本质上他们访问的是结构体成员,这些结构体成员其实逃不出上面的3种用法。下面举一些不能作为左值的例子,如下:

9. int a = 1;
        10. a++ = 10;

11. int a = 10;
        12. 20 = a;

13. int a[64] = {};
        14. a[0]++ = 10;

例子有很多,就不一一举例了,只要看懂上面的原因分析,就没有问题。

序列点,它的本质是编译器在编译程序的时候的一些时间点,为了避免副作用的发生, 也就是歧义,如在表达式计算完毕之后或在||、&&、?:或逗号运算符之处或在函数调用之前。可能大家现在还不知所云,现在我们直接以例子的形式开始,如下:

15. int a = 0;
        16. a = a++;

这个程序在编译的时候会提示警告:warning: operation on `a` may be undefined,意思 是未定义,在C标准里未定义的意思是可能是任何情况。也就是说不同的编译器会有不通 的解释,可能解释为a的值为0,也可能是1,我们的gcc 4.4.5解释为1。同样还有其他例 子如下:

17. int a[64] = {}, i = 0;
        18. a[i] = i++;

上面的例子会出现同样的警告。那这又是怎么回事呢?这就需要用到序列点的概念了, C标准规定在两个序列点之间不能对同一个存储空间赋值两次,同时要想访问(访问的意思是取它存的值)存储空间必须在它没被赋值之前,一旦被赋值就不能进行访问。对于上面第1个例子,a++的本质是a = a+1,a被赋值了,然后就不能赋值给a了。对于上面第2个例子,i++的本事是i = i+1,也就是说i已经被赋值,所以a[i]中的i就不能被访问了。

根据上面的总结,我们在分析一个例子,如下:

19. int a = 0;
        20. a = a+1;

上面的例子首先没有两次赋值,同时在赋值前进行访问。所以没有违反上面所说的C 标准。

如果大家不乱用C语言规则,左值、右值和序列点的问题是不会遇到的,这也就是学习C语言的时候我们应该只学正确的,不学不正确的,知道不正确的不一定是好事,所以建议大家不要看什么C语言漏洞的书,如果遇到自己解释不了的东西,就百度一下。

发表评论
评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)