帕斯卡字符串是什么?

35

它们是以编程语言还是数学家命名的?

Pascal字符串的定义特征是什么?在维基百科的字符串文章中,似乎其定义特征是将字符串长度存储在第一个字节中。在另一篇文章中,我得出的印象是字符串的内存布局也很重要。

在浏览无关SO线程时,有人提到Pascal字符串使Excel快速。Pascal字符串相对于空终止字符串的优势是什么?或者更一般地说,在什么情况下Pascal字符串表现出色?

其他语言是否实现了Pascal字符串?

最后,我应该大写两个单词(“Pascal Strings”)还是只大写第一个单词(“Pascal strings”)?我是技术作家...


在 Lazarus 和 Free Pascal 维基上有关于字符和字符串类型的广泛概述,网址为 http://wiki.lazarus.freepascal.org/Character_and_string_types 。 - jwdietrich
2
关于大小写,它是“Pascal字符串”;没有理由用大写字母“S”写“Strings”。 - Andreas Rejbrand
1
但这不是无缘无故被称为帕斯卡命名法的。 - H H
Pascal字符串是一个带长度前缀的字符串。它非常有用,因为可以在常数时间内获取字符串的长度或获取字符串的最后一个字符。 - PlsWork
2个回答

31

帕斯卡字符串得名于一种具有极大影响力的帕斯卡实现,叫做UCSD,因此“UCSD字符串”是一个更好的术语。这也是使字节码解释器流行的相同实现。

一般来说,它不是一个具体的类型,而是基于在字符数据前加上长度前缀的基本原理。这使得获取长度成为一个常数时间操作(O(1)),而不是扫描字符数据寻找空字符。

并非所有的帕斯卡都使用这个概念。我回想起来,最初(七十年代)的惯例是填充分配的空间,并向后扫描一个非空格字符(使字符串无法有一个终止空格)。此外,由于软件大多用于隔离,各种方案被使用,通常基于那个实现/架构的优点。

尽管该结构不是标准帕斯卡的一部分,但 Borland 最流行的方言(Turbo Pascal、Delphi 和 Free Pascal)一般基于 UCSD 方言,在其中使用帕斯卡字符串,Delphi 目前拥有 5 种这样的字符串。(short/ansi/wide/unicode/open)

另一方面,这意味着在循环中,您需要一些基于索引的附加检查来检查字符串的末尾。

因此,代替使用复制字符串:

while (p^) do begin P^=p2^; inc(p) inc(p2); end;

其完全等同于

while (*s++ = *t++);
在使用优化编译器时,需要执行例如C中的操作。
while (len>0) do begin p^:=p2^; inc(p) inc(p2); dec(len); end;

甚至更多

i:=1;
while (i<=len) do begin p[i]:=p2[i]; inc(i); end;

这使得Pascal字符串循环中的指令数量略大于等效的零终止字符串,并且增加了一个活跃变量。此外,UCSD是一种字节码(p-code)解释器语言,基于pascal字符串使用的后者代码是“安全的”。

对于具有内置后自增(++)运算符的架构(例如最初为PDP-8、11开发的C),指针版本甚至更便宜,尤其是没有优化的情况下。现在的优化编译器可以轻松检测到任何这些结构,并将其转换为最佳状态。

更重要的是,早在90年代,安全性变得更加重要,在一般情况下,仅依靠空终止字符串特性被视为不可取,因为验证中的小错误可能会导致潜在的可利用缓冲区溢出问题。因此,C及其标准已经弃用了旧的字符串用法,现在使用旧字符串例程(如strNcpy等)的“-n-”版本,需要传递最大长度。这增加了与长度类似的同样额外的活跃变量,类似于手动管理Pascal字符串的原则,程序员必须注意在周围传递长度(或C的-N-函数的最大缓冲器大小)。Pascal字符串仍然具有O(1)操作访问最后一个被占用字符的优点,而且没有禁止使用的字符。

长度前缀字符串在文件格式中也被广泛使用,因为显然知道要读取的字节数是很有用的。


将一个短字符串移动到另一个短字符串中,可以使用以下代码:Move(t[0],s[0],Ord(s[0]))。这个方法和你的C语言示例一样快。当然,你的示例是基于所谓的“ShortString”,而Delphi和新的Pascal方言默认使用所谓的HugeString或NativeString。它们在数据之前有4个字节的长度,因此引用[0]是错误的。在我的编译器中,在[1]的字节之前还有更多内容,因为String是一个对象。 - Ozz Nixon
UCSD Pascal没有move()函数,这是Turbo发明的,而且在字节码解释器中效果不佳。请阅读帖子了解历史背景。 - Marco van de Voort
8086被设计为支持'Pascal'字符串和'C'字符串:CISC指令都存在。这里的示例假设字符串指针和字符串索引是不同的东西。在由实际编译器生成的8086代码中,通常并非如此:复制循环仅使用索引或null,更新索引的代码与检查null的代码长度相同,但更新指针“inc(p)”是额外的,使C循环比Pascal循环更长。 - david
david:所有的x86都有rep; movsb用于固定字符串复制。 - Marco van de Voort

15

这是一个古老的名称,可以追溯到“C语言与Pascal语言”实际上是人们进行比较的日子。根据你问的人是谁,它要么是特别将长度存储在第一个字节中,要么是指任何长度前缀(两个字节、四个字节)。其他内存管理细节不包括在内,它们是实现相关的,对于C字符串来说并不是基本的区别。

Pascal字符串在各方面都表现出色。以NUL结尾的字符串可以在短字符串上节省一到三个字节,在1970年可能有用,但在几乎所有情况下今天甚至不值得一提。除了不能存储零字节(这对于文本来说还不太糟糕,但排除了任何类型的二进制数据),你无法有效地确定字符串长度。这会对很多字符串算法产生负面影响。例如,在你链接的评论中,一个例子是字符串比较:如果你有长度,当比较不同长度的字符串时,你可以立即返回false。还有许多其他缺点与性能无关。

因此,几乎所有1980年后的语言实现都使用字符串长度前缀。这也是为什么“Pascal字符串”名称已经过时的另一个原因。


4
实际上,在“旧日子”里,以空字符结尾的字符串与短字符串(那时称作“String”)相比,并没有节省什么空间: [0]=3,[1]=O,[2]=z,[3]=z (这需要4个字节 - Pascal字符串) [0]=O,[1]=z,[2]=z,[3]=0 (这需要4个字节 - C字符串) - Ozz Nixon
在 C 语言中,在 Modula2(Pascal 的后继者)的零结尾字符串形式中,如果字符串完全填满了分配大小,则没有终止零。这利用了大多数堆管理系统已经具有的分配大小这一事实。 - Marco van de Voort
@MarcovandeVoort:对于许多C语言的字符串处理例程来说,如果传递了缓冲区大小,如果缓冲区中没有零字节,它们将在整个缓冲区上操作,否则将在零字节之前的部分上操作。使用这种类型的数据结构,即使是最大紧凑的长度前缀,也比节省一个字节。 - undefined
@supercat: 我猜C99的-n-函数更接近Modula 2系统。旧的例程没有任何缓冲区长度信息。 - undefined
@MarcovandeVoort:strnlen(用于处理零填充字符串)和strncpy(从零终止转换为零填充)例程在C89中已经存在,printf系列函数中的%s长度指定选项也是如此。除了几乎没有合理用例的strncat之外,我认为C99没有为零填充字符串添加更多的函数。在C89之后添加的“安全”函数将在预期长度内未找到零字节视为错误,而不是预期条件。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接