ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 数组 `数组`可以声明时指定长度,或者是变长的。对`storage`的数组来说,元素类型可以是任意的,类型可以是数组,映射类型,数据结构等。但对于`memory`[datalocation]的数组来说。如果函数是对外可见的,那么函数参数不能是映射类型的数组,只能是支持ABI的类型。 一个类型为T,长度为k的数组,可以声明为`T[k]`,而一个变长的数组则声明为`T[]`。<br/> 你还可以声明一个多维数据,如一个类型为`uint`的数组长度为5的变长数组,可以声明为`uint[][5] x`。需要留心的是,相比非区块链语言,多维数组的长度声明是反的。 要访问第三个动态数据的,第二个元素,使用`x[2][1]`。数组的序号是从0开始的,序号顺序与定义相反。 `bytes`和`string`是一种特殊的数组。`bytes`类似`byte[]`,但在外部函数作为参数调用中,会进行压缩打包,更省空间,所以应该尽量使用`bytes`。`string`类似`bytes`,但不提供长度和按序号的访问方式。 由于`bytes`与`string`,可以自由转换,你可以将字符串`s`通过`bytes(s)`转为一个`bytes`。但需要注意的是通过这种方式访问到的是UTF-8编码的码流,并不是独立的一个个字符。比如中文编码是多字节,变长的,所以你访问到的很有可能只是其中的一个代码点。 类型为数组的状态变量,可以标记为`public`类型,从而让`Solidity`创建一个访问器,如果要访问数组的某个元素,指定数字下标就好了。 ## 创建一个数组 可使用`new`关键字创建一个`memory`的数组。与`stroage`数组不同的是,你不能通过`.length`的长度来修改数组大小属性。我们来看看下面的例子: ``` pragma solidity ^0.4.0; contract C { function f() { //创建一个memory的数组 uint[] memory a = new uint[](7); //不能修改长度 //Error: Expression has to be an lvalue. //a.length = 100; } //storage uint[] b; function g(){ b = new uint[](7); //可以修改storage的数组 b.length = 10; b[9] = 100; } } ``` 在上面的代码中,`f()`方法尝试调整数组`a`的长度,编译器报错`Error: Expression has to be an lvalue.`。但在`g()`方法中我们看到可以修改<sup id="fnref5">[5](#fn5)</sup>。 ## 字面量及内联数组 数组字面量,是指以表达式方式隐式声明一个数组,并作为一个数组变量使用的方式。下面是一个简单的例子: ``` pragma solidity ^0.4.0; contract C { function f() { g([uint(1), 2, 3]); } function g(uint[3] _data) { // ... } } ``` 通过数组字面量,创建的数组是`memory`的,同时还是定长的。元素类型则是使用刚好能存储的元素的能用类型,比如代码里的`[1, 2, 3]`,只需要`uint8`即可存储。由于`g()`方法的参数需要的是`uint`(默认的`uint`表示的其实是`uint256`),所以要使用`uint(1)`来进行类型转换。 还需注意的一点是,定长数组,不能与变长数组相互赋值,我们来看下面的代码: ``` pragma solidity ^0.4.0; contract C { function f() { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. uint[] x = [uint(1), 3, 4]; } ``` 限制的主要原因是,ABI不能很好的支持数组,已经计划在未来移除这样的限制。(当前的ABI接口,不是已经能支持数组了?) ## 数组的属性和方法 ### length属性 数组有一个`.length`属性,表示当前的数组长度。`storage`的变长数组,可以通过给`.length`赋值调整数组长度。`memory`的变长数组不支持。 不能通过访问超出当前数组的长度的方式,来自动实现上面说的这种情况。`memory`数组虽然可以通过参数,灵活指定大小,但一旦创建,大小不可调整,对于变长数组,可以通过参数在编译期指定数组大小。 ### push方法 `storage`的变长数组和`bytes`都有一个`push()`,用于附加新元素到数据末端,返回值为新的长度。 ``` pragma solidity ^0.4.0; contract C { uint[] u; bytes b; function testArryPush() returns (uint){ uint[3] memory a = [uint(1), 2, 3]; u = a; return u.push(4); } function testBytesPush() returns (uint){ b = new bytes(3); return b.push(4); } } ``` ### 限制的情况 当前在外部函数中,不能使用多维数组。 另外,基于EVM的限制,不能通过外部函数返回动态的内容。 ``` pragma solidity ^0.4.0; contract C { function f() returns (uint[]) { } } ``` 在上面的例子中,通过web.js调用能返回数据,但在Solidity中不能返回数据。一种临时的解决办法,是使用一个非常大的静态数组。 ``` pragma solidity ^0.4.0; contract ArrayContract { //the orginal length of m_aLotOfIntegers is 2**20 //run it cause a out of gas,so change it to a much smaller 2**2 for test uint[2**2] m_aLotOfIntegers; // Note that the following is not a pair of arrays but an array of pairs. bool[2][] m_pairsOfFlags; // newPairs is stored in memory - the default for function arguments function setAllFlagPairs(bool[2][] newPairs) { // assignment to a storage array replaces the complete array m_pairsOfFlags = newPairs; } function setFlagPair(uint index, bool flagA, bool flagB) { // access to a non-existing index will throw an exception m_pairsOfFlags[index][0] = flagA; m_pairsOfFlags[index][1] = flagB; } function changeFlagArraySize(uint newSize) { // if the new size is smaller, removed array elements will be cleared m_pairsOfFlags.length = newSize; } function clear() { // these clear the arrays completely delete m_pairsOfFlags; delete m_aLotOfIntegers; // identical effect here m_pairsOfFlags.length = 0; } function addFlag(bool[2] flag) returns (uint) { return m_pairsOfFlags.push(flag); } function createMemoryArray(uint size) { // Dynamic memory arrays are created using `new`: bool[2][] memory arrayOfPairs = new bool[2][](size); m_pairsOfFlags = arrayOfPairs; } } ``` 更多请查看这里的重新梳理: [http://me.tryblockchain.org/solidity-array.html](http://me.tryblockchain.org/solidity-array.html)