數(shù)組

2022-05-12 17:01 更新

數(shù)組可以具有編譯時固定大小,也可以具有動態(tài)大小。

固定大小k和元素類型的數(shù)組的類型T寫為T[k],動態(tài)大小的數(shù)組寫為T[]。

例如,一個由 5 個動態(tài)數(shù)組組成的數(shù)組uint寫為 uint[][5]. 與其他一些語言相比,這種表示法是相反的。在 Solidity 中,X[3]總是一個包含三個 type 元素的數(shù)組X,即使X它本身就是一個數(shù)組。在其他語言(例如 C)中并非如此。

索引從零開始,訪問與聲明的方向相反。

例如,如果您有一個變量,則使用 訪問第三個動態(tài)數(shù)組中的第七個,并使用 訪問第三個動態(tài)數(shù)組。同樣,如果您有一個類型的數(shù)組也可以是一個數(shù)組,那么總是有 type 。uint[][5] memory xuintx[2][6]x[2]T[5] aTa[2]T

數(shù)組元素可以是任何類型,包括映射或結(jié)構(gòu)。類型的一般限制適用,因為映射只能存儲在 storage數(shù)據(jù)位置,公開可見的函數(shù)需要ABI 類型的參數(shù)。

可以標(biāo)記狀態(tài)變量數(shù)組public并讓 Solidity 創(chuàng)建一個getter。數(shù)字索引成為 getter 的必需參數(shù)。

訪問超出其末尾的數(shù)組會導(dǎo)致斷言失敗。方法.push()和.push(value)可用于在數(shù)組末尾追加一個新元素,其中.push()追加一個零初始化元素并返回對它的引用。

bytesstring作為數(shù)組

bytes和 類型的變量string是特殊的數(shù)組。類型與bytes類似bytes1[],但它緊緊地封裝在 calldata 和內(nèi)存中。string等于bytes但不允許長度或索引訪問。

Solidity 沒有字符串操作函數(shù),但有第三方字符串庫。您還可以使用 keccak256-hash 比較兩個字符串, 并使用 連接兩個字符串。keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2))string.concat(s1, s2)

您應(yīng)該使用bytesoverbytes1[]因為它更便宜,因為使用bytes1[]inmemory在元素之間添加了 31 個填充字節(jié)。請注意,在 中storage,由于緊密包裝而缺少填充,請參見bytes 和 string。作為一般規(guī)則,bytes用于任意長度的原始字節(jié)數(shù)據(jù)和string任意長度的字符串 (UTF-8) 數(shù)據(jù)。如果您可以將長度限制為一定數(shù)量的字節(jié),請始終使用其中一種值類型bytes1bytes32因為它們便宜得多。

筆記

如果要訪問 string 的字節(jié)表示s,請使用 bytes(s).length/ 。請記住,您訪問的是 UTF-8 表示的低級字節(jié),而不是單個字符。bytes(s)[7] = 'x';

功能bytes.concatstring.concat

您可以使用 連接任意數(shù)量的stringstring.concat。該函數(shù)返回一個包含參數(shù)內(nèi)容的單個數(shù)組,沒有填充。如果要使用其他類型的參數(shù)不能隱式轉(zhuǎn)換為,則需要先轉(zhuǎn)換為。string memorystringstring

類似地,該bytes.concat函數(shù)可以連接任意數(shù)量的bytes或值。該函數(shù)返回一個包含參數(shù)內(nèi)容的單個數(shù)組,沒有填充。如果要使用字符串參數(shù)或其他不能隱式轉(zhuǎn)換為的類型,則需要先將它們轉(zhuǎn)換為或/…/ 。bytes1 ... bytes32bytes memorybytesbytesbytes1bytes32

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;

contract C {
    string s = "Storage";
    function f(bytes calldata bc, string memory sm, bytes16 b) public view {
        string memory concatString = string.concat(s, string(bc), "Literal", sm);
        assert((bytes(s).length + bc.length + 7 + bytes(sm).length) == bytes(concatString).length);

        bytes memory concatBytes = bytes.concat(bytes(s), bc, bc[:2], "Literal", bytes(sm), b);
        assert((bytes(s).length + bc.length + 2 + 7 + bytes(sm).length + b.length) == concatBytes.length);
    }
}

如果您調(diào)用string.concatbytes.concat不使用參數(shù),它們將返回一個空數(shù)組。

分配內(nèi)存數(shù)組

可以使用new運(yùn)算符創(chuàng)建具有動態(tài)長度的內(nèi)存數(shù)組。與存儲數(shù)組相反,無法調(diào)整內(nèi)存數(shù)組的大?。ɡ?,.push成員函數(shù)不可用)。您要么必須提前計算所需的大小,要么創(chuàng)建一個新的內(nèi)存數(shù)組并復(fù)制每個元素。

與 Solidity 中的所有變量一樣,新分配的數(shù)組的元素始終使用默認(rèn)值初始化。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    function f(uint len) public pure {
        uint[] memory a = new uint[](7);
        bytes memory b = new bytes(len);
        assert(a.length == 7);
        assert(b.length == len);
        a[6] = 8;
    }
}

數(shù)組文字

數(shù)組字面量是一個或多個表達(dá)式的逗號分隔列表,用方括號 ( [...]) 括起來。例如. 數(shù)組字面量的類型確定如下:[1, a, f(3)]

它始終是一個靜態(tài)大小的內(nèi)存數(shù)組,其長度是表達(dá)式的數(shù)量。

數(shù)組的基本類型是列表中第一個表達(dá)式的類型,這樣所有其他表達(dá)式都可以隱式轉(zhuǎn)換為它。如果這是不可能的,這是一個類型錯誤。

有一個所有元素都可以轉(zhuǎn)換為的類型是不夠的。其中一個元素必須屬于該類型。

在下面的例子中,類型是 ,因為每個常量的類型都是。如果希望結(jié)果為類型,則需要將第一個元素轉(zhuǎn)換為.[1, 2, 3]uint8[3] memoryuint8uint[3] memoryuint

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

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

數(shù)組字面量無效,因為第一個表達(dá)式的類型是,而第二個表達(dá)式的類型是,并且它們不能隱式相互轉(zhuǎn)換。為了使它工作,你可以使用,例如。[1, -1]uint8int8[int8(1), -1]

由于不同類型的固定大小的內(nèi)存數(shù)組不能相互轉(zhuǎn)換(即使基類型可以),如果你想使用二維數(shù)組字面量,你總是必須明確指定一個通用的基類型:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    function f() 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;
    }
}

固定大小的內(nèi)存數(shù)組不能分配給動態(tài)大小的內(nèi)存數(shù)組,即以下是不可能的:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

// This will not compile.
contract C {
    function f() 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];
    }
}

計劃在將來取消此限制,但由于數(shù)組在 ABI 中的傳遞方式,它會產(chǎn)生一些復(fù)雜性。

如果要初始化動態(tài)大小的數(shù)組,則必須分配各個元素:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract C {
    function f() public pure {
        uint[] memory x = new uint[](3);
        x[0] = 1;
        x[1] = 3;
        x[2] = 4;
    }
}

數(shù)組成員

長度

數(shù)組有一個length包含其元素數(shù)量的成員。內(nèi)存數(shù)組的長度在創(chuàng)建后是固定的(但是是動態(tài)的,即它可以依賴于運(yùn)行時參數(shù))。

推()

動態(tài)存儲數(shù)組 和bytes(not string) 有一個名為的成員函數(shù)push(),您可以使用它在數(shù)組末尾附加一個零初始化元素。它返回對元素的引用,以便可以像 或一樣使用它。x.push().t = 2x.push() = b

推(x)

動態(tài)存儲數(shù)組 和bytes(not string) 有一個名為的成員函數(shù)push(x),您可以使用它在數(shù)組末尾附加給定元素。該函數(shù)不返回任何內(nèi)容。

流行()

動態(tài)存儲數(shù)組 和bytes(not string) 有一個名為的成員函數(shù)pop(),您可以使用該函數(shù)從數(shù)組末尾刪除一個元素。這也隱式調(diào)用刪除元素上的刪除。

筆記

通過調(diào)用增加存儲數(shù)組的長度push() 具有恒定的gas成本,因為存儲是零初始化的,而通過調(diào)用減少長度pop()的成本取決于被刪除元素的“大小”。如果該元素是一個數(shù)組,它可能會非常昂貴,因為它包括顯式清除已刪除的元素,類似于對它們調(diào)用delete。

筆記

要在外部(而不是公共)函數(shù)中使用數(shù)組數(shù)組,您需要激活 ABI coder v2。

筆記

在拜占庭之前的 EVM 版本中,無法訪問函數(shù)調(diào)用返回的動態(tài)數(shù)組。如果您調(diào)用返回動態(tài)數(shù)組的函數(shù),請確保使用設(shè)置為拜占庭模式的 EVM。

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;

contract ArrayContract {
    uint[2**20] aLotOfIntegers;
    // Note that the following is not a pair of dynamic arrays but a
    // dynamic array of pairs (i.e. of fixed size arrays of length two).
    // Because of that, T[] is always a dynamic array of T, even if T
    // itself is an array.
    // Data location for all state variables is storage.
    bool[2][] pairsOfFlags;

    // newPairs is stored in memory - the only possibility
    // for public contract function arguments
    function setAllFlagPairs(bool[2][] memory newPairs) public {
        // assignment to a storage array performs a copy of ``newPairs`` and
        // replaces the complete array ``pairsOfFlags``.
        pairsOfFlags = newPairs;
    }

    struct StructType {
        uint[] contents;
        uint moreInfo;
    }
    StructType s;

    function f(uint[] memory c) public {
        // stores a reference to ``s`` in ``g``
        StructType storage g = s;
        // also changes ``s.moreInfo``.
        g.moreInfo = 2;
        // assigns a copy because ``g.contents``
        // is not a local variable, but a member of
        // a local variable.
        g.contents = c;
    }

    function setFlagPair(uint index, bool flagA, bool flagB) public {
        // access to a non-existing index will throw an exception
        pairsOfFlags[index][0] = flagA;
        pairsOfFlags[index][1] = flagB;
    }

    function changeFlagArraySize(uint newSize) public {
        // using push and pop is the only way to change the
        // length of an array
        if (newSize < pairsOfFlags.length) {
            while (pairsOfFlags.length > newSize)
                pairsOfFlags.pop();
        } else if (newSize > pairsOfFlags.length) {
            while (pairsOfFlags.length < newSize)
                pairsOfFlags.push();
        }
    }

    function clear() public {
        // these clear the arrays completely
        delete pairsOfFlags;
        delete aLotOfIntegers;
        // identical effect here
        pairsOfFlags = new bool[2][](0);
    }

    bytes byteData;

    function byteArrays(bytes memory data) public {
        // byte arrays ("bytes") are different as they are stored without padding,
        // but can be treated identical to "uint8[]"
        byteData = data;
        for (uint i = 0; i < 7; i++)
            byteData.push();
        byteData[3] = 0x08;
        delete byteData[2];
    }

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

    function createMemoryArray(uint size) public pure returns (bytes memory) {
        // Dynamic memory arrays are created using `new`:
        uint[2][] memory arrayOfPairs = new uint[2][](size);

        // Inline arrays are always statically-sized and if you only
        // use literals, you have to provide at least one type.
        arrayOfPairs[0] = [uint(1), 2];

        // Create a dynamic byte array:
        bytes memory b = new bytes(200);
        for (uint i = 0; i < b.length; i++)
            b[i] = bytes1(uint8(i));
        return b;
    }
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號