---
title: "什么是 Solidity 数组？"
description: "Solidity 数组入门指南——函数、声明与故障排查"
---

# 什么是 Solidity 数组？

数组是一种线性数据结构,用于存储固定数量、相同数据类型的元素,这些元素存储在连续且相邻的内存位置中。数组中的每一个元素都由其位置指定,通常称为索引。

数组基于索引系统工作,索引从 0 到 n-1,其中 n 表示数组的总大小。与创建多个相同类型的独立变量不同,开发者只需声明一个具有所需大小的数组,即可通过索引访问其中的元素。

在[Solidity](https://www.alchemy.com/overviews/solidity)(Ethereum 和 EVM 兼容区块链上使用的主要 web3 编程语言)中,数组既可以是固定大小,也可以是动态大小。作为一种面向对象的编程语言,[Solidity 支持智能合约之间的继承](https://www.alchemy.com/overviews/solidity-interface),即多个合约可以被继承到一个合约中。

如果你想更深入地学习 Solidity 数组,可以注册 Alchemy University 的[免费 Solidity 开发者课程](https://www.alchemy.com/university/courses/solidity)。

## 什么是动态数组和固定数组?

**动态数组的大小在声明时未预先定义,而固定数组则有预先定义的大小。**随着元素被逐步添加,动态数组的大小会发生变化,在运行时才会确定数组的实际大小。

相比之下,固定数组有预先定义的大小,数组中的元素数量不应超过该大小。在极少数情况下,如果没有指定数组大小,则会创建一个“足够大”的数组——即“足够”容纳初始化数据的大小。

### **存储数组与内存数组**

**在 Solidity 中,开发者需要考虑两种类型的数组:存储数组和内存数组。**

#### **存储数组**

**存储数组**通常声明为状态变量,大小可以是固定的,也可以是动态的。需要注意的是,长度可变的存储数组可以调整大小,这意味着它们可以使用 push\(\) 和 pop\(\) 函数,分别用于向数组添加和删除元素。

#### **内存数组**

**内存数组**声明时以 memory 作为其数据位置。与存储数组类似,内存数组在编译时也可以是固定或动态长度,但在分配内存后无法调整大小。这意味着不能使用 pop\(\) 和 pull\(\) [Solidity 函数](https://www.alchemy.com/overviews/solidity-functions)。

固定大小的内存数组在声明时会自动分配——例如 `uint256[5] memory numbers` 这样的语句。但是,要分配动态内存数组,需要使用 new 运算符。例如,可以使用 `uint256[] memory numbers = new uint256[](5)``;`

**注意:** 使用数组前务必先对其进行初始化,以便获得可用的有效地址。

## Solidity 数组与映射(mapping)的比较

[Solidity 中的映射](https://www.alchemy.com/overviews/solidity-mapping)与数组类似,都是用于存储一组数据的引用类型。但映射的语法和结构与数组截然不同,这使其能够服务于独特而重要的用途。映射是一张由键和值组成的表,每个键和值都有各自预先定义的类型。可以将映射理解为在 [Solidity 智能合约](https://www.alchemy.com/overviews/solidity-smart-contract)内部初始化一张空表,等待被数据填充。

与数组不同,映射没有可获取的长度,键或值也不需要在初始化阶段就“设定”。此外,映射不能像 Solidity 数组那样进行遍历循环。

不过,从映射中检索数据比从数组中获取相同数据要高效得多。从数组中获取数据需要遍历整个数组,直到找到所需的特定元素,而映射可以直接获取该数据。

从映射中检索数据带来的性能优势,对于[在 Ethereum 智能合约中节省 gas](https://www.alchemy.com/overviews/solidity-gas-optimization) 而言相当重要,因为在区块链上编辑数据的交易需要支付 gas 费用。因此,尽可能高效地在智能合约中存储或检索数据,长期来看可以帮助开发者节省 ETH。

总之,如果你需要遍历一组数据,比如使用 for 循环,那么请使用数组。如果不需要遍历数据集,而是可以根据已知的键检索值,那么可以考虑使用映射。

## 声明和初始化数组

在 Solidity 中声明数组相对简单。需要指定元素的数据类型和元素数量,数组的大小应为正整数,且数据类型在 Solidity 中必须有效。

例如,下面这条基本指令会初始化数组,插入数据后,便可在 [console.log](https://www.alchemy.com/overviews/solidity-console-log) 中查看:

数组中的每一项称为**元素**,每个元素都可以通过“n - 1”这一函数,利用其数字索引进行访问,其中 n 表示元素的编号。

一般来说,与元素相关的编号从 0 开始。这意味着第 10 个元素的索引为 9,第 11 个元素的索引为 10,依此类推。

关于相同数据类型的限制值得牢记,因为数组存储在连续的内存单元中,这意味着每个单元必须是相同的类型,因此大小也相同。

## **结构体(Structs)**

[结构体](https://www.alchemy.com/overviews/solidity-struct)允许程序员定义自己的数据类型。一旦定义了结构体,就可以将其用作状态变量,或用于其他多种函数中,既可以使用位置参数,也可以使用关键字。后一种方式可以避免记住结构体内部成员的顺序。

**以下是一个新结构体的示例:**

## **映射(Mappings)**

映射可以理解为键值存储,其中每一个可能的键都存在,任何值都可以通过该键一次性设置或获取。**KeyType** 可以是任意内置值类型(例如 bytes、string,或任意合约/枚举类型)。**ValueType** 可以是包括映射、数组和结构体在内的任意类型。

映射可以按如下方式声明:

注意:映射变量唯一允许的数据位置是 storage。

## **成员(Members)**

要在数组中查找某个特定成员,意味着需要在数组中搜索直到找到该成员。开发者应了解两个重要函数:`length` 和 `push`。

- **Length** —— 返回数组的大小,也可用于更改动态数组的大小。
- **Push** —— 使开发者能够直接在动态存储数组末尾追加一个元素,并因此返回数组的新长度。

## Solidity 数组的故障排查

开发者有时会遇到编译错误,这些错误可能源于对声明、创建和初始化数组规则的误解。以下是排查 Solidity 数组常见错误的一些高层次方法:

### **1. Array.length**

`array.length` 函数允许开发者检查数组中当前的元素数量。内存数组的大小在声明时就已固定,而动态数组则是在运行时定义的,因此在操作时需要用到 length。

### **2. Abi.encode 和 abi.encodePacked**

当调用外部合约上的函数时,EVM 会计算一个包含函数签名和参数的字节缓冲区,序列化参数的方式有两种:[`abi.encode` 和 `abi.encodePacked`](https://www.alchemy.com/overviews/solidity-abi)。

**Abi.encode** 按照 ABI 规范对其参数进行编码。实际上,ABI 的设计目的是用于调用合约,参数会被填充到 32 字节。如果要调用合约,通常会使用此函数。

相比之下,**abi.encodePacked** 使用该类型所需的最小空间对参数进行编码。编码一个 unit8 只会占用 1 字节,当你想节省空间且不打算调用合约时,可以使用此函数。

### **3. Byte\[\] 或 bytes**

这两种数组类型都可以容纳任意长度的原始字节数据。`byte\[\] ` 与 bytes 之间的区别在于,`bytes\[\]` 遵循数组类型的规则,而 Solidity 中内存数组的元素总是占用 32 字节的倍数。这意味着,如果某个元素不足 32 字节的倍数,就会被填充,直到达到所需大小。例如,在 byte 数组的情况下,每个元素可能浪费 31 字节,而使用 bytes 数组或 string 则不会出现这种情况。

### **4. String**

这种特殊的动态数组是 UTF-8 数据类型,其功能与其他编程语言中的字符串不同,Solidity 没有提供获取字符串长度、进行两个字符串拼接或比较的函数。可以使用 bytes\[\] 将字符串快速转换为字节数组。

### **5. Solidity 数组的 null**

尽管在 Python 和 JavaScript 等其他编程语言中存在 _undefined_、_null_、_nil_、_none_ 等固有概念,但 Solidity 中并不存在这些概念。相反,开发者可以称之为零值或默认值概念,因为每个值一旦创建,就会在内存中占据一个位置,因此它必定包含某种内容。

## 如何进一步学习 Solidity 数组

要[进一步学习 Solidity 编程语言](https://www.alchemy.com/overviews/learn-solidity),并熟悉数组以及其他基础主题,请前往 Alchemy University 的 [Ethereum 训练营](https://www.alchemy.com/university/courses/ethereum)。Ethereum 训练营是一个为期 7 周的免费编程课程,配有指导视频和实践项目。

如果开发者是编程领域的新手,那么在开始 Ethereum 训练营之前,Alchemy 的[为期 3 周的 JavaScript for Ethereum 课程](https://www.alchemy.com/university/courses/js)是一个很好的先修课程。
