Содержание

Массивы в Solidity

Массивы могут быть фиксированными и динамическими. Для storage-массивов, тип элемента может быть произвольным (то есть также другие массивы, маппинги или структуры). Для memory-массивов, он не может быть маппингом, и он должен быть типом ABI, если он является аргументом public-функции.

Массив фиксированного размера k и типом элемента T записывается, как T[k]. Массив динамического размера записывается, как T[]. Например, uint массив из 5 динамических массивов записывается, как uint[][5]. Чтобы получить доступ ко 2 элементу в третьем динамическом массиве нужно использовать запись x[2][1] (индексы начинаются с 0 и доступ работает в обратном направлении относительно объявления).

Переменные типа bytes и string — это специальные массивы. bytes подобен byte[], но он плотно упакован в calldata. string эквивалентен bytes, но не дает доступ к длине и индексу массива.

Таким образом bytes всегда предпочтительнее, чем byte[], так как он дешевле.

Примечание

Если нужно получить доступ к байт-представлению строки s, нужно использовать bytes(s).length, bytes(s)[7] = ‘x’;. Необходимо иметь в виду, что это доступ к низкоуровневым байтам представления UTF-8, а не отдельным символам.

Можно отметить массив как public, и в Solidity создастся геттер. Числовой индекс станет обязательным параметром для геттера.

Распределение памяти массивов

Создать массивы переменной длины в memory можно используя ключевое слово new. В отличие от массивов storage, нельзя изменять размер memory-массивов используя .length.

pragma solidity ^0.4.16;

contract C {
    function f(uint len) public pure {
        uint[] memory a = new uint[](7);
        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[3] memory, потому что тип каждого элемента uint8. Поэтому необходимо преобразовывать первый элемент, например, в uint. Memory-массивы фиксированного размера не могут быть присвоены динамическим memory-массивам, т.е. следующий код невозможен.

// Этот код не скомпилируется.

pragma solidity ^0.4.0;

contract C {
    function f() public {
        // Следующая строка вызывает ошибку, потому что uint[3] memory
        // не может быть преобразован в uint[] memory.
        uint[] x = [uint(1), 3, 4];
    }
}

В будущем планируется убрать это ограничение.

Члены массивов:

length:

length хранит число элементов массива. Динамические массивы могут изменять размер в storage (не в memory) путем изменения .length. Размер memory-массивов фиксируется (но может быть динамическим, то есть он может зависеть от параметров) в момент их создания.

push:

Динамические storage-массивы и bytes (не string) имеют функцию push, которую можно использовать для добавления элемента в конец массива. Функция возвращает новую длину.

Предупреждение

Пока нельзя использовать массивы массивов в external-функциях.

Предупреждение

Из-за ограничений  EVM, невозможно возвращать динамический контент из внешних вызовов функций. Функция f в contract C {function f() returns (uint[]) { … } } что-то вернет, при вызове из web3.js, но не при вызове из Solidity. Единственным обходным решением на данный момент является использование больших массивов статического размера.

pragma solidity ^0.4.16;

contract ArrayContract {
    uint[2**20] m_aLotOfIntegers;
    // В следующей строке не пара динамических массивов, а динамический
    // массив пар (то есть фиксированных массивов длины 2).
    bool[2][] m_pairsOfFlags;
    // newPairs хранится в memory — значение  по умолчанию для аргументов
    // функции

    function setAllFlagPairs(bool[2][] newPairs) public {
        // присвоение storage-массиву заменяет весь массив
        m_pairsOfFlags = newPairs;
    }

    function setFlagPair(uint index, bool flagA, bool flagB) public {
        // доступ к несуществующему индексу вызовет ошибку
        m_pairsOfFlags[index][0] = flagA;
        m_pairsOfFlags[index][1] = flagB;
    }

    function changeFlagArraySize(uint newSize) public {
        // если новый размер меньше, удаленные элементы массива будут очищены
        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 {
        // байтовые массивы («bytes») различаются, 
        // поскольку они хранятся без заполнения,
        // но можно обрабатывать идентично "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) {
        // Динамические memory-массивы создаются с помощью `new`:
        uint[2][] memory arrayOfPairs = new uint[2][](size);
        // Создание динамического массива:
        bytes memory b = new bytes(200);
        for (uint i = 0; i < b.length; i++)
            b[i] = byte(i);
        return b;
    }
}
Материал был полезен? Поделитесь в соц. сетях:
Логотип echain.ru

Добавить комментарий

Ваш e-mail не будет опубликован.