数组

本章学习 Solidity 中数组的定义语法和使用方法。

视频Bilibili  |  Youtube
官网binschool.app
推特@BinSchool    DiscordBinDAO   微信:bkra50 

Solidity 中,数组 是一种用于存储相同类型元素的数据结构。

例如,声明一个数组变量 uint numbers[10],那么变量 numbers 存储了 10 个 uint 类型的数据。

数组中的特定元素可以通过索引访问,索引值从 0 开始。

例如,上面声明的数组 numbers,可以使用 numbers[0]、numbers[1] 访问前两个变量。

Solidity 支持 固定长度数组动态长度数组 两种类型。

1. 固定长度数组

固定长度数组 在声明时需要指定数组的长度,并且这个长度在编译时就确定了,部署后无法更改。

声明一个 固定长度数组 ,需要指定元素类型和数量,语法如下:

type_name arrayName [length];

其中数组长度 length 必须是一个大于零的整数数字,type_name 可以是任何数据类型。

例如,声明一个 uint 类型,长度为 10 的数组 balance,如下所示:

uint balance[10];

初始化一个 固定长度数组,可以使用下面的语句:

uint balance[3] = [uint(1), 2, 3]; // 初始化固定长度数组
balance[2] = 5; // 设置第 3 个元素的值为 5

这里的数组 balance 的初值,为什么写为 [uint(1), 2, 3],而不是 [1,2,3] 呢?

这是因为在 Solidity 中,如果一个类型的字面量没有显式指定,那么就会由编译器自行推断。

整数字面量的解析规则是根据其值的范围来确定默认的类型。当你提供一个整数字面量时,编译器会尝试确定最小的类型来容纳该值。

如果整数字面量的值在 uint8 的取值范围内(0 到 255),编译器会将其默认解析为 uint8 类型。因为在这种情况下,使用 uint8 类型可以使用更少的存储空间,来提高存储效率。

如果整数字面量的值超出了 uint8 的取值范围,编译器会根据需要自动升级到更大的整数类型,如 uint16uint32

所以,我们在编写程序的时候,尽量显式指定某些字面量的类型,防止异常情况发生。

我们可以通过数组的索引来访问数组元素,例如:

uint salary = balance[2];

我们可以使用下面的合约,来展示 固定长度数组 的用法。

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

contract StaticArray {
    // 初始化固定长度数组为[1,2,3]
    uint[3] balance = [uint(1), 2, 3];

    function staticArray() external returns (uint length, uint[3] memory array) {
        // 获取第 3 个元素的值
        uint element = balance[2];

        // 设置第 3 个元素的值乘以 2
        balance[2] = element * 2;

        // 返回数组的长度和数组所有元素
        return (balance.length, balance);
    }
}

我们将合约代码复制到 Remix,进行编译,并部署到区块链上:

 

点击 staticArray,返回结果:数组长度为 3,数组元素为 1,2,6。

2. 动态长度数组

动态长度数组 与 固定长度数组 相比,就是 动态长度数组 的长度是可以改变的。

声明一个 动态长度数组,只需要指定元素类型,而不需要指定元素的数量,语法如下:

type_name arrayName[];

动态长度数组 初始化后,是一个长度为 0 的空数组。我们可以使用 push 方法,在数组末尾追加一个元素;使用 pop 方法,截掉末尾的一个元素。

其中,push 有两种用法:一种是带参数 push(x),第二种是不带参数 push(),如果不带参数,那么就会在数组的末尾添加一个具有默认值的元素。

动态长度数组 元素的访问方法,与固定长度数组相同,也是通过索引来访问数组元素。

我们可以使用下面的合约,来展示动态数组的用法。

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

contract DynamicArray {
    // 初始化动态长度数组为[1,2,3]
    uint[] balance = [uint(1), 2, 3];

    function dynamicArray() external returns (uint length, uint[] memory array) {
        // 追加两个新元素4、5
        balance.push(4);
        balance.push(5);

        // 删除最后一个元素 5
        balance.pop();

        // 返回数组的长度和数组所有元素
        return (balance.length, balance);
    }
}

我们将合约代码复制到 Remix,进行编译,并部署到区块链上。

点击 dynamicArray,返回结果:数组长度为 4,数组元素为 1,2,3,4。

3. new、delete 操作

我们可以使用 new 关键字来动态创建一个数组。

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

contract Array {
   function newArray() external pure returns(uint[] memory) {
      uint[] memory arr = new uint[](3);
      arr[0] = 1;
      return arr;  // 返回结果 arr = [1,0,0]
   }
}

通过 new 关键字创建的数组是一个动态长度数组。它是无法一次性赋予初值的,只能对每一个元素的值分别进行设置。

另外,在Solidity 中可以使用 delete 操作符来操作数组元素,但 delete 操作并不是用于删除数组的某个元素,这与我们直观的理解是不同的。

我们在前面章节中已经讲过,delete 操作符只是对变量值重新初始化,使其值变为默认值,而不是删除这个变量。在数组上使用 delete 操作,效果也是一样的。

delete arr[i],并不是要删除一个数组中的元素,而是将该元素恢为默认值,例如:

uint[] balance = [uint(1), 2, 3];
delete balance[0]; // 执行 delete 后,balance = [0,2,3]

除了动态长度数组的 pop 函数外,Solidity 并没有提供删除某个特定元素的函数。如果非要删除数组中某个特定的元素,只能使用其它变通的方法。

数组是 Solidity 中比较常用的数据类型之一,我们要熟练掌握。