智慧合約語言 Solidity 教程系列5 - 陣列介紹

Tiny熊發表於2017-12-25

最新內容會更新在主站深入淺出區塊鏈社群
原文連結:智慧合約語言 Solidity 教程系列5 - 陣列介紹

寫在前面

Solidity 是以太坊智慧合約程式語言,閱讀本文前,你應該對以太坊、智慧合約有所瞭解,
如果你還不瞭解,建議你先看以太坊是什麼

本文前半部分是參考Solidity官方文件(當前最新版本:0.4.20)進行翻譯,後半部分對官方文件中沒有提供程式碼的知識點補充程式碼說明(訂閱專欄閱讀)。

陣列(Arrays)

陣列可以宣告時指定長度,也可以是動態變長。對storage儲存的陣列來說,元素型別可以是任意的,型別可以是陣列,對映型別,結構體等。但對於memory的陣列來說。如果作為public函式的引數,它不能是對映型別的陣列,只能是支援ABI的型別

一個元素型別為T,固定長度為k的陣列,可以宣告為T[k],而一個動態大小(變長)的陣列則宣告為T[]
還可以宣告一個多維陣列,如宣告一個型別為uint的陣列長度為5的變長陣列(5個元素都是變長陣列),可以宣告為uint[][5]。(注意,相比非區塊鏈語言,多維陣列的長度宣告是反的。)

要訪問第三個動態陣列的第二個元素,使用x[2][1]。陣列的序號是從0開始的,序號順序與定義相反。

bytesstring是一種特殊的陣列。bytes類似byte[],但在外部函式作為引數呼叫中,bytes會進行壓縮打包。string類似bytes,但不提供長度和按序號的訪問方式(目前)。
所以應該儘量使用bytes而不是byte[]

可以將字串s通過bytes(s)轉為一個bytes,可以通過bytes(s).length獲取長度,bytes(s)[n]獲取對應的UTF-8編碼。通過下標訪問獲取到的不是對應字元,而是UTF-8編碼,比如中文編碼是多位元組,變長的,所以下標訪問到的只是其中的一個編碼。
型別為陣列的狀態變數,可以標記為public,從而讓Solidity建立一個訪問器,如果要訪問陣列的某個元素,指定數字下標就好了。(稍後程式碼事例)

建立記憶體陣列

可使用new關鍵字建立一個memory的陣列。與stroage陣列不同的是,你不能通過.length的長度來修改陣列大小屬性。我們來看看下面的例子:

pragma solidity ^0.4.16;

contract C {
    function f(uint len) public pure {
        uint[] memory a = new uint[](7);
                
        //a.length = 100;  // 錯誤
        bytes memory b = new bytes(len);
        // Here we have a.length == 7 and b.length == len
        a[6] = 8;
    }
}

陣列常量及內聯陣列

陣列常量,是一個陣列表示式(還沒有賦值到變數)。下面是一個簡單的例子:

pragma solidity ^0.4.16;

contract C {
    function f() public pure {
        g([uint(1), 2, 3]);
    }
    function g(uint[3] _data) public pure {
        // ...
    }
}

通過陣列常量,建立的陣列是memory的,同時還是定長的。元素型別則是使用剛好能儲存的元素的能用型別,比如[1, 2, 3],只需要uint8即可儲存,它的型別是uint8[3] memory

由於g()方法的引數需要的是uint(預設的uint表示的其實是uint256),所以需要對第一個元素進行型別轉換,使用uint(1)來進行這個轉換。

還需注意的一點是,定長陣列,不能與變長陣列相互賦值,我們來看下面的程式碼:

//  無法編譯
pragma solidity ^0.4.0;

contract C {
    function f() public {
        // The next line creates a type error because uint[3] memory
        // cannot be converted to uint[] memory.
        uint[] x = [uint(1), 3, 4];
    }
}

已經計劃在未來移除這樣的限制。當前因為ABI傳遞陣列還有些問題。

成員

length屬性

陣列有一個.length屬性,表示當前的陣列長度。storage的變長陣列,可以通過給.length賦值調整陣列長度。memory的變長陣列不支援。
不能通過訪問超出當前陣列的長度的方式,來自動實現改變陣列長度。memory陣列雖然可以通過引數,靈活指定大小,但一旦建立,大小不可調整。

push方法

storage的變長陣列和bytes都有一個push方法(string沒有),用於附加新元素到資料末端,返回值為新的長度。

限制情況

當前在external函式中,不能使用多維陣列。

另外,基於EVM的限制,不能通過外部函式返回動態的內容。

contract C {
     function f() returns (uint[]) { ... }
      }

在這個的例子中,如果通過web.js呼叫能返回資料,但從Solidity中呼叫不能返回資料。一種繞過這個問題的辦法是使用一個非常大的靜態陣列。


pragma solidity ^0.4.16;

contract ArrayContract {
    uint[2**20] m_aLotOfIntegers;
    // 這裡不是兩個動態陣列的陣列,而是一個動態陣列裡,每個元素是長度為二的陣列。
    bool[2][] m_pairsOfFlags;
    // newPairs 存在 memory裡,因為是函式引數
    function setAllFlagPairs(bool[2][] newPairs) public {
        m_pairsOfFlags = newPairs;
    }

    function setFlagPair(uint index, bool flagA, bool flagB) public {
        // 訪問不存在的index會丟擲異常
        m_pairsOfFlags[index][0] = flagA;
        m_pairsOfFlags[index][1] = flagB;
    }

    function changeFlagArraySize(uint newSize) public {
        // 如果新size更小, 移除的元素會被銷燬
        m_pairsOfFlags.length = newSize;
    }

    function clear() public {
        // 銷燬
        delete m_pairsOfFlags;
        delete m_aLotOfIntegers;
        // 同銷燬一樣的效果
        m_pairsOfFlags.length = 0;
    }

    bytes m_byteData;

    function byteArrays(bytes data) public {
        // byte arrays ("bytes") are different as they are stored without padding,
        // but can be treated identical to "uint8[]"
        m_byteData = data;
        m_byteData.length += 7;
        m_byteData[3] = byte(8);
        delete m_byteData[2];
    }

    function addFlag(bool[2] flag) public returns (uint) {
        return m_pairsOfFlags.push(flag);
    }

    function createMemoryArray(uint size) public pure returns (bytes) {
        // Dynamic memory arrays are created using `new`:
        uint[2][] memory arrayOfPairs = new uint[2][](size);
        // Create a dynamic byte array:
        bytes memory b = new bytes(200);
        for (uint i = 0; i < b.length; i++)
            b[i] = byte(i);
        return b;
    }
}

補充事例說明

事例程式碼及講解,請訂閱區塊鏈技術檢視。

參考視訊

我們也推出了目前市面上最全的視訊教程:深入詳解以太坊智慧合約語言Solidity
目前我們也在招募體驗師,可以點選連結瞭解。

參考文件

Solidity官方文件-陣列

深入淺出區塊鏈 - 系統學習區塊鏈,打造最好的區塊鏈技術部落格

相關文章