---
title: "什么是 Yul?"
description: "了解如何使用 Yul 编写智能合约，以及它与 Solidity 的区别"
---

# 什么是 Yul?

Yul 是一种中间[编程语言](https://www.alchemy.com/overviews/web3-programming-languages)，可以在[智能合约](https://www.alchemy.com/overviews/solidity-smart-contract)内部用它写一种形式的汇编语言。虽然我们通常看到 Yul 用在智能合约内部，但你也可以完全用 Yul 编写一个智能合约。

理解 Yul 可以提升你的智能合约水平，让你了解 [solidity](https://www.alchemy.com/overviews/solidity) 底层发生了什么，这反过来能帮你为用户节省 gas 费用。

我们可以通过以下语法在智能合约中识别出 Yul。

在本文接下来的部分，我们将通过示例讨论使用 Yul 的基础知识，建议你在 Remix 中跟着一起操作。

## **Yul 中的变量赋值、运算与求值**

首先要讲的是简单的运算。Yul 有 \+、-、、/、%、、&lt;、&gt; 和 =。

注意 &gt;= 和 &lt;= 不在 Yul 可用运算符列表中，因为 Yul 没有这些运算符。此外，求值结果不是 true 或 false，而是分别等于 1 或 0。好了，让我们开始学习一些 Yul 吧！

<EmbeddedTable
  table={{
    columns: [
      { key: "1", width: 200, title: "Instruction", dataType: "object" },
      { key: "2", width: 200, title: "Explanation", dataType: "object" },
    ],
    data: [
      {
        "1": { title: "<p>let</p>", tooltip: "", icon: "" },
        "2": { title: "<p>定义变量前必须使用它。由于所有值都是字节，因此不需要指定变量类型。</p>", tooltip: "", icon: "" },
        id: 0,
      },
      {
        "1": { title: "<p>:=</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x = y</p>", tooltip: "", icon: "" },
        id: 1,
      },
      {
        "1": { title: "<p>add(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x + y</p>", tooltip: "", icon: "" },
        id: 2,
      },
      {
        "1": { title: "<p>sub(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x - y</p>", tooltip: "", icon: "" },
        id: 3,
      },
      {
        "1": { title: "<p>mul(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x * y</p>", tooltip: "", icon: "" },
        id: 4,
      },
      {
        "1": { title: "<p>div(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x / y（若 y 等于 0 则为 0）</p>", tooltip: "", icon: "" },
        id: 5,
      },
      {
        "1": { title: "<p>mod(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x % y（若 y 等于 0 则为 0）</p>", tooltip: "", icon: "" },
        id: 6,
      },
      {
        "1": { title: "<p>lt(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x < y</p>", tooltip: "", icon: "" },
        id: 7,
      },
      {
        "1": { title: "<p>gt(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x > y</p>", tooltip: "", icon: "" },
        id: 8,
      },
      {
        "1": { title: "<p>eq(x,y)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x == y</p>", tooltip: "", icon: "" },
        id: 9,
      },
      {
        "1": { title: "<p>iszero(x)</p>", tooltip: "", icon: "" },
        "2": { title: "<p>Solidity 等价写法：x == 0</p>", tooltip: "", icon: "" },
        id: 10,
      },
    ],
  }}
/>

在继续之前，我们先来快速看一个例子。

## **Yul 中的 for 循环与 if 语句**

为了学习 for 循环和 if 语句，我们来写一个函数，统计一串数字中有多少个偶数。

if 语句的语法与 Solidity 类似，但条件不需要放在括号里。

对于 for 循环，注意在声明 _i_ 和递增 _i_ 时我们使用了括号，但在判断条件时没有使用。此外，我们用 _continue_ 跳过了循环的某一次迭代。在 Yul 中我们也可以使用 break 语句。

## **Yul 中存储的工作原理**

一个智能合约有 2²⁵⁶ 个存储槽（slot）。声明变量时，我们从槽 0 开始并依次递增。每个槽长 256 位（32 字节）；这也是 uint256 和 bytes32 名称的由来。所有变量都会被转换为十六进制。如果使用的是像 uint128 这样的变量，我们不会用整个槽来存储它，而是在左侧用 0 补齐。

想了解更多[关于 Yul 存储的工作原理](http://www.alchemy.com/overviews/yul-storage)，请阅读我们另一篇涵盖以下内容的文章：

1. 数组
1. 动态数组
1. 映射（Mappings）
1. 嵌套映射

## **如何在 Yul 中读写打包（packed）变量**

打包变量指的是对变量进行排序，使它们的存储总量小于 32 字节，从而能够一起放入同一个槽中！打包变量可以节省 gas，是智能合约优化的一项重要技巧。

例如，`0x000000000000000000000000000002` 和 `0x000000000000000000000000000001` 可以完美地放入同一个槽，因为它们各占 16 字节（半个槽）。

想了解更多[如何在 Yul 中读写打包存储变量](http://www.alchemy.com/overviews/yul-packed-storage-variables)，请阅读我们的深度解析文章！

## **Yul 中内存是如何工作的？**

内存的行为与存储不同，它不是持久化的（也就是说，函数执行完毕后，所有变量都会被清空）。Yul 中内存的一些用途包括：

1. 为外部调用返回值
1. 为外部调用设置函数值
1. 从外部调用中获取值
1. 使用错误字符串进行 revert
1. 记录日志消息

想了解更多[关于 Yul 内存的工作原理](http://www.alchemy.com/overviews/yul-memory)，请阅读我们另一篇深入探讨该主题的文章！

## **Yul 中合约调用是如何工作的？**

一旦你理解了 Yul 的语法、存储以及内存的工作原理，接下来就该学习如何调用合约了。我们 Yul 系列的最后一篇文章讲解了[在 Yul 中如何调用合约](http://www.alchemy.com/overviews/yul-contract-calls)，具体包括：

1. `call()` - 调用地址 a 处的合约，出错时返回 0，成功时返回 1
1. `staticcall()` - 与 call\(g, a, 0, in, insize, out, outsize\) 相同，但不允许状态修改
1. `delegatecall()` - 与 callcode 相同，但同时保留 caller 和 callvalue‍

关于合约调用定义的更多信息，可参阅 [Yul 语言文档](https://docs.soliditylang.org/en/v0.8.19/yul.html#yul-call-return-area)。
