绑定成员函数表达式上的非法操作_C|表达式的副作用和函数传参与返回的副本机制...

1 表达式的副作用

在表达式的求值过程中不但要提取变量的值,还可能改变变量的值。

如:k=m++

表达式能产生副作用的原因:引入了具有副作用的操作。

表达式作为语句使用时,它的功能通过副作用来体现。因此把没有副作用的表达式作为语句使用是无意义的。

如:x+=5;(有意义)

k+1;(无意义)

复杂表达式序列点是一个时间点(在整个表达式全部计算完毕之后或在||、&&、?:或逗号运算符处,或在函数调用之前,此刻尘埃落定,所有的副作用都己确保结束。

在上一个和下一个序列点之间,一个对象所保存的值至多只能被表达式的计算修改一次。而且前一个值只能用于决定将要保存的值。

一个表达式中如果某个对象需要写入,則在同一表达式中对该对象的访问应该只局限于直接用于计算将要写入的值。例如i = i+1合法,而a[i] = i++則非法。i左边的引用不确定,i++这样的表达式有一个副作用,会修改自身的值。由于i在同一表达式的其它地方被引用,这会导致无定义的结果。无从判断该引用(左边的a[i])是旧值还是新值。尽管在K&R中建议这类表达式的行为不确定,但C标准却强烈声明它是无定义的。

包含多个不确定的副作用的代码的行为总是被认为末定义。(简单而言,“多个不确定副作用”是指在同一个表达式中使用导致同一对象修改两次或修改以后又被引用的自增、自减和赋值操作符的任何组合。

1.1 循环初始化和循环参数调整都应当是具有副作用的表达式,其中循环参数调整应当能够影响循环条件。

1.2 作为语句的函数调用的功能由函数的副作用体现。

1.3 作用于变量的操作只能施加于变量对象。

1.4 当若干个作用于变量的操作施加于同一变量时,除了最后一个外,不得有后增1或后减1操作。

1.5 通常,应该尽量写单一功能的表达式,如果为了精减写成复合功能的表达式,要注意其多重副作用,如:

int a = *pMove++;

其实该表达式写成单一功能的表达应该是两句:

int a = *pMove;

pMove++;

数值表达式副作用的有关结论,对于指针表达式同样适用。

2 函数传参与返回的副本机制

2.1 函数传参与结合

函数参数传值都会在栈上有一个值的内存空间的副本,用于保证被调函数和主调函数之间的独立性,而传址(包括引用),内存副本只是一个内存地址的副本;指向函数体栈帧外一段空间的首地址。

数组没有副本机制,传递的只是内存块首地址,也就是数组名。

函数参数的传址通过副本机制体现,函数参数的传址通过副作用体现。

如下面代码:

int a=3;int b=5;int c=a+b;a++;

第三句右值表达式可以提炼成一个函数,取的只是a、b的值,对上下文(或内存单元的更新)没有任何影响,所以可以通过函数参数传值的方式实现,函数参数使用副本机制,主调函数和被被函数各自的栈帧相互独立。

int adder(int a, int b){     return a+b;}int c=adder(a,b);

第四句有修改内存变量的值,在前后代码中有上下文关系,如果需要将此句提取成一个函数,需要通过传址的方式将副作用体现出来。

void addOne(int* a){    a++;}addOne(&a);

2.2 函数返回

函数返回时,对于基本数据类型,会保存到寄存器,对于复合数据类型,主调函数会临时开辟一片空间用于保存返回值。

返回的可以是值,也可以是地址,前者可以构成右值表达式的一部分,后者可以作为左值存在。

默认拷贝构造函数按成员逐项进行复制可能导致对象的浅拷贝,这时副本对象的指针数据成员与原始对象的指针数据成员指向相同的内存块。深拷贝是指对象的副本与原始对象没有共享的内存块。默认赋值运算符成员函数只提供按成员逐项进行的复制。对于移动语义,也就是为了避免深拷贝产生的副本机制而导致的效率低下的问题。

编译器必须在编译期间对constexpr函数求值,函数也不允许有任何副作用。

声明一个volatile变量会要求编译器在每次使用该变量时都会获取它的一个新的副本。而不用通过该变量的值保存在一个寄存器中并复用它来优化程序。

3 短路表达式中的副作用

只有表达式的第一个参数不足以确定表达式的值时,程序才会执行接下来的参数。例如,AND运算的第一个参数是false时,可以断定整个表达式为false,OR运算的第一个参数为true时,可以断定整个表达式为true。此时,AND或者OR运算的后续参数都不会被执行,看起来表达式就像被短路了一样。

if( (i=0) && (i=3) )//if( (i=1) || (j=3) )

短路以后,布尔运算符后面的表达式的副作用体现不出来。

对于布尔运算,异或表达式不会,因为需要确定两个操作数才能确定结果。

56c1b387ce3095c6a5fc212ba6a0bea6.png

-End-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值