> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md).

# \expandafter 是如何工作的：TeX 标记简介

[第1部分](/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [第2部分](/latex/zh-cn/shen-ru-wen-zhang/22-how-does-expandafter-work-the-meaning-of-expansion.md) [第3部分](/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [第4部分](/latex/zh-cn/shen-ru-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [第5部分](/latex/zh-cn/shen-ru-wen-zhang/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [第6部分](/latex/zh-cn/shen-ru-wen-zhang/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)

## \expandafter 的背景：TeX 记号和记号列表

作为理解如何……的第一步 `\\expandafter` 真正如何工作，我们将看看 TeX 的两个组成部分，它们对……的运作至关重要 `\\expandafter`：TeX 记号（整数）和记号列表（整数列表）。想更深入探索这些主题的读者，可能会对阅读 Overleaf 发表的以下文章感兴趣：

* [什么是 TeX 记号？](/latex/zh-cn/shen-ru-wen-zhang/53-what-is-a-tex-token.md)
* [什么是 TeX 记号列表？](/latex/zh-cn/shen-ru-wen-zhang/54-what-is-a-tex-token-list.md)-[TeX 宏究竟是如何工作的？](/latex/zh-cn/geng-duo-zhu-ti/01-a-six-part-series-how-do-tex-macros-actually-work.md)

### 记号数据从哪里来？

在本文中，我们使用由 TeX 计算出的实际记号值——这些数据通常对用户不可见。对于想知道这些记号值数据是如何获得的读者，Overleaf 为多个 TeX 引擎提供了定制构建版本，供我们用于研究。这些引擎经过修改后会输出 TeX 内部处理活动的信息——这也有助于为我们撰写的一些文章提供额外背景材料。通过展示/讨论数值记号值，我们的目标是加入一些细节，希望能帮助读者更好地理解“TeX 记号”，让这个重要概念显得不那么晦涩。

## TeX 记号 101（以及展开的概念）

当 TeX 处理你的输入文件时，它会读取文本，并将单个字符以及字符序列（命令）转换为所谓的 *记号*。TeX 记号只是一个由 TeX 计算出的整数值，用于对 TeX 需要从当前输入源中读取并存储的某个项目的数据进行“编码”。你可以把记号看作小小的信息包，它们把 TeX 需要记录的数据打包在一起，准备传递到下一处理阶段。在内部，TeX 处理的是这些整数记号值——它并不使用输入文件中最初包含的实际字母、符号、数字等：一切都会被转换成一个记号（整数），TeX 再对这些记号进行处理。

## TeX 如何计算记号值

这里我们讨论的是 Knuth 原版 TeX、e-TeX 和 pdfTeX 中使用的记号计算方式；对于其他 TeX 引擎，尤其是 XeTeX 和 LuaTeX，由于使用了 Unicode，记号计算需要稍有不同，但计算方法与下文所述相似。

### 字符记号（非活动字符）

非活动字符的记号值计算很直接：

$$\text{character token} = 256\times \text{(category code)} + \text{character (ASCII) code}$$

**示例**：字母 A 具有 [类别码](/latex/zh-cn/geng-duo-zhu-ti/43-table-of-tex-category-codes.md) 11，字符码 65，在 TeX 中表示为字符记号值 $$256\times 11 + 65 = 2881$$.

你可能会在 TeX 文献中看到这样的描述：一旦 TeX 输入了某个字符，其类别码值就会“永久绑定”到该字符上：上面的记号值计算就说明了这一点。不过，在 TeX 之后的处理过程中，它可以，而且确实会，对字符记号进行“解包”，以揭示构成该记号的（字符码，类别码）对——当 TeX 进行这种“解包”时，它仍然不会改变该字符的类别码，只是在后续处理时使用这些信息。

### 命令记号

TeX 的输入处理和记号生成会识别两类命令：

* 由一个或多个类别码为 11 的字符构成的命令；
* 单字符命令，其中该字符的类别码不是 11；例如 `\$` 或 `\#`.

在这两种情况下，TeX 都会排除开头的 `\` 字符，并使用其余每个字符的字符码来计算一个整数，TeX 将其称为 `curcs` (**cur**rent **c**ontrol **制**序列）。随后 TeX 使用 `curcs` 来为该命令计算一个记号值。

#### 由类别码为 11 的字符构成的命令

假设我们的命令（去掉开头的 `\` 字符）由以下字符序列组成： $$\mathrm{C\_1C\_2C\_3...C\_N}$$ 其中 $$\mathrm{C}\_i$$ 是每个字符的字符码——例如，A 的字符码是 65。TeX 使用所有字符码 $$\mathrm{C}\_i$$ 来计算整数 `curcs` （使用 [哈希函数](https://en.wikipedia.org/wiki/Hash_function)）。一旦 TeX 计算出 `curcs` 的值，它只需在该值上加 4095，即可得到记号值：

$$\text{command token} = \text{curcs + 4095}$$

注意，变量 `curcs` 在 TeX 的内部处理活动中扮演着极其重要的角色。

#### 单字符命令

用于表示诸如……之类命令的记号 `\$`, `\#` 等则要采用略有不同的计算：整数 `curcs` 现在计算更简单：

$$\text{curcs} = 257 + \text{character (ASCII) code}$$

例如，对于 `\$`, $$\text{curcs}=257 + 36 = 293$$。TeX 同样会在该值上加 4095（使用 $$\text{command token} = \text{curcs} + 4095$$）从而得到 `\$` 的记号值为 $$293 + 4095 = 4388$$.

与由类别码为 11 的字符构成的命令相比，这里的唯一区别在于 TeX 计算……的值的方式 `curcs`.

**注意**：整数 `curcs` 不会为字符记号计算：当 TeX 创建或处理字符记号时，它始终设为 0。

#### 活动字符记号

TeX 有一个所谓的 *活动字符*：任何被指定为类别码 13 的字符。与普通字符相比，这类特殊字符的记号要采用不同的计算方式。

活动字符机制使 TeX 能创建实际上相当于单字符宏的东西，你可以直接使用 *没有* 而无需在活动字符前加上转义字符（通常是 `\`）：这个单独的字符就会触发其宏行为。典型例子是波浪号字符 (\~)，TeX/LaTeX 用它表示不换行空格，可以如下定义/启用：

```
\catcode`~=13 % 将类别码 13 赋给 ~
\def~{\penalty100000\ } % 将 ~ 定义为宏
```

当 TeX 随后读取一个 `~` 字符时，它会检测到其类别码为 13，并将其作为“迷你宏”处理。要计算表示活动字符的记号，TeX 会采用另一种变体来计算 `curcs`:

$$\begin{align\*} \text{curcs} &= \text{character code} + 1\ \text{active character token} &= \text{curcs} + 4095\ \end{align\*}$$

例如，\~ 字符的字符码是 126，这意味着其活动字符记号值表示计算如下：

$$\begin{align\*} \text{curcs} &= 126 + 1\ \text{active character token} &= 127 + 4095\ &=4222\ \end{align\*}$$

注意，与命令一样，表示活动字符的记号大于 4095。

### 后果/说明

* 任何值超过 4095 的记号都能立即识别为命令记号——因此 TeX 可以非常轻松地检测某个记号表示的是字符还是命令。
* 对于任何记号值，TeX 在需要时都可以“解包”该记号，以揭示最初出现在你的 `.tex` 文件中、宏定义里，或其他记号列表中所包含的内容。
* 称为“中间”量的 `curcs`——TeX 用它来计算命令记号值——在 TeX 的底层处理中起着重要作用。 `curcs` 充当 TeX 用来存储/查找某个命令当前含义的“索引值”。给定任何命令记号， $$\mathrm{T}$$，TeX 只需减去 4095 即可访问 `curcs`: $$\text{curcs} = \mathrm{T}-4095$$

顺便说一句，TeX 确实会存储生成命令记号所对应的人类可读字符字符串——这对错误报告以及诸如 `\string` 等命令至关重要，这些命令展开后就是记号值的人类可读版本。不过，TeX 内部存储的人类可读字符字符串只有在请求时才会使用/输出：在其他所有处理中，使用的都是记号整数值。

## 看看一些真实的记号

为了让“记号”这个概念不那么晦涩，我们来定义下面这个简单宏，并看看 TeX 生成的记号：

```
\def\hello{问候，来自 \TeX。 \hskip 10pt}
```

对于 `\hello` 宏，TeX 使用字符 `h`, `e`, `l`, `l`, `或` 来计算出 3745 的值，用于 `curcs`；随后 TeX 再加上 4095，生成记号值 $$3745 + 4095 = 7840$$ （适用于 Knuth 的 TeX、e-TeX 或 pdfTeX）。

在创建一个用于表示 `\hello`， `\def` 命令的记号后，TeX 会读取后续记号，并用它们创建一个记号列表，该列表被存储为 *定义* 的 `\hello` 命令。之后，只要你让 TeX 使用该 `\hello` 命令。

下表列出了在以下 `\hello` 宏定义中包含的每个项目（字符、宏或原语）所生成的实际记号值——这份记号（整数）列表就是 TeX 存储在其内存中的内容（作为一种名为 [链表](https://en.wikipedia.org/wiki/Linked_list)）的数据结构。想更深入理解记号列表的读者，可参阅 Overleaf 文章 [什么是 TeX 记号列表？](/latex/zh-cn/shen-ru-wen-zhang/54-what-is-a-tex-token-list.md)

|             |            |
| ----------- | ---------- |
| **TeX 记号值** | **所表示的项目** |
| 2887        | G          |
| 2930        | r          |
| 2917        | e          |
| 2917        | e          |
| 2932        | t          |
| 2921        | i          |
| 2926        | n          |
| 2919        | g          |
| 2931        | 制          |
| 3116        | ,          |
| 2592        |            |
| 2918        | f          |
| 2930        | r          |
| 2927        | 或          |
| 2925        | m          |
| 2592        |            |
| 5235        | \TeX       |
| 3118        | .          |
| 2592        |            |
| 7943        | \hskip     |
| 3121        | 1          |
| 3120        | 0          |
| 2928        | p          |
| 2932        | t          |

在上面的记号列表中，这些字符的类别码为 10、11 或 12。例如：

* 字符的类别码为 10，字符码为 32，因此得到的记号值为 $$256\times 10 + 32 = 2592$$
* `,` 和 `.` 的类别码为 12，字符码分别为 44 和 46，因此得到记号：
* 用于 `,` $$= 256 \times 12 + 44 = 3116$$
* 用于 `.` $$= 256\times 12+ 46 = 3118$$

每当 TeX 之后遇到记号值 7840（表示 `\hello`） `curcs` 通过简单的计算 $$\text{curcs} = \text{token value} - 4095$$ （见上文）。使用 `curcs` ，TeX 可以查阅其内部数据表，确定命令记号 7840 代表一个宏命令。此外，再通过 `curcs`，TeX 还可以查找并取回存储的 `\hello`.

当 TeX 需要完整处理记号 7840，也就是运行该 `\hello` 宏时，它就不再需要记号 7840：该记号已经 *完成了它的任务*——也就是它触发 TeX 运行该宏 `\hello`。TeX 现在可以丢弃记号 7840，并取出表示存储在内存中的定义（记号列表）的记号。实际上，这个 `\hello` 宏命令（记号 7840）已经被 *移除* 出 TeX 当前输入源，并 *替换为* 由包含在……定义中的记号 `\hello`。 *记号展开*.

该 `\TeX` 命令（上面列出的记号值 5235）在 `\hello` 本身也是由更多记号构成的一个宏——因此它的定义也作为记号列表存储：

|             |            |
| ----------- | ---------- |
| **TeX 记号值** | **所表示的项目** |
| 2900        | T          |
| 19598       | \kern      |
| 3117        | -          |
| 3118        | .          |
| 3121        | 1          |
| 3126        | 6          |
| 3126        | 6          |
| 3127        | 7          |
| 2917        | e          |
| 2925        | m          |
| 19597       | \lower     |
| 3118        | .          |
| 3125        | 5          |
| 2917        | e          |
| 2936        | x          |
| 6175        | \hbox      |
| 379         | {          |
| 2885        | E          |
| 637         | }          |
| 19598       | \kern      |
| 3117        | -          |
| 3118        | .          |
| 3121        | 1          |
| 3122        | 2          |
| 3125        | 5          |
| 2917        | e          |
| 2925        | m          |
| 2904        | X          |

如果我们要把 `\hello` 命令替换为构成它的完整记号列表，包括 `\TeX` 宏，那么它将是一个相当长的列表——也就是说，如果我们还要 *展开* 该 `\TeX` 该宏，我们会看到：

![存储在 TeX 宏中的记号列表](/files/bf8d0f33de8993b3e3a62fbcb8f5a1fe1bac0d7a)

本质上，单个记号值 7840（对应 `\hello`）在完全展开后会产生共 51 个记号（整数），表示字符和原语命令。在下面的列表中，每个记号所表示的字符或命令都用括号“(...)”标出——这些内容并不是直接存储在 TeX 的记号列表中的，显示出来是为了帮助读者：

```
2887 (G), 2930 (r), 2917 (e), 2917 (e), 2932 (t), 2921 (i), 2926 (n), 2919 (g), 2931 (s), 3116 (,), 2592 (<space>), 2918 (f), 2930 (r), 2927 (o), 2925 (m), 2592 (<space>),  2900 (T), 19598 (\kern), 3117 (-), 3118 (.), 3121 (1), 3126 (6), 3126 (6), 3127 (7), 2917 (e), 2925 (m), 19597 (\lower), 3118 (.), 3125 (5), 2917 (e), 2936 (x), 6175 (\hbox), 379 ({), 2885 (E), 637 (}), 19598 (\kern), 3117 (-), 3118 (.), 3121 (1), 3122 (2), 3125 (5), 2917 (e), 2925 (m), 2904 (X), 3118 (.), 2592 (<space>), 7943 (\hskip), 3121 (1), 3120 (0), 2928 (p), 2932 (t)
```

对于人类读者来说，这只是一串整数，但对 TeX 来说，它编码了大量信息。

## 现在读取记号并将它们保存供以后使用

当 TeX 读取你的输入时，有时它可能需要（或被要求）延迟对某一组特定记号的完整处理。如果被这样指示，TeX 就会在你告诉它停止之前，继续从输入中创建记号，但把它们保存起来，以便稍后使用——随后再把它们取出并作为排版活动的一部分进行处理。这些被保存的记号被称为所谓的 *记号列表* 实际上是 TeX 唯一（内部）的记号数据存储机制。

我们已经见过记号列表示例—— `\hello` 和 `\TeX` 上面列出的宏：这些宏的定义以记号列表的形式存储在 TeX 的内存中。只有当你决定调用这些宏时，TeX 才会处理（执行）这些记号列表。还要记住，每个记号（整数值）都编码了足够的信息，使 TeX 能轻松判断宏定义中存储的每个记号表示的是字符还是命令。

### 使用记号寄存器保存记号

记号存储的另一个例子，是显式创建被保存到所谓 *记号寄存器*中的记号列表：TeX 为用户存储记号列表而提供的专用内部存储区域。TeX 原语 `\toksdef` 是使用记号寄存器的一种方式；例如，要使用记号寄存器 `100` 并用命令引用它 `\mylist`:

```
        \toksdef\mylist=100
        \mylist={一些 \TeX{} 记号在此}
```

`\mylist` 实际上只是一个名称，你把它赋给存储在寄存器位置 `100`。 `\mylist` 包含以下记号列表：

|             |            |
| ----------- | ---------- |
| **TeX 记号值** | **所表示的项目** |
| 2931        | 制          |
| 2927        | 或          |
| 2925        | m          |
| 2917        | e          |
| 2592        |            |
| 5235        | \TeX       |
| 379         | {          |
| 637         | }          |
| 2592        |            |
| 2932        | t          |
| 2927        | 或          |
| 2923        | k          |
| 2917        | e          |
| 2926        | n          |
| 2931        | 制          |
| 2592        |            |
| 2920        | h          |
| 2917        | e          |
| 2930        | r          |
| 2917        | e          |

**注意**：为了终止 `\TeX` 宏并防止其吸收后面的 `<space>` 字符，我们使用了一对花括号 `{}` 紧跟在 `\TeX`之后—— `{` （379）和 `}` （637）被存储在记号列表中。另一种做法是使用“控制空格”记号 `\<space>` 它在记号列表中会如下所示（以粗体显示）：

|             |            |
| ----------- | ---------- |
| **TeX 记号值** | **所表示的项目** |
| 2931        | 制          |
| 2927        | 或          |
| 2925        | m          |
| 2917        | e          |
| 2592        |            |
| 5235        | \TeX       |
| **4384**    | **\\**     |
| 2932        | t          |
| 2927        | 或          |
| 2923        | k          |
| 2917        | e          |
| 2926        | n          |
| 2931        | 制          |
| 2592        |            |
| 2920        | h          |
| 2917        | e          |
| 2930        | r          |
| 2917        | e          |

请注意， `<space>` 字符表示为一个 *。通过计算这个会传递给 TeX 内部处理的记号值，该特定* ，其值为 $$256\times 10 + 32 = 2592$$ 但 `\﻿<space>` 被视为一个单字符 *命令记号* （值为 4384），其计算使用上文给出的公式：

\begin{align\*} \text{curcs} & = 257 + \text{字符（ASCII）码}\\\ & = 257 + 32\\\ &=289\\\ \text{<\space> 的命令记号} & = \text{curcs + 4095}\\\ & = 289+4095\\\ &=4384\\\ \end{align\*}

本质上 `\mylist={一些 \TeX{} 记号在此}` 就是在对 TeX 说：请扫描我的输入文件，把下面的字符/命令转换成记号并保存起来，以便日后使用。TeX 会照办，并将这些记号存储到一个内存位置中，你可以通过写出 `\the\mylist`，指示 TeX 插入记号寄存器中所包含记号的一个副本 `\mylist`。 `\everyjob`, `\everypar`, `\mark`，以及许多其他命令。

[第1部分](/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md) [第2部分](/latex/zh-cn/shen-ru-wen-zhang/22-how-does-expandafter-work-the-meaning-of-expansion.md) [第3部分](/latex/zh-cn/shen-ru-wen-zhang/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md) [第4部分](/latex/zh-cn/shen-ru-wen-zhang/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) [第5部分](/latex/zh-cn/shen-ru-wen-zhang/17-how-does-expandafter-work-a-detailed-macro-case-study.md) [第6部分](/latex/zh-cn/shen-ru-wen-zhang/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-wen-zhang/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
