\vspace

\vspace

我不相信我是第一个问这个问题的人,但是我确实没有通过搜索找到这个问题。

\LaTeX在它的定义中\addvspace,我通常使用\vspace添加垂直空间。所以这让我想知道它们的区别。

答案1

\vspace为了理解和之间的区别\addvspace,让我们看一下这两个宏的定义(来自 Texlive 2022 的latex.ltx):

\vspace

\vspace定义为

\DeclareRobustCommand\vspace{\@ifstar\@vspacer\@vspace}

区分带星号的版本(\@vspacer)和不带星号的版本(\@vspace)。

无星号版本

无星号版本定义为

\def\@vspace #1{%
  \ifvmode
    \@vspace@calcify{#1}%
    \vskip\z@skip
  \else
    \@bsphack
    \vadjust{\@restorepar
             \@vspace@calcify{#1}%
             \vskip\z@skip
            }%
    \@esphack
  \fi}

这里发生的情况是,我们首先查看是否处于垂直模式(\ifvmode)。

如果我们处于水平模式(即,我们在一个段落内),那么我们告诉 LaTeX 等待当前行被渲染(\vadjust),然后我们执行与垂直模式时相同的代码:

\@vspace@calcify{#1}%
\vskip\z@skip

\@vspace@calcify{#1}%在其他地方定义为

\def\@vspace@calcify#1{\begingroup\setlength\skip@{#1}\vskip\skip@\endgroup}

这里,我们临时 ( \begingroup...\endgroup) 将长度寄存器 ( ) 设置为( )\setlength\skip@参数中定义的数量,并使用该寄存器作为参数 ( )调用原语,将垂直跳跃插入输出流。临时宏的设置已完成,因此包中的宏可以在 中使用。\vspace#1\vskip\vskip\skip@calc#1

第二行,在实际跳过后\vskip\z@skip添加零长度 ( )。这样做是为了防止宏消耗我们明确希望存在的垂直跳过。\z@skip=0pt plus0pt minus0pt\removelastskip

(我们将忽略\@bsphack和因为它们与问题无关)\@esphack\@restorepar

加星标版本

\vspace*定义为

\def\@vspacer#1{%
  \ifvmode
    \dimen@\prevdepth
    \hrule \@height\z@
    \nobreak
    \@vspace@calcify{#1}%
    \vskip\z@skip
    \prevdepth\dimen@
  \else
    \@bsphack
    \vadjust{\@restorepar
             \hrule \@height\z@
             \nobreak
             \@vspace@calcify{#1}%
             \vskip\z@skip}%
    \@esphack
  \fi}

它基本上是相同的,但有一个重要的补充:我们在跳过(\hrule \@height\z@)之前添加了一个零高度(和宽度)规则。这样,我们强制 LaTeX 进入水平模式,同时防止潜在的分页符(\nobreak),我们立即返回垂直模式(这是使用 时自动完成的\vskip)。这具有以下效果:假设我们位于页面的最顶部并处于垂直模式。在这种特定情况下,vskips 通常不适用。现在,通过“打破”垂直模式,TeX 不再“位于”类型区域的“最顶部”(因为实际顶部和当前位置之间存在 hmode 内容,尽管空的hmode-stuff)。 因此,vskip已应用。

通过第二次添加,\dimen@\prevdepth以及稍后\prevdepth\dimen@在垂直模式下,我们将有关我们是否位于类型区域顶部的信息“移动”到之后的内容\vspace*(“稍微”简化)。

\addvspace

\addvspace定义为

\def\addvspace#1{%
  \ifvmode
     \if@minipage\else
       \ifdim \lastskip =\z@
         \@vspace@calcify{#1}%
       \else
         \setlength\@tempskipb{#1}%
         \@xaddvskip
       \fi
     \fi
  \else
    \@noitemerr
  \fi}

首先,我们检查是否处于垂直模式,如果不是,则抛出错误。这里,我们有\vspace和之间的第一个真正区别\addvspace\addvspace只能在垂直模式下使用,而\vspace可以在垂直和垂直模式下使用和水平模式。

第二个条件 ( \if@minipage) 检查我们是否处于环境的顶部minipage。由于“true”分支为空,因此 any\addvspace不执行任何操作。第二个不同之处。

第三个条件是\ifdim \lastskip =\z@,它检查最后一个垂直跳跃(存储在\lastskip)是否为 0pt(\z@)。如果是,我们应用\@vspace@calcify{#1}我们已经知道的宏。

然而,如果在之前有一个非零的垂直跳过\addvspace,我们将参数存储在临时长度寄存器中\@tempskipb并调用宏\@xaddvskip,该宏定义为

\def\@xaddvskip{%
  \ifdim\lastskip<\@tempskipb
    \vskip-\lastskip
    \vskip\@tempskipb
  \else
    \ifdim\@tempskipb<\z@
      \ifdim\lastskip<\z@
      \else
        \advance\@tempskipb\lastskip
        \vskip-\lastskip
        \vskip \@tempskipb
      \fi
    \fi
  \fi}

在这里,我们检查存储的长度是否大于最后一个 skip ( \ifdim\lastskip<\@tempskipb)。如果是,则删除最后一个 skip ( \vskip-\lastskip) 并添加临时 skip ( \vskip\@tempskipb)。

如果最后一次跳过大于我们的临时跳过,我们检查临时跳过和最后一次跳过是否为负。

如果临时跳过为负数,并且最后一个跳过为负数,则我们什么也不做。这意味着,只有最后一个跳过有效,并且我们提供的数量将被丢弃。当两个值都为正数时,也会发生同样的情况,但我们的临时值仍然小于最后一个跳过的值(因为条件中\addvspace没有分支)。\else\ifdim\@tempskipb<\z@

如果临时跳跃为负,而最后一个跳跃为正,我们将最后一个跳跃的值添加到临时跳跃(\advance\@tempskipb\lastskip),删除整个最后一个跳跃(\vskip-\lastskip),然后插入一个垂直跳跃,其中包含临时跳跃的新值(\vskip \@tempskipb)。例如,如果最后一个跳跃是10pt,我们加上\addvspace{-3pt},那么我们得到一个垂直跳跃-3pt + 10pt = 7pt

\vskip\z@skip请注意,的定义中没有\addvspace,这意味着\removelastskip 影响跳过插入\addvspace,这是\addvspace和之间的另一个区别\vspace

您可能还会注意到, 的定义\@xaddvskip缺少对 的调用\@vspace@calcify。这个中间函数的唯一目的是传递 skip 的参数,\setlength以便calc包中的宏(挂接到)可以在参数中使用。我们在调用 之前(当我们设置为参数的值时\setlength)已经这样做了,因此不需要额外的步骤。\@xaddvskip\@tempskipb

总结

  • \vspace{X}添加X垂直空间量,检查上方\addvspace{Z}是否有垂直空间,并且仅在或 时添加。Y\addvspace{Z}ZZ>YY=0pt
  • 如果Z<0ptY>0ptZ+Y则添加为垂直空间
  • \addvspace环境顶部minipage不执行任何操作,而环境顶部则\vspace执行空格操作。
  • \vspace可在垂直和水平模式下使用,\addvspace只能在垂直模式下使用
  • 添加 的跳过\addvspace可以通过 删除\removelastskip,添加 的跳过\vspace不能。

关于“最后跳过”的最后一点说明

正如我们所发现的,\addvspace通过将其参数中的值与 的值进行比较,考虑了“跳过之前”本身\lastskip。但是,有些情况下\lastskip可能会受到其他命令的干扰(例如,\write\penalty),更具体地说是将非粘合项添加到垂直列表的命令,即使它们不会产生任何可见的输出。在这些情况下,数量\addvspace显然是添加到已经存在的垂直跳跃,你最终可能会得到比其他地方大得多的空间。

相关内容