contract C { // 这里,数据 x 的存储的位置是 storage, // 这有这里,数据位置(data location)是可以省略的。 uint[] x;
// memoryArray 的存储位置是 memory,这就意味着它的生命周期只是在这个函数调用中 functionf(uint[] memory memoryArray) public { x = memoryArray; // 将 memoryArray 拷贝给 x,x 的存储位置是 storage uint[] storage y = x; // y 是一个 local storage,将 storage 赋值给 local storage,属于引用赋值 y[7]; // 将返回 x 中第八个元素 y.pop(); // 通过 y 来修改 x,x 和 y 指向同一块存储区域 delete x; // 清空数组,此时,y 也被清理 // 下述方式,是不可取的。。 // y = memoryArray; // This does not work either, since it would "reset" the pointer, but there // is no sensible location it could point to. // delete y; g(x); // g 在调用时,传递的是 x 的引用,这就意味着在 g 中可以修改 x h(x); // h 在调用时,将拷贝 x 进行参数传递 }
functiong(uint[] storage) internal pure {} functionh(uint[] memory) public pure {} }
这里需要注意的是:The type of the local variable x is uint[] storage, but since storage is not dynamically allocated, it has to be assigned from a state variable before it can be used. 来源
2 引用类型
引用类型有三种,数组,结构体以及mapping。
2.1 数组
我们可以创建一个在编译时就确定的固定(长度)数组,也可以使用动态数组。 固定长度为 k 类型为 T 的数组即 T[k],动态长度的数组可以写成 T[]。 比如说,一个拥有 5 个动态数组的数组,可以写成 uint[][5],需要注意的是,这种写法和有些语言是相反的。在 Solidity 中,X[3] 表示数组中存在三个类型为 X 的元素,即使 X 本身是一个数组。这种使用,和某些语言(比如 C) 是不一样的。
contract TestContract is ReentrancyGuard { // 这种是可行的 mapping (uint256=>address) [] test ; int [] array ; function a()public{ // 这种是不行的,编译不通过 // 报错:"Type mapping(uint256 => address) is only valid in storage because it contains a (nested) mapping." // mapping (uint256=>address) [] memory test ; // 这种操作也是不被允许的 // 报错:"TypeError: Unary operator delete cannot be applied to type int256[] storage pointer" // int [] storage array1 = array; // delete array1; } }
contract C { functionf(uint len) public pure { uint[] memory a = new uint[](7); bytes memory b = newbytes(len); assert(a.length == 7); assert(b.length == len); a[6] = 8; } }
contract C { functionf() public pure returns (uint24[2][4] memory) { uint24[2][4] memory x = [[uint24(0x1), 1], [0xffffff, 2], [uint24(0xff), 3], [uint24(0xffff), 4]]; // The following does not work, because some of the inner arrays are not of the right type. // uint[2][4] memory x = [[0x1, 1], [0xffffff, 2], [0xff, 3], [0xffff, 4]]; return x; } }
// This will not compile. contract C { functionf() public { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] memory x = [uint(1), 3, 4]; } }
functioncontribute(uint campaignID) public payable { Campaign storage c = campaigns[campaignID]; // 创建一个临时的 memory 中的 Funder, // 并将其拷贝给 c.funders[i] // Note that you can also use Funder(msg.sender, msg.value) to initialise. c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); c.amount += msg.value; }
functioncheckGoalReached(uint campaignID) public returns (bool reached) { Campaign storage c = campaigns[campaignID]; if (c.amount < c.fundingGoal) returnfalse; uint amount = c.amount; c.amount = 0; c.beneficiary.transfer(amount); returntrue; } }
// How to use it contract User { // Just a struct holding our data. itmap data; // Apply library functions to the data type. using IterableMappingfor itmap;
// Insert something functioninsert(uint k, uint v) public returns (uint size) { // This calls IterableMapping.insert(data, k, v) data.insert(k, v); // We can still access members of the struct, // but we should take care not to mess with them. return data.size; }
// Computes the sum of all stored data. functionsum() public view returns (uint s) { for ( uint i = data.iterate_start(); data.iterate_valid(i); i = data.iterate_next(i) ) { (, uint value) = data.iterate_get(i); s += value; } } }
3 delete 操作符
delete a 意味着将为 a 赋值一个初始值。 对于整型来说,a 的初始值就是 0。但是,当 a 是数组时,如果是静态数组,其中的每个元素将被初始化;如果是动态数组,数组的长度将归零;delete a[x] 将仅仅删除索引为 x 的数组 item。
对于结构体而言,delete a 意味着 a 中的所有成员将被重置。换句话说,delete a 之后,a 的值就像是声明但是为赋值的状态。