引用類型
引用類型和值類型相對,在傳遞時具钥,并不拷貝一份新的數(shù)據(jù)氓拼,而是傳遞是指向同一份數(shù)據(jù)的引用桃漾。下面看一個例子:
pragma solidity ^0.4.23;
contract RefTypeTest {
event PrintValue(uint value1, uint value2);
function testRefType() public {
uint[] x;
x.push(1);
x.push(2);
x.push(3);
uint[] y = x;
emit PrintValue( x[0], y[0]);
y[0] = 0;
emit PrintValue( x[0], y[0]);
}
}
上述代碼輸出的結(jié)果為:
[
{
"event": "PrintValue",
"args": {
"0": "1",
"1": "1",
"value1": "1",
"value2": "1",
"length": 2
}
},
{
"event": "PrintValue",
"args": {
"0": "0",
"1": "0",
"value1": "0",
"value2": "0",
"length": 2
}
}
]
從結(jié)果中可以看出撬统,x 和y 指向了同一份數(shù)據(jù)恋追。solidity中的引用類型包括苦囱,動態(tài)數(shù)組和結(jié)構(gòu)體兩種撕彤。
存儲位置
solidity中對于復雜的類型(array羹铅,struct)有一個額外的概念用于定義變量(成員)的存儲位置职员。solidity 中存儲位置分別有memory,storage和calldata. storage是持久化的存儲倒谷,memory和calldata類似是非持久化的存儲渤愁。
對于不同的類型變量诺苹,都擁有一個默認的存儲位置,可以通過memory或是storage關(guān)鍵字改變掌呜。對于某些類型的變量质蕉,solidity會使用一個強制的存儲位置模暗,具體的規(guī)則如下:
- Forced data location:
- parameters (not return) of external functions: calldata
- state variables: storage
- Default data location:
- parameters (also return) of functions: memory
- all other local variables: storage
復雜類型
動態(tài)數(shù)組(Arrays)
動態(tài)數(shù)組是指在運行期間確定元素個數(shù)的為主,solidity中的格式為:
T[] array;
動態(tài)數(shù)組有兩個成員:
- push 向數(shù)組追加元素
- length 獲得數(shù)組中元素的個數(shù)
對于存儲在memory中的動態(tài)數(shù)組隶糕,push方法將無法使用枚驻,這是由于EVM(solidity虛擬機)在組織不同位置的數(shù)據(jù)采用了不同的數(shù)據(jù)結(jié)構(gòu)導致测秸,后續(xù)post會詳細介紹這些數(shù)據(jù)結(jié)構(gòu)铃拇。
byte[] storageArray;
function pushToArray(byte value) public {
storageArray.push(value);
byte[] memory memoryArray = new byte[](15);
// memoryArray.push(value); compile error
}
bytes 和 string是兩種特殊的數(shù)組慷荔,bytes 和 byte[]類似贷岸,string 是特殊的bytes類型偿警,但是無法訪問push 和length元素螟蒸。
結(jié)構(gòu)體(struct)
struct 是solidity中用于定義新的數(shù)據(jù)結(jié)構(gòu)的方法七嫌,其語法和c語言中的結(jié)構(gòu)體類似诵原。
struct Operation {
bytes31 hash;
uint8 status;
}
Operation[] operations;
function addOperation(bytes31 opHash, uint8 status) public {
Operation memory op;
op.hash = opHash;
op.status = status;
operations.push(op);
}
存儲位置對復雜類型的影響
復雜類型存儲在不同位置的變量蔓纠,相互賦值的行為會有差異贺纲,下面通過一個例子來介紹:
pragma solidity ^0.4.23;
contract Contract{
uint[] x; // the data location is storage
uint[] y; // the data location is storage
event PrintUint(uint value1, uint value2);
// Test memory assigin to storage
function m2s(uint[] memoryArray) public {
x = memoryArray; // works, copies the whole array to storage
x[0] = 1;
emit PrintUint(x[0], memoryArray[0]);
}
// Test assigin storage to storage
function s2s() public {
x[0] = 0;
y = x;
y[0] = 1;
emit PrintUint(x[0], y[0]);
}
// Test assigin storage to memory
function s2m() public {
y[0] = 0;
uint[] memory memoryArray = y;
memoryArray[0] = 1;
emit PrintUint(memoryArray[0], y[0]);
}
// Test memoryArray assigin to memoryArray
function m2m(uint[] memoryArray) public {
// default local variable is in storage
// uint[] memoryArray1 = memoryArray;
uint[] memory memoryArray1 = memoryArray;
memoryArray1[0] = 1;
emit PrintUint(memoryArray[0], memoryArray1[0]);
}
}
分別測試上述四個函數(shù),結(jié)果如下:
- m2s
[
{
"event": "PrintUint",
"args": {
"0": "1",
"1": "0",
"value1": "1",
"value2": "0",
"length": 2
}
}
]
- m2m
輸入 [0]
輸出:
[
{
"event": "PrintUint",
"args": {
"0": "1",
"1": "1",
"value1": "1",
"value2": "1",
"length": 2
}
}
]
- s2m
[
{
"event": "PrintUint",
"args": {
"0": "1",
"1": "0",
"value1": "1",
"value2": "0",
"length": 2
}
}
]
- s2s
[
{
"event": "PrintUint",
"args": {
"0": "0",
"1": "1",
"value1": "0",
"value2": "1",
"length": 2
}
}
]
從上面的輸出結(jié)果可以看出,對于存儲在不同位置的復雜類型澄成,賦值操作的表現(xiàn)會有所不同墨状,總結(jié)如下:
storage to storage 拷貝數(shù)據(jù), 表現(xiàn)為值類型
storage to memory 拷貝數(shù)據(jù),表現(xiàn)為值類型
memory to storage 拷貝數(shù)據(jù)镐确,表現(xiàn)為值類型
memory to memory 拷貝引用源葫,表現(xiàn)為引用類型