數(shù)組在所有的語(yǔ)言當(dāng)中都是一種常見(jiàn)類(lèi)型。在Solidity中,可以支持編譯期定長(zhǎng)數(shù)組和變長(zhǎng)數(shù)組。一個(gè)類(lèi)型為T(mén),長(zhǎng)度為k的數(shù)組,可以聲明為T[k]
,而一個(gè)變長(zhǎng)的數(shù)組則聲明為T[]
。
1. 創(chuàng)建一個(gè)數(shù)組
1.1 字面量
創(chuàng)建數(shù)組時(shí),我們可以使用字面量,隱式創(chuàng)建一個(gè)定長(zhǎng)數(shù)組。
pragma solidity ^0.4.0;
contract ArrayLiteral{
function arrayLiteral(){
uint[3] memory a = [uint(1), 2, 3];
//長(zhǎng)度必須匹配
//Type string memory[1] memory is not implicitly convertible to expected type string memory[2] memory.
//string[2] memory b = ["a"];
}
}
通過(guò)上面的代碼,我們可以發(fā)現(xiàn)。
首先元素類(lèi)型是剛好能存儲(chǔ)的元素的類(lèi)型,比如代碼里的[1, 2, 3]
,只需要uint8
即可存儲(chǔ)。但由于我們聲明的變量是uint
(默認(rèn)的uint
表示的其實(shí)是uint256
),所以要使用uint(1)
來(lái)進(jìn)行顯式的類(lèi)型轉(zhuǎn)換。
其次,字面量方式聲明的數(shù)組是定長(zhǎng)的,且實(shí)際長(zhǎng)度要與聲明的相匹配,否則編譯器會(huì)報(bào)錯(cuò)Type string memory[1] memory is not implicitly convertible to expected type string memory[2] memory
。
1.2 new關(guān)鍵字
對(duì)于變長(zhǎng)數(shù)組,在初始化分配空間前不可使用,可以通過(guò)new
關(guān)鍵字來(lái)初始化一個(gè)數(shù)組。
pragma solidity ^0.4.0;
contract NewArray{
uint[] stateVar;
function f(){
//定義一個(gè)變長(zhǎng)數(shù)組
uint[] memory memVar;
//不能在使用new初始化以前使用
//VM Exception: invalid opcode
//memVar [0] = 100;
//通過(guò)new初始化一個(gè)memory的變長(zhǎng)數(shù)組
memVar = new uint[](2);
//不能在使用new初始化以前使用
//VM Exception: invalid opcode
//stateVar[0] = 1;
//通過(guò)new初始化一個(gè)storage的變長(zhǎng)數(shù)組
stateVar = new uint[](2);
stateVar[0] = 1;
}
}
在上面的例子中,我們聲明了一個(gè)storage
的stateVar
,和一個(gè)memory
的memVar
。它們不能在使用new
關(guān)鍵字初始化前使用下標(biāo)方式訪(fǎng)問(wèn),會(huì)報(bào)錯(cuò)VM Exception: invalid opcode
。可以根據(jù)情況使用如例子中的new uint[](2);
來(lái)進(jìn)行初始化。
2. 數(shù)據(jù)的屬性和方法
2.1 length屬性
數(shù)組有一個(gè)length
屬性,表示當(dāng)前的數(shù)組長(zhǎng)度。對(duì)于storage
的變長(zhǎng)數(shù)組,可以通過(guò)給length
賦值調(diào)整數(shù)組長(zhǎng)度。
2.1.1 storage
我們來(lái)看一個(gè)自增長(zhǎng)數(shù)組的例子。
pragma solidity ^0.4.0;
contract AutoExtendArray{
uint[] stateVar = new uint[](1);
function autoExendArray(uint a) returns (uint){
//stateVar.length++語(yǔ)句會(huì)修改數(shù)組的長(zhǎng)度加1
stateVar[stateVar.length++] = a;
return stateVar[stateVar.length - 1];
}
}
在上面這個(gè)例子中,我們可以看到,通過(guò)stateVar.length++
語(yǔ)句對(duì)數(shù)組長(zhǎng)度進(jìn)行自增,我們就得到了一個(gè)不斷變長(zhǎng)的數(shù)組。
還可以使用后面提到的push()
方法,來(lái)隱式的調(diào)整數(shù)組長(zhǎng)度。
不能通過(guò)對(duì)超出當(dāng)前數(shù)組的長(zhǎng)度序號(hào)元素賦值的方式,來(lái)實(shí)現(xiàn)數(shù)組長(zhǎng)度的自動(dòng)擴(kuò)展。
2.1.2 memory
對(duì)于memory
的變長(zhǎng)數(shù)組,不支持修改length
屬性,來(lái)調(diào)整數(shù)組大小。memory
的變長(zhǎng)數(shù)組雖然可以通過(guò)參數(shù)靈活指定大小,但一旦創(chuàng)建,大小不可調(diào)整。
2.2 push方法
變長(zhǎng)的storage
數(shù)組和bytes
(不包括string)有一個(gè)push()
方法。可以將一個(gè)新元素附加到數(shù)組末端,返回值為當(dāng)前長(zhǎng)度。
pragma solidity ^0.4.0;
contract NewArray{
uint[] stateVar;
function f() returns (uint){
//在元素初始化前使用
stateVar.push(1);
stateVar = new uint[](1);
stateVar[0] = 0;
//自動(dòng)擴(kuò)充長(zhǎng)度
uint len = stateVar.push(1);
//不支持memory
//Member "push" is not available in uint256[] memory outside of storage.
//uint[] memory memVar = new uint[](1);
//memVar.push(1);
return len;
}
}
在上面的代碼中,我們聲明了一個(gè)storage
的stateVar
,在未顯式初始化的情況下,通過(guò)push()
附加了一個(gè)新值,方法內(nèi)進(jìn)行了初始化。后面的代碼中,我們通過(guò)stateVar = new uint[](1);
顯示分配了存儲(chǔ)空間為1
,使用push()
能實(shí)現(xiàn)存儲(chǔ)空間的自動(dòng)擴(kuò)展。另外,我們發(fā)現(xiàn),memory
的變長(zhǎng)數(shù)組不支持push()
。
2.3 下標(biāo)
與大多數(shù)語(yǔ)言一樣,數(shù)組可以通過(guò)數(shù)字下標(biāo)訪(fǎng)問(wèn),從0開(kāi)始。對(duì)于大小為2的數(shù)組T[2]
,要訪(fǎng)問(wèn)第二個(gè)元素,要使用下標(biāo)值1
。
如果狀態(tài)變量的類(lèi)型為數(shù)組,也可以標(biāo)記為public
類(lèi)型,從而讓Solidity
創(chuàng)建一個(gè)訪(fǎng)問(wèn)器。
pragma solidity ^0.4.0;
contract ArrayPublic{
uint[] public stateVar = [uint(1)];
}
如上面的合約在Remix運(yùn)行后,需要我們填入的是一個(gè)要訪(fǎng)問(wèn)序號(hào)的數(shù)字,來(lái)訪(fǎng)問(wèn)具體某個(gè)元素。
3. 多維數(shù)組
多維數(shù)據(jù)的定義與非區(qū)塊鏈語(yǔ)言類(lèi)似。如,我們要?jiǎng)?chuàng)建一個(gè)長(zhǎng)度為5
的uint
數(shù)組,每個(gè)元素又是一個(gè)變長(zhǎng)數(shù)組。將被聲明為uint[][5]
(注意,定義方式對(duì)比大多數(shù)語(yǔ)言來(lái)說(shuō)是反的,使用下標(biāo)訪(fǎng)問(wèn)元素時(shí)與其它語(yǔ)言一致)。
pragma solidity ^0.4.6;
contract ArrayMD{
//一個(gè)變長(zhǎng)的數(shù)組,里面的每個(gè)元素是一個(gè)長(zhǎng)度為2的數(shù)組。
//定義方式與常規(guī)語(yǔ)言相反
bool[2][] flags;
function appendFlag() returns(uint length) {
//添加一個(gè)新元素到二維數(shù)組中
return flags.push([true,true]);
}
function getFlag(uint dynamicIndex, uint lengthTwoIndex) constant returns(bool flag) {
//訪(fǎng)問(wèn)數(shù)組,第一個(gè)為變長(zhǎng)數(shù)組的序號(hào),第二個(gè)為長(zhǎng)度為2的數(shù)組序號(hào)
return flags[dynamicIndex][lengthTwoIndex];
}
}
在上面的代碼中,我們聲明了一個(gè)二維數(shù)組,它是一個(gè)變長(zhǎng)的數(shù)組,里面的每個(gè)元素是一個(gè)長(zhǎng)度為2的數(shù)組。要訪(fǎng)問(wèn)這個(gè)數(shù)組flags
,第一個(gè)下標(biāo)為變長(zhǎng)數(shù)組的序號(hào),第二個(gè)下標(biāo)為長(zhǎng)度為2的數(shù)組序號(hào)。
4. bytes與String
bytes
和string
是一種特殊的數(shù)組。bytes
類(lèi)似byte[]
,但在外部函數(shù)作為參數(shù)調(diào)用中,會(huì)進(jìn)行壓縮打包,更省空間,所以應(yīng)該盡量使用bytes
[1]。string
類(lèi)似bytes
,但不提供長(zhǎng)度和按序號(hào)的訪(fǎng)問(wèn)方式。
由于bytes
與string
,可以自由轉(zhuǎn)換,你可以將字符串s
通過(guò)bytes(s)
轉(zhuǎn)為一個(gè)bytes
。但需要注意的是通過(guò)這種方式訪(fǎng)問(wèn)到的是UTF-8編碼的碼流,并不是獨(dú)立的一個(gè)個(gè)字符。比如中文編碼是多字節(jié),變長(zhǎng)的,所以你訪(fǎng)問(wèn)到的很有可能只是其中的一個(gè)代碼點(diǎn)。
另外bytes
支持push()
方法,參考2.2節(jié)說(shuō)明。
5. 限制
當(dāng)前在外部函數(shù)中,不能使用多維數(shù)組。另外,因?yàn)镋VM的限制,不能通過(guò)外部函數(shù)返回變長(zhǎng)的數(shù)據(jù)。
pragma solidity ^0.4.0;
contract ArrayWarming {
function f() returns (uint[]) {
return new uint[](1);
}
//Return argument type inaccessible dynamic type is not implicitly convertible to expected type (type of first return variable) uint256[] memory.
/*
function g() returns (uint[]){
return this.f();
}
}
*/
在上面的例子中,f()
返回的是uint[]
的變長(zhǎng)數(shù)組。使用web3.js的方式可以訪(fǎng)問(wèn)得到結(jié)果,但使用Solidity的外部函數(shù)的訪(fǎng)問(wèn)方式,將編譯不通過(guò),提示Return argument type inaccessible dynamic type is not implicitly convertible to expected type
。
對(duì)于這樣的問(wèn)題的一種臨時(shí)的解決辦法,是使用一個(gè)非常大的定長(zhǎng)數(shù)組。
pragma solidity ^0.4.0;
contract ArrayBig {
function f() returns (uint[1]) {
//構(gòu)造一個(gè)超級(jí)大數(shù)組,來(lái)拼裝你想返回的結(jié)果
return [uint(1)];
}
function g() returns (uint[1]){
return this.f();
}
}
關(guān)于作者
專(zhuān)注基于以太坊的相關(guān)區(qū)塊鏈技術(shù),了解以太坊,Solidity,Truffle。
博客:http://me.tryblockchain.org
參考資料
-
因?yàn)樽止?jié)數(shù)組在使用時(shí)會(huì)padding,所以不節(jié)省空間。https://github.com/chriseth/solidity/blob/48588b5c4f21c8bb7c5d4319b05a93ee995b457d/_docs/faq_basic.md#what-is-the-difference-between-bytes-and-byte ?