> For the complete documentation index, see [llms.txt](https://ayakaleaf-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://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/54-what-is-a-tex-token-list.md).

# 什么是 TeX 标记列表

## 那么，究竟什么是“TeX 标记列表”？

在一个 [上一篇文章](/latex/zh-cn/shen-du-wen-zhang/53-what-is-a-tex-token.md)——也是本 [关于底层 TeX 细节的系列文章的一部分](/latex/zh-cn/shen-du-wen-zhang/01-a-new-series-of-articles-tex-tokens-and-related-concepts-but-why-and-how.md)——我们探讨了 TeX 扫描你的 `.tex` 文件以生成新标记的过程：我们考察了 TeX 标记的基本性质，以及 TeX 如何创建它们（见 [什么是“TeX 记号”？](/latex/zh-cn/shen-du-wen-zhang/53-what-is-a-tex-token.md)).

在这篇后续文章中，我们来看看 *标记列表*：它们是什么，以及 TeX 引擎如何创建/使用它们。理解标记列表可能有点棘手，因为它们存储在 TeX 内部的深处：这些细节对用户是隐藏的——不过，今天如果你用 LuaTeX 做更高级的编程，这种情况并不总是如此。但目前，你可以先把标记列表理解为 TeX 用来存储一系列整数值的方式，其中每个整数都是一个由 TeX 从你的输入文件中读取的字符或命令派生出来的标记。

标记列表在 TeX 的内部运行中扮演着关键角色，往往以一些令人惊讶的方式出现，例如像 `\uppercase` 和 `\lowercase`。标记列表的一个特别重要用途是存储和执行宏，这个主题我们将在本系列未来文章中详细讨论。

### TeX 从文件和标记列表中获取输入

TeX 引擎有三种输入来源——你可能知道其中两种：

* 存储在磁盘上的物理文本文件；
* 用户在终端（命令行）输入的文本；

但它还有第三种读取/获取输入的方式：标记列表！

实际上，标记列表是 TeX 在操作中使用的一种内部数据存储设施。由于 TeX 的标记列表充当着先前创建的标记的“存储设施”，因此 TeX 将其重新用作另一种输入来源是很合理的。当有必要从某个特定标记列表获取下一段输入时（或者 TeX 被指示这样做时），TeX 会暂时停止从物理文件中读取输入（也就是，创建 *新标记*）并切换为从 *现有标记*：即存放该标记列表的内存位置来获取输入。显然，对于标记列表来说，扫描 + 生成标记的过程已经完成，因此 TeX 只需查看列表中的每个标记，并决定如何处理它们。

作为一个简短的例子，低级（TeX 原语） `\toks` 命令可以让你创建一个标记列表，TeX 会将其保存在内存中以供日后重用：

```latex
\toks100={Hello}
```

要取回这些标记（即，让 TeX 将它们作为下一来源的输入）你可以发出如下命令

```latex
\the\toks100
```

这会使 TeX 从根据你的输入文件创建新标记，切换为从这些标记（由 `\toks`创建）所在的位置继续获取下一段输入——也就是所谓的 *记号寄存器* ，这只是 TeX 已知的一个内部内存位置（这里是寄存器 100）。

此外，TeX 的许多命令还能在运行时内部生成标记列表。一个例子是命令 `\jobname` ，它会生成一系列字符标记——对于 TeX 正在处理的主文件名中的每个字符，都会生成一个标记。另一个例子是 `\string` 命令；例如

```latex
\string\mymacro
```

会为名称中的每个字母生成一系列字符标记 `\mymacro`——包括最初的 `\` 字符。我们会在本文末尾更仔细地看看一些“生成标记的命令”。

## 标记列表：通过类比来解释

除非你有编程背景和/或一些计算机科学知识，否则“标记列表”这个概念可能有些模糊，也许还有点令人困惑。不过，如果你想熟练编写 TeX/LaTeX 宏，那么对 TeX 标记、标记列表以及类别码（`\catcode`）等主题有扎实的理解将会非常有帮助。

在本节中，我们将用一个类比来解释/说明 TeX 标记列表的核心思想/原理：TeX 如何将标记存入内存。值得花点时间读完这一段，因为标记列表是 *基本* 方面，值得稍微深入理解一下。

### 标记列表：一个类比（思想实验）

我们将通过一个“思想实验”来为理解 TeX 标记列表打下基础。设想你可以使用一大批容器，比如几百个罐子——我们不能用“盒子”这个词来描述思想实验中的容器，因为当然，“盒子”在 TeX 中有非常特定的含义，与这里的讨论完全无关。所以我们把容器称为“罐子”，其中每个罐子：

* 外部印有一个唯一的识别编号；
* （内部）被分成两个隔间。

这两个隔间的设计如下：

* 左侧隔间放你想放进罐子的物品；
* 右侧隔间用于放一张纸条，你可以在上面写一个数字：另一个罐子的编号。

![测试](/files/b930570fb33052b02a4a862e5467df96b90b8a29)

假设你有一组，比如说 5 个物品，而你想把这组物品存放在这些罐子里；但遗憾的是，每个罐子一次只能容纳 1 个你想存放的那类物品。

为简单起见，我们假设想存放 5 个彩色圆圈：

![{{{alt}}}](/files/4d566868e04aef1ee49dddf5b6e1d76048f1d2df)

此外，当你回头从你的存储系统（罐子）中取回这些物品时，这些物品 *必须* 必须按特定顺序被取回/找到——也就是它们被存放的顺序：必须保留这个顺序。该怎么实现呢？

我们可以利用每个罐子都：

* 外部贴有一个唯一的识别编号；
* 有 2 个隔间——我们只用其中 1 个来装物品，另一个则装一张写有另一个罐子编号的纸条。

我们假设每个罐子都是空的——不过，你完全可以打开任意一个罐子检查它是否为空；如果不空，就试下一个，直到找到空罐子。

我们可以这样做。把第一个物品（深绿色圆圈）放入某个罐子（例如罐子 124），并记下这个第一个罐子的编号——第一个罐子具体是多少号并不重要，重要的是我们把它记下来并保存起来，以备后用。

![{{{alt}}}](/files/0fd6c882e146abfb4b681174ffd6d0bd3afea95a)

找到第二个罐子——任意一个编号都可以（例如罐子 432）——并记下它的编号。把第二个罐子的编号（432）写在一张纸条上，然后把这张纸条放进 *第一个罐子* （罐子 124）里。把第二个物品（浅绿色圆圈）放进第二个罐子。于是，我们目前有如下情况：

* 一张写好的纸条——没有放在任何罐子里——写着第一个罐子的编号是 124（它包含我们的第一个物品）；
* 在罐子 124 中，我们又放了一张纸条，说明下一个物品在罐子 432 中。

本质上，我们已经 *连接起来* 了前两个罐子：我们知道从哪里开始（罐子 124），而且罐子 124 里的纸条告诉我们下一个物品在哪个罐子里（罐子 432）。

![{{{alt}}}](/files/746d54624e440e8037f9786bf44db4028ada13bc)

接着我们找到第三个罐子，把它的编号（例如罐子 543）写在纸上，然后放进 *第二个* 罐子（编号 432）里。然后我们把第三个物品（红色圆圈）放进第三个罐子。

现在我们把三个罐子按顺序连接起来了：起点罐子 124（深绿色圆圈）→ 罐子 432（浅绿色圆圈）→ 罐子 543（红色圆圈）→…

![{{{alt}}}](/files/114a9a8d0664ea6a4c378ae0bbb308340db37262)

对最后两个物品（浅蓝色和深蓝色圆圈）重复这一过程，使用罐子 213（浅蓝色圆圈）和罐子 102（深蓝色圆圈）。

![{{{alt}}}](/files/062ff0d99ee8be267a6df59c6f6cb1ff37d9471c)

现在我们已经把全部 5 个罐子链接在一起（使用每个罐子的编号），并且可以按正确顺序取回所有已存放的物品——只需依次访问每个罐子，取出我们的物品，并查看纸条，纸条会告诉我们下一个物品在哪个罐子里。

### 那么我们列表中的最后一个物品（罐子 102）呢？

为什么我们要特别关心这个呢？到目前为止，我们已经把每个物品存进一个罐子，并附上一张说明下一个物品所在罐子的纸条：那么对于列表中的最后一个物品，这张纸条该写什么——因为并不存在下一个罐子。

当我们到达最后一个物品（罐子）时，必须很明显地知道这个罐子（装着最后一个物品）是列表中的最后一项——我们不需要再去找另一个罐子，因为根本没有。实现这一点的一种方法是在最终罐子（102）里放一个“特殊”的罐子编号。我们可以使用任何我们想要的数字，只要选的是一个不是真实罐子编号的唯一数字即可——例如“罐子 -1”、“罐子 0”：只要我们知道“罐子 -1”或“罐子 0”等会立即告诉我们停止查找就行了：我们不需要再找任何罐子，因为这就是最后一个，因此也没有更多物品可取回。

### 从“物品”和“罐子”到标记和 TeX

现在我们需要从类比回到更接近 TeX 实际情况的描述。首先，不再是把不同颜色的圆圈存进我们想象中的罐子里，应该很清楚，我们可以把这些罐子视为存放 TeX 标记：简单的整数。这是把类比转向软件（TeX）领域较容易的部分。但我们的实体编号罐子带“隔间”的对应物会是什么呢？

我们不想过多深入编程概念，但你可以把我们的“罐子”看作若干字节的计算机内存，这些字节被“打包”成了一个方便的存储单元。类比中对每个罐子使用数字编号，可以看作计算机内存中每个小内存包所在的位置。在 TeX 本身中，这些小存储包被称为“内存字”——这个术语反映了 TeX 被创造出来的时代/年代（20 世纪 70 年代）。这些“内存字”是 TeX 内部使用的基本构件，但这里我们不需要更深入地探讨它们——想了解更多细节的人可以参考一篇关于 [作者个人博客](http://www.readytext.co.uk/?p=3537).

用计算机编程术语来说，我们刚才讨论的是一个 [*链表*](https://en.wikipedia.org/wiki/Linked_list)：TeX 标记列表是由 TeX 的存储容器构成的一个链表，称为 *内存字* ，其中每个内存字都可以用来存储：

* a *值*：标记的值（一个整数）；
* a *链接*：下一个内存字的内存位置，其中包含我们列表中的下一个标记。

## TeX 在哪里使用标记列表？

无处不在！这是因为 TeX/LaTeX 宏定义（例如一个 LaTeX 命令）以（稍作专门化的）标记列表形式存储——这里的“专门化”是指它包含了一些你在“标准”标记列表中看不到的标记（与匹配宏参数等有关）。不用担心这个，因为我们会在未来的文章中讨论这些细节。

### 一个宏的例子

一个宏可以被看作由三个部分组成：

```
\def\<macro name><parameter text>{<replacement text>}
```

注意，除了 `\def` ，你也可以使用 `\edef`, `\gdef` 或 `\xdef`.

**给 LaTeX 用户的说明**：这里我们使用原始的、底层的 TeX 命令来定义宏（称为 *原语*）。LaTeX 用户会更熟悉通过 LaTeX 的 `\newcommand` （它本身也是一个宏）来创建宏。

当你要求 TeX 创建（定义）一个宏时，它会创建一个标记来表示 `<宏名>` 以及一个 *标记列表* ，用于表示合并后的 `<参数文本>` 和 `<替换文本>`。TeX 会仔细保存一切，以便表示 `<宏名>` 的标记能与表示其定义的标记列表（`<参数文本>` 和 `<替换文本>`).

例如，如果我们定义 `\mymacro` 如下：

```latex
\def\mymacro abc #1 defz{I typed "#1"!}
```

我们可以看到它的组成部分是：

* `<宏名>` = `mymacro`
* `<参数文本>` = `abc #1 defz`
* `<替换文本>` = `I typed "#1"!`

例如，你可以调用 `\mymacro` 如下：

```latex
\mymacro abc THIS TEXT defz
```

，结果会是 `I typed "THIS TEXT"!` 被排版出来—— `abc` 和 `defz` 已 *不* 被排版。 `abc` 和 `defz` 是用来 *分隔* 宏参数的字符标记序列 `#1` ，并且在你的宏调用被 TeX 成功处理后会被吸收并丢弃。

当你定义 `\mymacro`时，存储其中所含的标记模式会作为一个“模板”，供 TeX 用来判断：

* 你输入中的哪些标记是分隔符标记；
* 你输入中的哪些标记实际上构成了你宏的参数（这里，你在 `#1` 的调用中所使用的是哪些） `\mymacro`).

你必须调用 `\mymacro` 替换为一个 `<参数文本>` ，并且其中包含的分隔符必须与定义它时使用的分隔符完全相同——这也包括使用类别码完全相同的字符分隔符。如果用于调用 `<参数文本>` 的分隔符与定义它时所用的分隔符（存储在 `\mymacro` 时，它将无法匹配保存在内存中的“模板”。 `\mymacro` 。

当 TeX 看到你在调用某个宏时，它会扫描你的输入文本以创建新标记，并逐个标记地尝试将它们与标记列表 `<参数文本>` 模板（作为宏定义的一部分存储）进行匹配。如果你输入文本中使用的分隔符生成了一系列与“模板”中存储的那些不匹配的标记，那么 TeX 通常会报错。

TeX 非常挑剔——记住，字符标记是字符码和类别码的组合：如果你改变了某个字符的类别码，就会得到由该字符生成的不同标记值。

假设我们把 `z` 的类别码改成，比如说 12——通常它是 11——然后尝试这样调用我们的宏：

```latex
\catcode`z=12
\mymacro abc THIS TEXT defz more text here...
```

这次它不会工作，因为 `z` 的类别码已经被改变了。你会看到类似这样的错误：

```latex
Runaway argument?
THIS TEXT defz
! Paragraph ended before \mymacro was complete.
<to be read again>
\par
l.22
```

当 TeX 读取并扫描 `z` .bib 文件中插入一个 % `defz` 时，它无法将其识别为构成 `\mymacro`的节点列表中的以下内容： `<参数文本>` 在你的输入文件中使用的结束标记。直到看到这个出错的 `z` TeX 一直正确地匹配前 3 个字符 `def` 但那个 `z` （类别码为 12）会干扰 TeX 的扫描。假设 `z` 在我们 *定义* `\mymacro`：这会导致一个 token 值 256×11 + 122 = 2938 被存储为 `\mymacro`的定义的一部分（即，作为“模板”的一部分存储）。然而，若类别码为 12， `z` 现在会生成一个 token 值 256×12 + 122 = 3194。由于从你的输入中读入的 token 值（3194） `z`）与存储的 `z`-标记所包含的 `<参数文本>` 标记列表模板（值 2938），TeX 将继续扫描你的输入。TeX 会继续扫描在你的宏后面（*更多文本这里* ）之后的文本，以寻找额外的标记——试图将存储的模板与它在输入中找到的标记匹配起来。它很可能找不到正确的标记模式，结果就会出错，因为 TeX 会“越过”你的输入并错误地读取额外文本以创建额外标记——这些额外标记本不应在此时被读取，并且几乎肯定会产生错误。

我们将在未来的文章中更详细地讨论这一点。

## 标记列表的其他用途

其他用于创建/存储标记列表的命令包括：

```latex
\toks<n>={...}
\everypar={...}
\everymath={...}
\everydisplay={...}
\everyhbox={...}
\everyvbox={...}
\output={...}
\everyjob={...}
\everycr={...}
\errhelp={...}
```

这些命令中的每一个都会从花括号‘{...}’中的字符和命令创建一个标记列表，而该标记列表旨在在某些情况下重复使用。例如， `\everypar={...}` 会创建并存储一组标记（一个标记列表），TeX 会在开始一个新段落之前将其注入输入中。

## 标记列表的隐藏用途：示例

在这一最后部分，我们将看看一些你可能意想不到的标记列表实际使用方式。

### 示例 1：\uppercase{...} 和 \lowercase{....}——临时标记列表

除了显式生成标记列表的命令之外，在某些情况下，TeX 会生成一个隐藏的、临时的内部标记列表，以执行某些特殊处理。记住，当 TeX 读取/处理你的输入字符/命令时，它们会被转换为标记：这是 TeX 引擎所处理的基本构件。

一个很好的例子是命令 `\uppercase{...}` 或 `\lowercase{...}` 因为它们的工作机制乍一看可能相当令人困惑。一旦你理解了它们在 TeX 深处、对用户不可见的工作方式，它们就更容易理解了。

假设你有一串简单的字母，想把它们变成大写——例如 abcde，并将其转换为 ABCDE。用 TeX 的 `\uppercase` 命令：

```latex
\uppercase{abcde}
```

将会使 TeX 输出 `ABCDE`。现在假设我们想把这串简单的字母保存起来以后再用——也就是说，我们不想立刻输出它们，因此我们将使用 TeX 唯一的 *内部* 保存数据的机制——而不是外部（文件）机制：使用标记列表。我们可以通过创建一个宏或者使用一个显式的标记列表命令来做到这一点：

```latex
\toks100={abcde}
\def\mychars{abcde}
```

然后，在某个时候，你可能会决定想把这串字母重复使用，但这次要大写；于是你尝试

```latex
\uppercase{\the\toks100}
```

和

```latex
\uppercase{\mychars}
```

但遗憾的是，这两个都不起作用。为什么会这样？

### 秘密标记列表！

要理解命令 `\uppercase{...}` `\lowercase{...}` 实际如何工作，我需要窥探 TeX 的内部运作，因此下面的解释就是基于这一点得出的。

当 TeX 检测到 `\uppercase{<material>}` 或 `\lowercase{<material>}` 中的任意一个时，它首先做的事就是从 `<material>` 生成一个（临时的）内部标记列表，这个 `\uppercase{...}` 或 `\lowercase{...}` 被花括号‘{’和‘}’括起来，而它们紧跟在

命令之后——这个临时标记列表是 TeX 内部的。 `\uppercase{<material>}` 和 `\lowercase{<material>}` 实际上是如何工作的，一个关键点，也是理解 `<material>` 的核心，是包含在 *展开*中的任何命令或宏都不会被 `{...}`：TeX 所做的只是从放在 `\uppercase{<material>}` 或 `\lowercase{<material>}` 之间的字符和命令生成标记。在

在 `<material>` 该 `{...}` 的运行过程中，花括号之间的内容不会被执行：它只是被转换成标记。 *@* 已被转换成一个（临时）标记列表之后，TeX 接着重新访问该列表中的每个标记，并测试它是否是一个 *命令* 字符标记 `\uppercase` 或 `\lowercase` 或一个命令标记（使用该标记的数值）。如果 TeX 检测到字符标记，它会修改该标记以调整字符的大小写（取决于

正在处理）。TeX  פשוט 忽略任何命令标记，也不会“查看”这些命令标记内部看看它们代表什么或包含什么（例如，一个包含字符的宏）——它们只是被跳过：只有字符标记才会在改变大小写的操作中被实际处理/影响。 `\uppercase{abcde}` abcde `TeX 会从` 生成一个只包含字符标记的标记列表：它们都会被调整为表示 A、B、C、D 和 E 的一系列修改后的标记。这些修改后的标记会被送回 TeX 的输入处理器，从而得到 `ABCDE` 被排版出来。然而，如果我们把字符 *存储在一个宏中*——例如 `\def\mychars{abcde}`——并试图像这样将它们转换成大写：

```latex
\uppercase{\mychars}
```

那么它就会失败，而 abcde 会被排版出来——而不是你所期望的 ABCDE。如果我们接着尝试把字符存储在像这样的标记列表中： `\toks0={abcde}` 并执行 `\uppercase{\the\toks0}` 那么，同样地， `\uppercase` 也会失败，因为这个标记列表将完全由不会受 `\uppercase`.

影响的标记组成。 `\mychars`，在 TeX 检测到输入中的 `\uppercase` 之后，TeX 会查找 `\uppercase` 的含义并对其执行动作，从 `{\mychars}`生成一个临时标记列表。显然，该临时标记列表只包含一个标记，而这个标记并不是字符标记，而是代表我们的宏命令 `\mychars`：因此，就执行 `\uppercase`而言，该标记会被忽略——`\mychars` 并不代表一个字符标记。不过，正如上面所指出的，一旦 `\uppercase` 完成了它的工作，由 `\uppercase`的动作创建的临时标记列表就会被送回 TeX 的完整输入处理（扫描）机制。当 TeX 重新读取该标记列表时，它会检测到一个代表我们 `\mychars` 宏的标记，而 TeX 会执行（展开）该宏，并生成一系列字符来排版 abcde——它们仍然是小写，因为它们被“包裹”在一个宏里，因此对 `\uppercase`.

的动作来说是不可见的。 `\uppercase{...}` 或 `\lowercase{...}`一旦 TeX 重新检查为

### 这怎么修复？

因为 `\uppercase{...}` 或 `\lowercase{...}` 只会作用于字符标记，因此我们需要一种方法在 `\mychars` （或包含在一个 `\toks` 寄存器中）之前“强制解包”其中的字符 `\uppercase{...}` 或 `\lowercase{...}` 作用于它。所谓“解包”，我们真正指的是 TeX 的 *展开*:

* 用 *序列* 替换一个 TeX/LaTeX 命令的标记 *，也就是该命令* (*例如一个宏*) *的组成内容，* 或
* 生成一个命令所设计用来 *产生*的标记序列。一个会生成标记的命令例子是 `\jobname`，它会生成一系列字符标记，表示正在处理的主 TeX 文件名。

#### 更底层的魔法：scantoks(..., ...)

这里我们实际上是在探查 TeX 内部运作更阴暗的角落，所以除非你喜欢这些细节，否则可以忽略这一节……

在 TeX 检测到 `\uppercase` 或 `\lowercase` 位于输入流中之后，它会执行一个名为 `scantoks(..., ...)` 的内部函数，其任务是利用开头的‘{’和结尾的‘}’之间的内容来生成标记列表——如前所述，随后会检查该标记列表，以检测（然后调整）任何字符标记，从而按需改变字符大小写。请注意，我们这里指的是 `scantoks(..., ...)` 作为 TeX 引擎源代码中内置的内部函数——这里并不是把它当作一个控制序列的名称。

作为其工作的一部分， `scantoks(..., ...)` 可以被指示是否展开其正在构造的标记列表，而对于 `\uppercase` 和（`\lowercase`），它并不会展开这些标记：它只是创建它们并把它们放入标记列表中。

它要做的第一件事之一就是检查是否有一个开头的‘{’（或任何类别码为 `scantoks(..., ...)` 的字符），因为它必须确保用户没有犯语法错误而忘记了开头的‘{’（或任何类别码为 1 的字符）——因为需要一个类别码为 1 的字符来界定要被标记化的项目列表的开始。 `\catcode` 1

而这里的诀窍是：寻找开头‘{’的任务会触发 `scantoks(..., ...)` 运行 TeX 的展开过程，这意味着下面这些示例会奏效：

```latex
\let\ob={
\uppercase\ob abcde}
\def\obb{\ob}
\uppercase\obb xyz}
```

以 `\obb`为例，它是一个宏，会被识别为一个 *可展开命令* 并由 TeX（通过 `scantoks(..., ...)` 函数）在寻找开头花括号（任何类别码为 1 的字符）时适当地展开。这意味着我们可以使用“`\expandafter` 技巧”来实现我们的目标：把宏中的字符“解包”出来——也就是展开它。注意 `\expandafter` 也属于一个 *可展开命令*，因此 TeX 会在此处处理它，并让它在寻找开头‘{’（或任何类别码为 1 的字符）时发挥作用。

所以，如果你定义：

```latex
\toks0={abcde}
\def\mychars{abcde}
```

然后这样做：

```latex
\uppercase\expandafter{\mychars}
\uppercase\expandafter{\the\toks0}
```

在这两种情况下，你现在都会看到 ABCDE 被排版出来，因为 `\expandafter` 会促使 `\mychars` 和 `的“解包”（展开）`——二者都会导致 `\uppercase` 看到一串字符标记，从而可以处理它们来改变大小写。

### 示例 2：\string——更多临时标记列表

在内部，TeX 将 `\string` 视为其所谓“转换”命令之一：执行“转换为文本”的操作。该 `\string` 该命令旨在将一个记号转换为人类可读的文本版本——也就是说，排版出生成该记号时所依据的人类可读字符字符串。

例如 `\string\hello` 会创建一个临时记号列表，其中包含字符 \\\\, h, e, l, l, o——是的，甚至包括开头的‘\\\’。一旦该记号列表创建完成，TeX 就会重新读取它，而命令“`\hello`” 的文本就会被排版——是的，如果你选择了合适的字体，连‘\\\’也包括在内……

你可能会想，TeX 平时是如何/为什么能够排版转义字符的；因为它通常被用来触发 TeX 的扫描器生成一个命令记号：这里为什么不会这样做？答案与类别码有关：通常，字符‘\\\’的 catcode 为 0（转义字符），但当 `\string` 生成其内部记号列表时，它做法略有不同。它创建字符记号列表时，会把除了空格字符之外的所有字符都赋予类别码 12；空格字符被赋予 catcode 10——回忆一下，字符记号是由 256 x catcode + ASCII 值计算得出的。所以，当 TeX 重新读取（输入）由 `\string` 生成的 `\hello`，TeX *不会看到一个转义字符* ，因为 ‘\\\’ 的记号是用 catcode 12 而不是 0 计算出来的：TeX 只是把 ‘\\\’ 当作普通字符并将其排版出来。

严格来说，我们也许应该指出，当 TeX 在输入中检测到转义字符时，它实际上并不会为其生成一个记号。一旦它识别出一个类别码为 0 的字符，该字符就只是被用来“触发”生成一个控制序列记号：一旦它触发 TeX 完成这项工作，转义字符的使命就结束了，不再被考虑。

### 技术说明

一个名为 `\showtokens{...}` 的命令（由 e-TeX 引擎引入）可以显示记号列表（在日志文件中）。摘自 e-TeX 手册：

> 命令 `\showtokens{<token list>}` 显示该记号列表，并允许显示那些无法由 `\show` 或 `\showthe`显示的量，例如：
>
> ```latex
> \showtokens\expandafter{\jobname}
> ```

## 总而言之

在 TeX 源代码的第 291 节（见《 [TeX：程序](https://www.amazon.co.uk/Computers-Typesetting-TeX-Program-TEX/dp/0201134373)》第 122 页）中，Knuth 将记号列表描述如下：

> “记号列表是 mem 中由一个字单元节点组成的单链表，其中每个字都包含一个记号和一个链接。宏定义、输出例程定义、标记， `\write` 文本，以及少数其他内容，TeX 以记号列表的形式记忆，通常前面会有一个节点，其“token\_ref\_count”字段中存放着引用计数。”

乍一看，这可能不太容易理解，但希望现在它能稍微更通顺一些。


---

# 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://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/54-what-is-a-tex-token-list.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.
