美文网首页
iOS中有关C++左值右值的一些理解

iOS中有关C++左值右值的一些理解

作者: Frankxp | 来源:发表于2020-03-07 23:56 被阅读0次

OC初级赋值错误

还记得我们在修改一个view的size的时候经常是这么写的:

CGRect frame = self.view.frame;
frame.size.width = 100;
self.view.frame = frame;

为什么不直接赋值呢?像这样呢

self.view.frame.size.width = 100;

可知会报以下错误

Expression is not assignable

在gcc下会抛出以下错误:

 error: lvalue required as left operand of assignment

有没有仔细想过为什么?lvalue是什么东东

左值右值

C/C++编程中不是经常出现术语lvalue(左值)和rvalue(右值),但是一旦出现,它们的语意就不是特别清晰,最经常看到它们的地方是在编译错误和警告信息中。比如,用gcc编译下面的程序:

int userCount() { return 2; }

int main()
{
    userCount() = 2;
    return 0;
}

你会得到

test.c: In function 'main':
test.c:8:5: error: lvalue required as left operand of assignment

是的,这段代码不是合法的并且不是你想写的,但是那个错误信息提到了lvalue,用gcc编译下面的代码:

int& foo()
{
    return 2;
}

现在那个错误为:

testcpp.cpp: Infunction 'int& foo()':
testcpp.cpp:5:12: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'

再次,那个错误信息提到了难以理解的rvalue。那么在C/C++中lvalue和rvalue意味着什么?

简单定义

lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。可以被赋值修改等。
rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式。
譬如:

int var;
var = 4;

赋值运算符要求一个lvalue作为它的左操作数,当然var是一个左值,因为它是一个占确定内存空间的对象。另一方面,下面的代码是无效的:

4 = var;        //ERROR!
(var + 10) = 4; //ERROR!

常量4和表达式var+10都不是lvalue(它们是rvalue)。它们不是lvalue,因为都是表达式的临时结果,没有确定的内存空间(换句话说,它们只是计算的周期驻留在临时的寄存器中)。因此给它们赋值没有语意-这里没有地方给它们赋值。
因此现在应该清楚了第一个代码片段的错误信息。userCount返回一个临时的rvalue。尝试给它赋值,userCount()=2,是一个错误;编译器期待在赋值运算符的左部分看到一个lvalue。
不是所有的对函数调用结果赋值都是无效的。比如,C++的引用(reference)让这成为可能:

int globalvar = 20;

int& foo()
{
    return globalvar;
}

int main()
{
    foo() = 10;
    return 0;
}

这里foo返回一个引用,这是一个左值,所以它可以被赋值,当然我们也可以将函数返回值改为指针是同样的效果。实际上,C++从函数中返回左值的能力对于实现一些重载运算符时很重要的。一个普遍的例子是在类中为实现某种查找访问而重载中括号运算符 []。std::map可以这样做。

std::map<int, float> mymap;
mymap[10]=5.6;

给 mymap[10] 赋值是合法的因为非const的重载运算符 std::map::operator[] 返回一个可以被赋值的引用。
以上是我对C/C++左值右值一方面的粗浅理解,更多的其它新增释义和特性可以参考:Understanding lvalues and rvalues in C and C++
通过以上理解我们来回头看下那个错误,首先要搞清一点,OC的点语法只是一种语法糖,本质还是转换成runtime对应的c/c++函数调用,当然对于一些结构体等点调用除外。
self.view.frame是OC语法,这句话会被转换成:

[[self view] frame]

而frame属性是一个CGRect结构,所以frame.size.width是C语言的语法,就是访问CGRect结构中的size字段,同样,width是CGSize结构的一个字段。所以,你这句话实际上等于:

[[self view] frame].size.width = 100f;

也就是相当于C/C++下的:

getframe().size.width = 100f;

而在C语言里,函数的返回值CGRect是一个R-Value,是不能直接给它赋值的,因此,会报错误。
所以,解决办法就是,用一个临时变量保存这个函数的返回值,修改这个临时变量,然后再赋给frame:

CGRect frame = self.view.frame;
frame.size.width = 100;
self.view.frame = frame;

对比总结

@property (nonatomic) CGRect cframe;
@property (nonatomic, strong) PXobj *pxobj;
@property (nonatomic, strong) NSMutableArray *testArr;

分别CGRect、NSObject、数组等三个类型,我们分别赋值:

self.cframe.size.width = 100;//报错
self.pxobj.name = @"aaa";//正常编译
self.testArr[0] = @"3";//正常编译

对于第一个报错,就不在说了以上已着重解释,对于第二种其实在开始的左值右值介绍里面类似引用/指针返回值类型,self.pxobj对应调用的get方法返回的是PXobj对象指针,指针和引用都是可以隐性修改对应的值的,所以是lvalue,当然可以赋值,同理self.testArr[0],经过重载运算符[]返回的也是一个指针引用,也是可以修改对应的值,所以是不会报错的。

相关文章

  • C++11之move语义

    要理解c++11的move语义,就需要理解C++中的左值和右值和临时对象的概念。 左值与右值和临时对象的简单介绍:...

  • [进阶]C++:算数运算

    左值和右值 理解这两个意思对后面的内容非常重要。C++的表达式不是左值,就是右值。在C++中可以归纳为:当一个对象...

  • 左值右值引用和移动构造

    左值与右值 左值引用右值引用 C++ 11中用&表示左值引用,用&&表示右值引用 (move函数可以把一个) 进...

  • 右值引用

    参考资料 谈谈 C++ 中的右值引用C++11 左值、右值、右值引用详解

  • C++11那些难事:左值引用、右值引用与完美转发

    上一篇C++那些难事:左值与右值搞明白左值与右值,下面讲解左值引用与右值引用。 1. 左值引用与右值引用 左值右值...

  • 理解c++中左值、右值

    说明 c/c++程序员肯定都知道左值、右值,理解好左值、右值有利于我们更好的使用c++语言。下面是我的学习笔记 概...

  • C++11右值引用、移动语义和完美转发

    左值、右值 在C++中,所有的值不是左值,就是右值。左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束后...

  • iOS中有关C++左值右值的一些理解

    OC初级赋值错误 还记得我们在修改一个view的size的时候经常是这么写的: 为什么不直接赋值呢?像这样呢 可知...

  • C++ 左值与右值 右值引用 引用折叠 => 完美转发

    左值与右值 什么是左值?什么是右值? 在C++里没有明确定义。看了几个版本,有名字的是左值,没名字的是右值。能被&...

  • C++中的左值和右值

    在C/C++中,左值(lvalue)和右值(rvalue)是用于规定表达式(expression)的性质。C++中...

网友评论

      本文标题:iOS中有关C++左值右值的一些理解

      本文链接:https://www.haomeiwen.com/subject/yxhglhtx.html