---
title: "什麼是 Solidity arrays？"
description: "Solidity Arrays 入門指南——函式、宣告與疑難排解"
---

# 什麼是 Solidity arrays？

陣列是線性資料結構，用來儲存固定數量、相同資料型別的元素，這些元素被儲存在連續且相鄰的記憶體位置中。陣列中的每一個元素都可以透過其位置來指定，這個位置通常稱為索引。

陣列採用以索引為基礎的系統運作，從 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 中的 mapping](https://www.alchemy.com/overviews/solidity-mapping) 與陣列類似，都是用來儲存一組資料的參考型別。然而 mapping 的語法和結構相當不同，這使它能夠發揮獨特且重要的作用。mapping 是一個鍵值表，每個鍵和值都有各自預先定義的型別。可以把 mapping 想像成在 [Solidity 智慧合約](https://www.alchemy.com/overviews/solidity-smart-contract)內建立一個空表格，等待被填入資料。

與陣列不同，mapping 沒有可取得的長度，鍵或值也不需要從初始化階段就「設定」好。此外，無法像對 Solidity 陣列那樣對 mapping 進行迴圈遍歷。

然而，從 mapping 中取得一筆資料，遠比從陣列中取得相同資料更有效率。從陣列中取得資料需要遍歷整個陣列，直到找到你要找的特定元素為止，而 mapping 則可以立即取得該資料。

從 mapping 取得資料所帶來的效能優勢，對於[節省 Ethereum 智慧合約的 gas](https://www.alchemy.com/overviews/solidity-gas-optimization)相當重要，因為在區塊鏈上修改資料的交易需要支付 gas 費用。因此，盡可能有效率地在智慧合約中儲存或取得資料，長期下來可以為開發者節省 ETH。

總結來說，如果你需要以例如 for 迴圈的方式遍歷一組資料，就應該使用陣列。如果不需要遍歷一組資料，而是可以根據已知的鍵來取得值，那麼就可以考慮使用 mapping。

## 宣告與初始化陣列

在 Solidity 中宣告陣列相對簡單。需要指定元素的資料型別以及元素的數量，陣列的大小應為正整數，且資料型別必須是 Solidity 中有效的型別。

例如，下方基本的指令會初始化陣列，一旦資料被插入後，就會顯示在 [console.log](https://www.alchemy.com/overviews/solidity-console-log) 中：

陣列中的每一項稱為一個**元素**，每個元素都可以透過「n - 1」這個函式，以其數字索引來存取，其中 n 代表元素編號。

一般來說，元素相關的編號從 0 開始。這表示第 10 個元素會在索引 9 處被存取，第 11 個元素則在索引 10 處，依此類推。

關於相同資料型別的限制很重要，因為陣列是儲存在連續的記憶體格中，這代表每個格子都必須是相同的型別，因此也必須是相同的大小。

## **Struct**

[Struct](https://www.alchemy.com/overviews/solidity-struct) 讓程式設計師可以定義自己的資料型別。一旦定義了 struct，它就可以作為狀態變數使用，或用於許多其他函式中，可以使用位置引數或關鍵字的方式來使用。第二種方式可以避免需要記住 struct 中所包含成員的順序。

**以下是一個新 struct 的範例：**

## **Mapping**

Mapping 可以視為鍵與值的儲存方式，每一個可能的鍵都存在，而任何值都可以透過該鍵在一次操作中設定或取得。**KeyType** 可以是任何內建的值型別（例如 bytes、string，或任何 contract/enum 型別）。**ValueType** 則可以是任何型別，包括 mapping、陣列及 struct。

Mapping 可以按以下方式宣告：

注意：mapping 變數唯一允許的資料位置是 storage。

## **Member**

要在陣列中找到特定成員，意味著要搜尋整個陣列直到找到該成員為止。開發者應該了解兩個重要的函式：`length` 和 `push`。

- **Length**——回傳陣列的大小，也可以用來改變動態陣列的大小。
- **Push**——讓開發者可以直接在動態儲存陣列的末端附加一個元素，並回傳陣列新的長度。

## Solidity 陣列疑難排解

開發者偶爾可能會遇到編譯錯誤，這些錯誤可能源自於對宣告、建立及初始化陣列規則的誤解。以下是排解 Solidity 陣列常見錯誤的一些高階方法：

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

`array.length` 函式讓開發者可以檢查陣列中存在的元素數量。記憶體陣列的大小在宣告時就已固定，而動態陣列則是在執行期間定義的，因此進行操作時需要用到 length。

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

當呼叫外部合約的函式時，EVM 會計算出一個包含函式簽章與引數的 bytes 緩衝區，而序列化引數的方式有兩種：[`abi.encode` 與 `abi.encodePacked`](https://www.alchemy.com/overviews/solidity-abi)。

**Abi.encode** 依照 ABI 規範來編碼其參數。實際上，ABI 的設計目的是用來呼叫合約，其中參數會被填補至 32 bytes。如果是要呼叫合約，最有可能使用的就是這個函式。

相比之下，**abi.encodePacked** 使用該型別所需的最小空間來編碼其參數。編碼一個 unit8 只會用到 1 byte，當你想節省一些空間且不打算呼叫合約時，就會使用這個函式。

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

這兩種陣列型別都可以儲存任意長度的原始位元組資料。`byte\[\] ` 與 bytes 的差別在於，`bytes\[\]` 遵循陣列型別的規則，而 Solidity 中記憶體陣列的元素永遠佔用 32 bytes 的倍數空間。這表示如果某個元素不足 32 bytes 的倍數，就會被填補直到達到所需的大小。舉例來說，在使用 byte 陣列時，每個元素可能會浪費多達 31 bytes，而使用 bytes 陣列或 string 則不會發生這種情況。

### **4. String**

這種特定的動態陣列是 UTF-8 資料型別，其功能與其他程式語言不同，Solidity 並未提供取得字串長度、串接兩個字串或比較兩個字串的函式。可以使用 bytes[] 快速將 string 轉換為位元組陣列。

### **5. Solidity 陣列的 null**

雖然 _undefined_、_null_、_nil_、_none_ 等概念在 Python 和 JavaScript 等其他程式語言中固有存在，但在 Solidity 中並不存在這樣的概念。開發者可以將其稱為零值或預設值的概念，因為每個值一旦被建立，就會在記憶體中佔有一個位置，因此必然包含某個內容。

## 如何進一步學習 Solidity 陣列

若想[進一步了解 Solidity 程式語言](https://www.alchemy.com/overviews/learn-solidity)，並熟悉陣列及其他基礎主題，歡迎前往 Alchemy University 的 [Ethereum bootcamp](https://www.alchemy.com/university/courses/ethereum)。Ethereum bootcamp 是一門免費、為期 7 週的程式設計課程，內含引導式影片與實作專案。

如果開發者是程式設計的新手，Alchemy 的 [3 週 JavaScript for Ethereum 課程](https://www.alchemy.com/university/courses/js)是開始 Ethereum bootcamp 前很好的先修課程。
