Skip to content

Latest commit

 

History

History
206 lines (154 loc) · 6.72 KB

13 数组.md

File metadata and controls

206 lines (154 loc) · 6.72 KB

数组

数组可以具有编译时固定大小或动态大小。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Array {
    // 初始化数组的几种方式
    uint[] public arr;
    uint[] public arr2 = [1, 2, 3];
    // 固定大小的数组,所有元素都初始化为 0
    uint[10] public myFixedSizeArr;

    function get(uint i) public view returns (uint) {
        return arr[i];
    }

    // Solidity 可以返回整个数组。
    // 但是,对于可以无限增长长度的数组,应该避免使用此函数。
    function getArr() public view returns (uint[] memory) {
        return arr;
    }

    function push(uint i) public {
        // 追加元素到数组
        // 这将使数组长度增加 1。
        arr.push(i);
    }

    function pop() public {
        // 从数组中删除最后一个元素
        // 这将使数组长度减少 1。
        arr.pop();
    }

    function getLength() public view returns (uint) {
        return arr.length;
    }

    function remove(uint index) public {
        // delete 不会改变数组长度。
        // 它会将索引处的值重置为其默认值,
        // 在本例中为 0。
        delete arr[index];
    }

    function examples() external {
        // 在内存中创建数组,只能创建固定大小的数组
        uint[] memory a = new uint[](5);
    }
}

术语说明

arr 和 arr2 分别演示了两种初始化数组的方式。arr 是一个动态数组,因为它没有指定固定的大小。arr2 是一个静态数组,因为它的大小在声明时已经确定了。
myFixedSizeArr 是一个具有固定大小的数组,因为它的大小在声明时已经确定了。
get 函数演示了如何访问数组中的元素。在 Solidity 中,数组的索引从 0 开始。
getArr 函数演示了如何返回整个数组。但是,如果数组可能无限增长,则应避免使用此函数。
push 函数演示了如何向数组添加元素。这将使数组长度增加 1。
pop 函数演示了如何从数组中删除元素。这将使数组长度减少 1。
getLength 函数演示了如何获取数组的长度。
remove 函数演示了如何从数组中删除元素。使用 delete 关键字,将元素值重置为其默认值(在本例中为 0)。
examples 函数演示了如何在内存中创建数组,只能创建具有固定大小的数组。

数组元素删除示例

通过从右向左移动元素来删除数组元素

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract ArrayRemoveByShifting {
    // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
    // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
    // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
    // [1] -- remove(0) --> [1] --> []

    uint[] public arr;

    function remove(uint _index) public {
        require(_index < arr.length, "index out of bound");

        for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }

    function test() external {
        arr = [1, 2, 3, 4, 5];
        remove(2);
        // [1, 2, 4, 5]
        assert(arr[0] == 1);
        assert(arr[1] == 2);
        assert(arr[2] == 4);
        assert(arr[3] == 5);
        assert(arr.length == 4);

        arr = [1];
        remove(0);
        // []
        assert(arr.length == 0);
    }
}

代码解释:

remove 函数删除数组中给定索引的元素,并将剩余元素从右向左移动。函数首先使用 require 语句检查索引是否在数组范围内,然后循环遍历从给定索引开始的数组,将每个元素向左移动一个位置。最后,使用 pop 函数从数组末尾删除一个元素。 test 函数为 remove 函数提供了几个用例,以检查函数是否正确删除数组中的元素,并更新数组长度。

代码中用到的词汇:

动态数组

数组大小在运行时可以更改的数组。

索引

一个指向数组元素的数字,从零开始计数。

require 语句

Solidity 中的一个内置函数,用于在函数中检查条件,如果条件不满足,则抛出异常。

pop 函数

从数组末尾删除一个元素,并返回删除的元素。

原文链接

https://solidity-by-example.org/array/

扩充

pop()通常和push()一起使用,实现类似栈的后进先出结构。

uint[] public stack = [1, 2, 3]; 

// 移除并返回最后一个元素  
uint removed = stack.pop(); 

// 添加一个新元素
stack.push(5);
//需要注意的是:pop()会消耗gas,因为它写入了状态变化。如果数组为空,调用pop()会触发错误。返回的值popped是被移除元素的一个副本,对它的修改不会影响原数组。

当然,我们也可以在内存中创建数组

function examples() external {

  // 在内存中创建数组,只能创建固定大小,避免gas费,提高效率
  uint[] memory a = new uint[](5);
  //memory表示仅在内存中创建这个数组,而不是存储在区块链上。内存数组的生命周期仅限于一个函数的执行。
}

当然如果我们想要删除一个数组,在solidity中是没有删除数组的方法的,所以需要一段逻辑

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract ArrayRemoveByShifting {
    // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
    // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
    // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
    // [1] -- remove(0) --> [1] --> []

    uint[] public arr = [1,2,3,4,5,6,7,8,9];

    function remove(uint _index) public {
        require(_index < arr.length, "index out of bound");

        for (uint i = _index; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr.pop();
    }

    function get() public view returns(uint[] memory ) {
        return arr;
    }
}

对于返回值声明为 uint[] memory 的原因是:

Solidity 中,存储在状态变量中的数组是引用类型,直接返回这个状态变量arr会返回一个引用,external函数调用者可以修改状态。

为了避免这种修改状态的情况,我们通过返回一个内存数组副本来断开引用关系。

memory关键字表示这个返回的数组仅存在于内存中,外部调用不会影响到存储的原数组。

一些同学会将代码这样去写

function get() public view returns(uint[] ) {
  return arr; 
}

这样报错

Solidity不允许直接将存储引用转换为内存数组,因为会带来安全风险。所以我们需要显式把存储数组 copy 一份到内存中返回 !直接返回存储引用类型是不被允许的,我们需要通过内存返回副本。