【问题标题】:What are Pascal Strings?什么是帕斯卡字符串?
【发布时间】:2014-09-23 23:43:33
【问题描述】:

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

Pascal 字符串的定义特征是什么?在Wikipedia's article on strings 中,定义特征似乎是将字符串的长度存储在第一个字节中。在另一篇文章中,我觉得the memory layout of the strings 也很重要。

在阅读不相关的 SO 线程时,有人提到 Pascal strings make Excel fast。 Pascal 字符串相对于以空字符结尾的字符串有哪些优势?或者更一般地说,Pascal 字符串在哪些情况下表现出色?

Pascal 字符串是否以任何其他语言实现?

最后,我是大写两个单词(“Pascal 字符串”)还是只大写第一个单词(“Pascal 字符串”)?我是一名技术作家...

【问题讨论】:

  • 在 Lazarus 和 Free Pascal wiki wiki.lazarus.freepascal.org/Character_and_string_types 中有一个关于字符和字符串类型的广泛概述。
  • 关于大小写,是“帕斯卡字符串”;没有理由用大写“S”写“字符串”。
  • 但它不叫 Pascal Casing for nothing。
  • 帕斯卡字符串是一个长度前缀字符串。这很有用,因为可以在恒定时间内完成获取字符串的长度或获取字符串的最后一个字符。

标签: string data-structures pascal


【解决方案1】:

Pascal 字符串是由一种特定但影响巨大的 Pascal 实现(称为 UCSD)而流行起来的。所以 UCSD 字符串 是一个更好的术语。这与使字节码解释器流行的实现相同。

一般来说,它不是一种特定类型,而是将大小作为字符数据前缀的基本原则。这使得获取长度成为一个常数时间操作 (O(1)),而不是在字符数据中扫描 nul 字符。

并非所有的帕斯卡都使用这个概念。 IIRC,最初的(七十年代)约定是空格填充分配,并向后扫描非空格字符(使字符串不可能有终止空格)。此外,由于软件大多是单独使用的,因此使用了各种方案,通常基于对该实施/架构有利的因素。

虽然该结构不是标准 Pascal 的一部分,但 Borland 最流行的方言(Turbo Pascal、Delphi 和 Free Pascal)通常基于 UCSD 方言,因此有 pascal 字符串,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 是一个对象。
  • UCSD pascal 没有 move(),这是一项 turbo 发明,在字节码解释器中不能很好地工作。请阅读帖子了解历史背景。
  • 8086 旨在支持“Pascal”字符串和“C”字符串:两者都存在 CISC 指令。这里的例子假设字符串指针和字符串索引是不同的东西。在实际编译器生成的 8086 代码中,这通常不是真的:复制循环仅使用索引或 null,更新索引的代码与检查 null 的代码长度相同,但更新指针“inc( p)" 是多余的,并且使 C 循环比 Pascal 循环更长。
【解决方案2】:

这是一个古老的名字,可以追溯到“C 语言与 Pascal 语言”实际上是人们进行比较的时代。根据您的要求,它要么专门将长度存储在第一个 byte 中,要么引用任何长度前缀(两个字节,四个字节)。其他内存管理细节不包括在内,它们依赖于实现,与 C 字符串没有根本区别。

Pascal 字符串擅长...一切。 NUL 终止的字符串在短字符串上节省了一到三个字节,这在 1970 年可能很有用,但在今天几乎所有情况下都不值得一提。除了无法存储零字节(这对于文本来说还不错,但排除了任何类型的二进制数据),您无法有效地确定字符串长度。这会对字符串算法的很大一部分产生负面影响。一个例子,在你链接到的评论中,是字符串比较:如果你有长度,你可以在比较不同长度的字符串时立即return false。还有很多other downsides与性能无关。

由于这些原因,几乎所有比 1980 年新的语言实现都使用字符串的长度前缀。这是“帕斯卡字符串”名称已过时的另一个原因。

【讨论】:

  • 实际上在“旧时代” - 以空字符结尾的字符串不会通过短字符串(当时是“字符串”)保存任何内容:[0]=3,[1]=O,[2]= z,[3]=z(使用 4 个字节 - Pascal 字符串)[0]=O,[1]=z,[2]=z,[3]=0(使用 4 个字节 - C 字符串)跨度>
  • 在 C 的情况下。在 Modula2(Pascal 的后继)形式的零终止字符串中,如果字符串完全填满分配大小,则没有终止零。这利用了大多数堆管理系统无论如何都具有该分配大小的事实。
猜你喜欢
  • 2011-02-15
  • 2014-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多