---
title: "如何在 Yul 中读写打包存储变量"
description: "了解如何在 Yul 中读写打包存储变量"
---

# 如何在 Yul 中读写打包存储变量

[Yul](http://www.alchemy.com/overviews/what-is-yul) 是一种中间编程语言，可以用来在[智能合约](https://www.alchemy.com/overviews/solidity-smart-contract)内部编写一种形式的汇编语言。在了解了 Yul 的语法以及存储的工作原理之后，接下来需要理解如何在 Yul 中读写打包存储变量。

## **如何在 Yul 中读写打包存储变量**

假设你想把 var5 改为 4。我们知道 var5 位于 slot 3，所以你可能会尝试这样写：

用 getValInHex\(3\) 查看，我们发现 slot 3 被重写成了：

`0x0000000000000000000000000000000000000000000000000000000000000004`

这就有问题了，因为现在 var4 被重写成了 0。在这一部分我们会讲解如何读写打包变量，但在此之前需要先多了解一些 Yul 的语法。

如果你还不熟悉这些操作也不用担心，接下来我们会通过示例逐一讲解。

先从 and\(\) 开始。我们拿两个 bytes32 来试一下 and\(\) 操作符，看看它会返回什么。

看输出结果我们会看到：

`0x0000000000000000000000009acc1d6aa9b846083e8a497a661853aae07f0f00` 

原因是 and\(\) 会分别比较两个输入中每一位（bit）的值。

如果两个 bit 都是 1（可以理解为二进制中的激活或未激活状态），那么该 bit 保持原值。

否则就被置为 0。

现在来看 or\(\) 的代码。

这次的输出是：

`0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`

这是因为它只要有一个 bit 是激活状态就会保留。

我们再看看把 mask 变量改成下面这样会发生什么：

`0x00ffffffffffffffffffffff0000000000000000000000000000000000000000`

可以看到输出变成了：

`0x00ffffffffffffffffffffff9acc1d6aa9b846083e8a497a661853aae07f0f00`

注意第一个字节是 0x00，因为两个输入在第一个字节上都没有激活的 bit。

xor\(\) 稍有不同。它要求一个 bit 是激活状态（1），另一个 bit 是未激活状态（0）。

**下面用代码来演示：**

输出是：

`0xffffffffffffffffffffffff0000000000000000000000000000000000000000`

关键区别在于，只有当 0x00 和 0xff 对齐时，输出中的对应位才是激活的。

shl\(\) 和 shr\(\) 的操作方式非常相似。两者都是将输入值按指定的 bit 数进行移位。shl\(\) 向左移位，shr\(\) 向右移位。

来看一段代码！

输出：

ans1: `0x0000ffff00000000000000000000000000000000000000000000000000000000`

ans2: `0x00000000000000000000000000000000000000000000000000000000ffff0000`

先看 ans1。我们对它执行了 16 位（2 字节）的 shr\(\)。可以看到最后两个字节从 0xffff 变成了 0x0000，而前两个字节整体向右移了两个字节。理解了这一点，ans2 就很好理解了：只是把移位方向从右改成了左。

**在写入 var5 之前，我们先写一个函数来读取 var4 和 var5。**

输出分别是预期中的 1 和 2。

要取出 var4，只需用一个 mask 把值设为：

`0x0000000000000000000000000000000000000000000000000000000000000001`

然后返回一个等于 1 的 uint128。

在读取 var5 时，需要把 var4 的部分右移掉。

处理后得到的就是可以返回的值：

`0x0000000000000000000000000000000000000000000000000000000000000002`

需要注意的是，有时候一个存储槽（slot）中打包了两个以上的变量，这种情况下需要同时使用移位和掩码才能正确读取某个值。

**好了，终于可以把 var5 的值改成 4 了！**

第一步是加载存储槽 slot 3。

接下来，我们需要创建一个 mask。

和读取 var4 时类似，我们要把值隔离到：

`0x0000000000000000000000000000000000000000000000000000000000000001`

下一步是把新值格式化到 var5 在槽中的位置，使其变成：

`0x0000000000000000000000000000000400000000000000000000000000000000`

和读取 var5 时不同，这次我们要把值向左移位。

最后，我们用 or\(\) 把两个值合并成 32 字节的十六进制数，并把这个值存回 slot 3。

我们可以调用 getValInHex\(3\) 来检查结果。

它会返回下面这个值，正是我们期望看到的：

`0x0000000000000000000000000000000400000000000000000000000000000001`

很好，现在你已经知道如何读写打包存储槽了！接下来，去了解一下 Yul 中 [Memory](http://www.alchemy.com/overviews/yul-memory)、[Storage](http://www.alchemy.com/overviews/yul-storage) 和 [Smart Contract Calls](http://www.alchemy.com/overviews/yul-contract-calls) 的工作原理。
