【问题标题】:Why do Delphi and Free Pascal usually prefer a signed-integer data type to unsigned one?为什么 Delphi 和 Free Pascal 通常更喜欢有符号整数数据类型而不是无符号数据类型?
【发布时间】:2012-09-28 16:28:41
【问题描述】:

我不是 Pascal 新手,但直到现在我仍然不知道为什么 DelphiFree Pascal 通常将参数和返回值声明为有符号整数,而我认为它们应该始终为正数。例如:

  • Pos() 返回整数类型。有可能是负数吗?
  • SetLength()NewLength 参数声明为整数类型。字符串的长度是否为负数?
  • System.THandle 声明为 Longint。句柄是否有负数?

在 Delphi 和 Free Pascal 中有许多类似的决定。这背后有什么考虑?

【问题讨论】:

  • FPC 的考虑很简单:与 Delphi 兼容。德尔福对 Pos 的考虑(通过 TurboPascal)可能渴望回到 1974 年的 Pascal 报告
  • Arioch:FPC 也通过 TP。在 FPC 开始致力于 Delphi 兼容性之前,这种东西早就决定了。

标签: delphi pascal freepascal


【解决方案1】:

好吧,首先THandle 声明不正确。它在 Windows 标头中未签名,在 Delphi 中也应如此。事实上,我认为这在最近发布的 Delphi 中得到了纠正。

我想签名而不是未签名的偏好在很大程度上是历史性的,并不是特别重要。但是,我可以想到一个重要的例子。考虑 for 循环:

for i := 0 to Count-1 do

如果i 是无符号的并且Count 是0,那么这个循环从0 运行到$FFFFFFFF,这不是你想要的。使用有符号整数循环变量可以避免这个问题。

Pascal 是其语法的牺牲品。等效的 C 或 C++ 循环没有这样的麻烦

for (unsigned int i=0; i<Count; i++)

由于语法差异和使用比较运算符作为停止条件。

这也可能是字符串或动态数组上的Length() 返回有符号值的原因。所以为了一致性,SetLength() 应该接受有符号的值。鉴于Pos() 的返回值用于索引字符串,它也应该被签名。

这是关于该主题的另一个 Stack Overflow 讨论:Should I use unsigned integers for counting members?

当然,我在这里胡乱猜测。也许没有设计,只是出于习惯,使用有符号值的先例被设置并被奉为神圣。

【讨论】:

  • +1 来自我,您的评论以及 Marco van de Voort 的评论值得接受。但是,我必须选择其中之一。
  • @Astaroth Marco 是authority on the subject。他的回答是正确的。
  • 这是 C/C++ 胜过 Delphi 的一个领域。 C/C++ for 循环比 Delphi 的 for 循环灵活得多,例如:for(unsigned i = 0; i &lt; Count; ++i)Count 无符号时不会出现溢出问题,因此无需事先检查 Count 是否为 0。我从不喜欢 Delphi 的循环条件是包容性的,需要您事先执行边界检查。
  • @RemyLebeau 另一方面,C 系列语言没有一个循环,它只计算一次终止条件。因此,如果循环条件是i&lt;obj.count(),那么每次都会调用count() 方法。显然,本地 var 可以解决这个问题,但灵活性是有代价的。我总是希望语言设计人员针对最常见的情况进行优化。
  • 是的,但这可以使用局部变量来完成,就像 Delphi 编译器隐式使用的一样:unsigned condition = ...; for(unsigned i = 0; i &lt; condition; ++i)
【解决方案2】:
  • 某些与字符串相关的搜索函数在未找到任何内容时返回 -1。
  • 我相信这背后的原因是 MaxInt 是 2GB,这是 32 位 Delphi 中字符串的最大大小。这是因为a single process can have up to 2GB memory

【讨论】:

  • 在 Delphi Pos() 中找不到任何东西时返回 0。
  • Pos() 是一个不好的例子,但是其他一些 VCL 函数/方法(例如 TStrings.IndexOf())在找不到时返回 -1。
  • @whosrdaddy,另一个问题出现了,为什么 MaxInt 是 2GB,而不是 4GB?为什么字符串的最大大小是 2GB,而不是 4GB?
  • @Astaroth 这是 CPU 架构/操作系统相关的。 $7FFFFFFF = 2GB,这是 32 位整数的正值最大值
  • 看来64位Delphi的字符串长度限制也是10亿个字符左右,也就是2G内存左右。
【解决方案3】:

在 Pascal 中,整数(有符号)是基本类型。所有其他整数类型都是整数的子范围。 (这在 Borland 方言中并不完全正确,TP 中的 longint 和 Delphi 中的 int64,但足够接近)。

如果计算的中间结果为负数,并且您使用无符号整数进行计算,则会触发范围检查错误,并且由于大多数较旧的编程语言不假设 2 补码整数,结果(带有范围检查)甚至可能已损坏。

THandle 案例要简单得多。 Delphi 在 D4 之前没有适当的 32 位无符号,但只有 31 位基数。 (由于 32 位无符号整数不是整数的子范围,所以后面的无符号整数是 int64 的子集,这将问题转移到 uint64 上,它是在 D2010 左右才添加的)

因此,在标头中的许多地方,在 winapi 使用无符号类型的地方使用了有符号类型,可能是为了避免第 32 位在这些版本中意外损坏,并且自定义卡住了。

但是winapi的情况和一般的情况不同。

稍后添加一些 Pascal(和 Modula2/3)实现通过将整数设置为大于字大小的大小来规避这个陷阱,并要求所有数字类型声明一个适当的子范围,就像下面的程序一样。

第一个假设所有内容都是整数的子集,第二个允许编译器再次缩小几乎所有内容以适应寄存器,特别是如果 CPU 有一些大于字操作的操作。 (比如 x86,其中 32 位 * 32 位 mul 给出 64 位结果,或者可以使用状态位检测字大小溢出(例如,在不执行完整 2*字大小相加的情况下为相加生成范围异常)

   var x : 0..20;
       y : -10..10;
       
   begin
     // any expression of x and y has a range -10..20

Turbo Pascal 和 Delphi 在其 16 位和 32 位产品中模拟两倍字长的整数类型。最高无符号类型的处理充其量是 hacky。

【讨论】:

  • 在最近的版本中,Delphi 已经开始在适当的情况下对 Windows API 类型使用无符号类型。例如,THandle 现在映射到 NativeUInt
  • 我希望如此,但我昨天碰巧编译了最新的 VST (5.01),据说它与 XE2 兼容。我仍然在 VST 实现的接口定义中有许多有符号无符号差异。
【解决方案4】:

使用有符号整数的原因有很多,甚至在您不打算返回负值时可能适用。

假设我编写了调用 Pos 的代码,并且我想对结果进行数学运算。如果Pos('x',s) 返回1,您宁愿得​​到一个负数结果(Pos('x',s)-5) 引发范围检查异常、下溢并成为一个非常大的无符号数,大约为40 亿,还是变成负数?对于很少考虑这些情况的新用户来说,任何一个都是问题的根源,但悠久的传统是,通过使用Integer 结果,您的工作是检查负结果和零结果,并且不要将它们用作字符串偏移量。对于初学者和高级程序员来说,使用整数是有优势的,并且不会让“负”值滚动并变成大的无符号值或引发范围异常。

其次,请记住,在开始编程时,通常会在引入Cardinal 等无符号类型之前很久就引入Integer(有符号)类型。初学者经常使用Pos 之类的函数,使用会产生最不友好的副作用集的类型是有意义的。范围大于您绝对需要的范围没有负面影响(Pos 可能需要的范围是 1 到 maximum-string-length-in-delphi)。在 32 位 Delphi 中使用 Cardinal 类型作为 Pos 的好处为零,选择它肯定有缺点。

然而,一旦您使用 64 位 delphi,您可能理论上拥有比 Integer 可以容纳的更大的字符串,并且迁移到 Cardinal 并不能解决您所有的潜在问题。但是,任何人拥有 2+ GB 字符串的机会可能为零,而且 Delphi 64 位编译器无论如何都不允许使用 &gt;2 GB 字符串。在我的测试中,我可以在 64 位 Delphi 中实现几乎 1 GB 的字符串。因此,Win64 字符串的实际长度限制约为十亿 (1073741814) 个字符,这使用了近 2 GB 的实际 RAM。在这个限制下,我得到EIntOverflowEAccessViolation,似乎我遇到了 Delphi 运行时库 (RTL) 错误,没有正确定义限制,所以你的里程可能会有所不同。

【讨论】:

  • "所以你的里程可能会有所不同" -- 无价的,+1 (:
  • 由于范围检查而导致的负面结果不切实际,这是一个主要论点,并且不在我的回答中
猜你喜欢
  • 2021-08-17
  • 2018-12-18
  • 2017-04-05
  • 2010-11-10
  • 1970-01-01
  • 2020-04-15
  • 2014-02-01
相关资源
最近更新 更多