1. 赋值规则
赋值是将一个变量赋值给另一个变量。赋值时遵循以下规则:
1.1 赋值给状态变量
赋值给状态变量,总是值传递。
- 从状态状态赋值给状态变量总是创建一个无关的拷贝。
pragma solidity ^0.4.0;
contract StateToState{
uint8[] arr1;
uint8[] arr2;
uint8 i1;
uint8 i2;
function f() returns (uint8, uint8, uint8, uint8){
arr1 = new uint8[](2);
arr1[0] = 100;
arr2 = arr1;
arr1[0] = 150;
i1 = 200;
i2 = i1;
i1 = 250;
return (arr1[0], arr2[0], i1, i2);//150, 100, 250, 200
}
}
上面的例子中,将arr2
赋值为arr1
后,修改arr1
的第一个元素的值为150
,并未影响到arr2
。对于基本类型也是如此,通过i2 = i1;
赋值后,修改i1
完全不影响i2
。
由于赋值时是值传递,总是创建一份拷贝,上述代码的执行结果是150
,100
,250
,200
。
- 从局部变量赋值到状态变量也总是创建一个无关的拷贝。
pragma solidity ^0.4.0;
contract LocalToState{
uint8[] stateVal;
uint8 stateI;
function f() returns(uint8, uint8, uint8, uint8){
uint8[] memory arr = new uint8[](3);
stateVal = arr;
arr[0] = 100;
var i = 200;
stateI = i;
i = 250;
return (arr[0], stateVal[0], i, stateI);//100, 0, 250, 200
}
}
在上面的例子中,当局部变量赋值给状态变量后,其后对局部变量的更改都不会影响到状态变量。
1.2 赋值给本地变量
- 从状态变量到局部变量,其中基本类型是创建一份无关的拷贝。但对于引用类型,如结构体,数组(包含
bytes
和string
)则是传递的引用。
pragma solidity ^0.4.0;
contract StateToLocal{
uint8[] stateArr;
uint8 stateI;
function f() returns(uint8, uint8, uint8, uint8){
stateArr = new uint8[](2);
stateArr[0] = 100;
uint8[] arr = stateArr;
arr[0] = 150;
stateI = 200;
uint8 i = stateI;
i = 250;
return (arr[0], stateArr[0], i, stateI);//150, 150, 250, 200
}
}
上面的例子中,状态状态通过uint8 i = stateI;
赋值给局部变量后,对i
的修改,并不影响状态变量。而对于引用类型则是引用传递,使用arr[0] = 150
赋新值后,会影响到状态变量stateArr[0]
。
所以上述代码的运行结果是150
,150
,250
,200
。
- 而对于本地变量到本地变量,基本类型是值传递,总是拷贝一个完全无关的对象。而对于复杂类型,如结构体和数组,则是引用传递。
pragma solidity ^0.4.0;
contract LocalToLocal{
function f() returns (uint, uint, uint, uint){
uint a = 100;
uint b = a;
a = 150;
uint[] memory arr1 = new uint[](2);
uint[] memory arr2 = new uint[](2);
arr1[0] = uint(200);
arr2[0] = uint(200);
arr2 = arr1;
arr1[0] = 250;
return (a, b, arr1[0], arr2[0]);//150,100,250,250
}
}
上面的例子告诉我们,对于基本类型通过b = a;
赋值后,对a
的修改并不会影响b
。而对于引用类型arr2
,通过arr2 = arr1;
,修改arr1[0] = 250;
,会影响到arr2[0]
。
所以上述代码的执行结果是150
,100
,250
,250
。
2 元组的解构赋值
Solidity还允许元组解构赋值。它是指一个长度在编译期知道的,不同类型的对象列表,即元组,可以同时赋值给多个变量1,这其实与Javascript里提出的解构赋值的概念类似,但相比Javascript提供的特性只是小巫见大巫。
Solidity的元组2,是由括号包含的,以英文逗号分隔的一个或多个值,如(var1, var2, var3)
。一个元组对象,同时赋值给多个变量,这就是解构赋值。下面我们来看一个元组解构赋值的例子。
pragma solidity ^0.4.0;
contract TupleAssign{
function f() returns(uint8, string, uint8[]){
var (a, b, c) = (1, "Hello world", new uint8[](1));
return (a, b, c);
}
}
我们可以看到,等号两边的对象数量相等时,右边的值会一一赋值到左边的变量。
并且Solidity并不允许我们,左右两边的数量不相等。如果变量过多,会报错Not enough components in value to assign all variables
;而如果值过多,会报错Too many components in value for variable assignment.
。
不过Solidity其实支持变量与值非一一对应。
pragma solidity ^0.4.0;
contract TupleIgnore{
function f() returns(uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8){
//按前对齐赋值
var (a, b, ) = (1, 2, 3, 4);//1, 2
//跳过一些值
var (c, , d) = (4, 5, 6);//4, 6
//后对齐赋值
var (,e) = (7, 8, 9);//9
var (, f, , g) = (10, 11, 12, 13, 14);//12, 14
//特殊的单元组
var (h) = (15);
//Stack too deep, try removing local variables.
//var(, i, ) = (16, 17, 18);
return (a, b, c, d, e, f, g, h);
}
}
通过上面的例子可以看出,我们可以在变量处以逗号结束,来按前对齐赋值。即使后面有更多值都将被忽略,如var (a, b, ) = (1, 2, 3, 4);
。如果我们想后对齐来进行快速赋值,可以以逗号开头,如var (,e) = (7, 8, 9);
。另外我们可以随时用空白来跳过不感兴趣的值。但是不能像var(, i, ) = (16, 17, 18);
那样前后都用逗号分隔,会报错Stack too deep, try removing local variables
。
2.1 解构赋值的应用场景
2.1.1 交换变量
解构赋值可以用来做什么呢,一个最简单的实践场景是交换两个变量值,下面是一个使用元组交换对象值的例子。
pragma solidity ^0.4.0;
contract TupleExchange{
function f() returns (uint8, uint8){
var a = 1;
var b = 2;
(a, b) = (b, a);
return (a, b);//2,1
}
}
上面的例子返回的结果将为2
, 1
,实现了变量的值交换。
2.1.2 函数返回值
使用元组,函数可以同时返回多个值,所以我们可以使用元组的解构赋值来方便的取出元组值。
pragma solidity ^0.4.0;
contract FunctionTuple{
string public recevier;
uint8 public recevierAge;
function f() private returns(string, uint8){
string memory name = "jack";
uint8 age = 12;
return (name, age);
}
function g(){
//更新其它值
(recevier, recevierAge) = f();
}
}
上面的例子中,函数的返回元组值,被解构赋值给了状态变量recevier
和recevierAge
。
关于作者
专注基于以太坊(Ethereum)的相关区块链(Blockchain)技术,了解以太坊,Solidity,Truffle,web3.js。
个人博客: http://tryblockchain.org
版权所有,转载注明出处
参考资料
处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。