---
title: "Yul 中的記憶體如何運作？"
description: "了解 Yul 智能合約程式語言中記憶體的運作方式"
---

# Yul 中的記憶體如何運作？

[Yul](http://www.alchemy.com/overviews/what-is-yul) 是一種中階程式語言，可以用來在[智能合約](https://www.alchemy.com/overviews/solidity-smart-contract)內撰寫組合語言。學會 Yul storage 以及如何讀寫 packed storage 變數之後，接下來要學習的是 memory 在 Yul 智能合約中如何運作。

## **Yul 中的 memory 如何運作？**

Memory 的行為和 [storage](http://www.alchemy.com/overviews/yul-storage) 不同。Memory 不是持久性的，也就是說函式執行結束後，所有變數都會被清除。

Memory 可以比擬為其他語言中的 heap，但沒有 garbage collector。

Memory 比 storage 便宜很多。前 22 個 word 的 memory 成本是線性計算的，但要小心，超過這個範圍後成本會變成二次方成長。

Memory 是以 32 byte 為單位排列的。稍後會更深入說明，但現在先了解 0x00 - 0x20 是一個序列（可以想成是一個 slot，如果這樣理解比較容易，但兩者其實不同）。

[Solidity](https://www.alchemy.com/overviews/solidity) 將 0x00 - 0x40 配置為 scratch space。這段 memory 不保證是空的，會用於某些運算。

0x40 - 0x60 儲存的是 free memory pointer 的位置，用來寫入新資料到 memory 中。

0x60 - 0x80 是留空的間隔。

0x80 是我們開始進行操作的地方。

Memory 不會將數值 packed 在一起。從 storage 取出的值會各自存放在獨立的 32 byte 序列中（例如 0x80-0xa0）。

### **Yul 中哪些運算會用到 memory？**

以下運算會用到 memory：

- External call 的回傳值
- External call 的函式設定值
- External call 取得的數值
- 用錯誤訊息 revert
- Log 訊息
- 用 keccak256\(\) 進行雜湊運算
- 建立其他智能合約

**以下是一些常用於 memory 操作的 Yul 指令：**

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Instruction", dataType: "object" },
      { key: "2", width: 200, title: "Explanation", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>mload(p)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>類似 sload()，但意思是讀取 p 之後的下 32 bytes</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>mstore(p, v)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>類似 sstore()，但意思是把數值 v 存到 p 到 p + 32 bytes 的位置</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>mstore8(p, v)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>類似 mstore()，但只針對單一 byte</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>msize()</p>", tooltip: "", icon: "" },
        "2": { title: "<p>回傳目前已存取過最大的 memory index</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>pop(x)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>捨棄數值 x</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>return(p, s)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>結束執行，並回傳位於 memory 中 p - v 位置的資料</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>revert(p, s)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>結束執行且不儲存任何狀態變更，並回傳位於 memory 中 p - v 位置的資料</p>", tooltip: "", icon: "" },
        id: 6,
      },
    ],
  }}
/>

接著我們來看看更多資料結構！

### **如何在 Yul 中使用 struct 和 memory**

Struct 和 fixed array 的行為其實一樣，但由於我們已經在 storage 章節看過 fixed array，這裡就來看看 struct。請看以下這個 struct。

這裡沒有什麼特別之處，就是一個簡單的 struct。

現在來看一些程式碼！

這裡我們把 s.subVar1 設定在 memory 位置 0x80 - 0xa0，把 s.subVar2 設定在 memory 位置 0xa0 - 0xc0。這就是為什麼我們回傳的是 0x80 - 0xc0。以下是交易結束前的 memory 配置表。

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Memory Location", dataType: "object" },
      { key: "2", width: 200, title: "Value Stored", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>0x00</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space（空）</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>0x20</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space（空）</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>0x40</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0xc0（Free Memory Pointer）</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>0x60</p>", tooltip: "", icon: "" },
        "2": { title: "<p>空</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>0x80</p>", tooltip: "", icon: "" },
        "2": { title: "<p>s.subVar1: 0x20（十進位為 32）</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>0xa0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>s.subVar1: 0x40（十進位為 64）</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>0xc0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>新的 Free Memory Pointer。這就是 msize() 會回傳的值。（空）</p>", tooltip: "", icon: "" },
        id: 6,
      },
    ],
  }}
/>

**從中可以歸納出以下重點：**

- 0x00 - 0x40 為 scratch space，是空的
- 0x40 給我們 free memory pointer
- Solidity 在 0x60 留了一個間隔
- 0x80 和 0xa0 用來儲存 struct 的值
- 0xc0 是新的 free memory pointer

在 memory 這個章節的最後一部分，我要示範 dynamic array 在 memory 中是如何運作的。這個例子中我們會傳入 \[0, 1, 2, 3\] 作為參數 arr。作為額外示範，我們還會在這個陣列中新增一個元素。在正式環境這樣操作時要特別小心，因為可能會覆寫到其他 memory 變數。

**程式碼如下：**

**這裡發生的事情是：**

- 取得陣列在 memory 中的儲存位置
- 取得陣列的長度，這儲存在陣列的第一個 memory 位置
- 將位置加上 32 bytes（跳過陣列長度）以找到下一個可用位置
- 將陣列長度乘以 32 bytes，移動到下一個 memory 位置
- 儲存我們的新值（4）
- 將陣列長度更新加一
- 更新 free memory pointer
- 回傳陣列

我們再看一次 memory 的配置。

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Memory Location", dataType: "object" },
      { key: "2", width: 200, title: "Value Stored", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>0x00</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space（空）</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>0x20</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Scratch Space（空）</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>0x40</p>", tooltip: "", icon: "" },
        "2": { title: "<p>0x140（Free Memory Pointer）</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>0x60</p>", tooltip: "", icon: "" },
        "2": { title: "<p>空</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>0x80</p>", tooltip: "", icon: "" },
        "2": { title: "<p>陣列的新長度（6）</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>0xa0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>arr[0]（0）</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>0xc0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>arr[1]（1）</p>", tooltip: "", icon: "" },
        id: 6,
      },
      {
        "1": { title: "<p>0xe0</p>", tooltip: "", icon: "" },
        "2": { title: "<p>arr[2]（2）</p>", tooltip: "", icon: "" },
        id: 7,
      },
      {
        "1": { title: "<p>0x100</p>", tooltip: "", icon: "" },
        "2": { title: "<p>arr[3]（3）</p>", tooltip: "", icon: "" },
        id: 8,
      },
      {
        "1": { title: "<p>0x120</p>", tooltip: "", icon: "" },
        "2": { title: "<p>arr[4]（4）</p>", tooltip: "", icon: "" },
        id: 9,
      },
      {
        "1": { title: "<p>0x140</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Free Memory Pointer（空）</p>", tooltip: "", icon: "" },
        id: 10,
      },
    ],
  }}
/>

Memory 章節到此結束！

接下來，可以學習[如何用 Yul 呼叫智能合約](http://www.alchemy.com/overviews/yul-contract-calls)，或回去複習[如何讀寫 packed storage 變數](http://www.alchemy.com/overviews/yul-packed-storage-variables)！
