Yul 中的内存是如何工作的?
Yul 是一种中间编程语言,可用于在 智能合约 内编写一种形式的汇编语言。在了解了 Yul storage 以及如何读写打包的 storage 变量之后,接下来需要学习 Yul 智能合约中 memory 的工作方式。
Yul 中 memory 是如何工作的?
memory 的行为与 storage 不同。memory 不是持久化的,这意味着函数执行结束后,所有变量都会被清除。
memory 可以类比为其他语言中的 heap,但它没有垃圾回收机制。
memory 比 storage 便宜得多。前 22 个字(word)的 memory 成本是线性计算的,但要注意,超过这个范围后 memory 成本会变为二次方增长。
memory 以 32 字节为一段进行布局。稍后我们会更详细地了解这一点,但现在只需知道 0x00 - 0x20 是一个段(可以把它想象成一个 slot,如果这样理解更方便的话,但它们其实是不同的东西)。
Solidity 将 0x00 - 0x40 分配为暂存空间(scratch space)。这部分 memory 不保证为空,会被用于某些操作。
0x40 - 0x60 存储的是所谓的空闲 memory 指针(free memory pointer)的位置,用于向 memory 写入新内容。
0x60 - 0x80 留空,作为间隔。
0x80 是我们开始操作的位置。
memory 不会对值进行打包。从 storage 中取出的值会各自存储在独立的 32 字节段中(例如 0x80-0xa0)。
Yul 中哪些操作会用到 memory?
以下操作会用到 memory:
- 外部调用的返回值
- 为外部调用设置函数值
- 从外部调用获取值
- 使用错误字符串 revert
- 记录日志消息
- 使用 keccak256() 进行哈希运算
- 创建其他智能合约
下面是一些常用的 Yul memory 指令:
接下来我们再看几种数据结构!
如何在 Yul 中使用 struct 和 memory
struct 和固定长度数组的行为其实是一样的,但由于我们在 storage 部分已经看过固定长度数组了,这里我们来看 struct。看看下面这个 struct。
没什么特别的,就是一个简单的 struct。
现在来看一些代码!
这里我们将 s.subVar1 设置到 memory 位置 0x80 - 0xa0,将 s.subVar2 设置到 memory 位置 0xa0 - 0xc0。这就是为什么我们返回的是 0x80 - 0xc0。下面是交易结束前 memory 布局的一张表。
从中可以总结出以下几点:
- 0x00 - 0x40 作为暂存空间留空
- 0x40 处存放的是空闲 memory 指针
- Solidity 会在 0x60 处留出一段间隔
- 0x80 和 0xa0 用于存储 struct 的值
- 0xc0 是新的空闲 memory 指针
在 memory 部分的最后,我想给大家展示动态数组在 memory 中的工作方式。在这个例子中,我们会传入 [0, 1, 2, 3] 作为参数 arr。作为附加内容,我们还会给数组追加一个新元素。在生产环境中这样做要小心,因为可能会覆盖其他 memory 变量。
代码如下:
下面是执行过程:
- 获取数组在 memory 中的存储位置
- 获取数组的长度,它存储在数组的第一个 memory 位置
- 在该位置基础上加 32 字节(跳过数组长度部分),得到下一个可用位置
- 将数组长度乘以 32 字节,得到下一个 memory 位置
- 存储我们的新值(4)
- 将数组长度更新加一
- 更新空闲 memory 指针
- 返回该数组
我们再来看一次 memory 布局。
memory 部分到此结束!
接下来,学习 如何使用 Yul 调用智能合约,或者返回学习 如何读写打包的 storage 变量!
相关概览

构建区块链应用
Alchemy 将最强大的 Web3 开发者产品和工具与资源、社区及专业支持结合在一起。


