数组
数组
可以声明时指定长度,或者是变长的。对storage
1的数组来说,元素类型可以是任意的,类型可以是数组,映射类型,数据结构等。但对于memory
[datalocation]的数组来说。如果函数是对外可见的2,那么函数参数不能是映射类型的数组,只能是支持ABI的类型3。
一个类型为T,长度为k的数组,可以声明为T[k]
,而一个变长的数组则声明为T[]
。
你还可以声明一个多维数据,如一个类型为uint
的数组长度为5的变长数组,可以声明为uint[][5] x
。需要留心的是,相比非区块链语言,多维数组的长度声明是反的。
要访问第三个动态数据的,第二个元素,使用x[2][1]
。数组的序号是从0开始的,序号顺序与定义相反。
bytes
和string
是一种特殊的数组。bytes
类似byte[]
,但在外部函数作为参数调用中,会进行压缩打包,更省空间,所以应该尽量使用bytes
4。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()
方法中我们看到可以修改5。
字面量及内联数组
数组字面量,是指以表达式方式隐式声明一个数组,并作为一个数组变量使用的方式。下面是一个简单的例子:
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
参考资料
变量声明时定义的数据位置属性。可以是memory的,也可以为storage的,详见:http://me.tryblockchain.org/data-location.org ↩
函数的可见性说明,详见这里: http://me.tryblockchain.org/solidity-function-advanced1.html ↩
了解更多关于ABI的信息,https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#types ↩
因为字节数组在使用时会padding,所以不节省空间。https://github.com/chriseth/solidity/blob/48588b5c4f21c8bb7c5d4319b05a93ee995b457d/_docs/faq_basic.md#what-is-the-difference-between-bytes-and-byte ↩
http://stackoverflow.com/questions/33839122/in-ethereum-solidity-when-changing-an-array-length-i-get-value-must-be-an-lva ↩