solidity 基本语法 - 引用类型

引用类型

引用类型和值类型相对,在传递时,并不拷贝一份新的数据,而是传递是指向同一份数据的引用。下面看一个例子:

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]);  
    }
}

上述代码输出的结果为:

[
    {
        "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
        }
    }
]

从结果中可以看出,x 和y 指向了同一份数据。solidity中的引用类型包括,动态数组和结构体两种。

存储位置

solidity中对于复杂的类型(array,struct)有一个额外的概念用于定义变量(成员)的存储位置。solidity 中存储位置分别有memorystoragecalldata. storage是持久化的存储,memorycalldata类似是非持久化的存储。

对于不同的类型变量,都拥有一个默认的存储位置,可以通过memory或是storage关键字改变。对于某些类型的变量,solidity会使用一个强制的存储位置,具体的规则如下:

  • 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

复杂类型

动态数组(Arrays)

动态数组是指在运行期间确定元素个数的为主,solidity中的格式为:

T[] array;

动态数组有两个成员:

  • push 向数组追加元素
  • length 获得数组中元素的个数

对于存储在memory中的动态数组,push方法将无法使用,这是由于EVM(solidity虚拟机)在组织不同位置的数据采用了不同的数据结构导致,后续post会详细介绍这些数据结构。

  byte[] storageArray;
  
 function pushToArray(byte value) public {
     storageArray.push(value);
     byte[] memory memoryArray = new byte[](15);
     // memoryArray.push(value); compile error
 }

bytes 和 string是两种特殊的数组,bytes 和 byte[]类似,string 是特殊的bytes类型,但是无法访问push 和length元素。

结构体(struct)

struct 是solidity中用于定义新的数据结构的方法,其语法和c语言中的结构体类似。

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]);
    }
}

分别测试上述四个函数,结果如下:

  • 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
        }
    }
]

从上面的输出结果可以看出,对于存储在不同位置的复杂类型,赋值操作的表现会有所不同,总结如下:

  • storage to storage 拷贝数据, 表现为值类型

  • storage to memory 拷贝数据,表现为值类型

  • memory to storage 拷贝数据,表现为值类型

  • memory to memory 拷贝引用,表现为引用类型

你可能感兴趣的:(solidity 基本语法 - 引用类型)