18

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

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

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

4

4 回答 4

18

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

一个重要的原因是,如果计算的中间结果为负,并且您使用无符号整数进行计算,则会触发范围检查错误,并且由于大多数较旧的编程语言不假设 2-complement 整数,结果(范围检查关闭)甚至可能是腐败的。

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。

于 2012-10-08T12:43:59.017 回答
13

好吧,首先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 讨论:我应该使用无符号整数来计算成员吗?

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

于 2012-10-08T12:20:24.667 回答
6
  • 一些与字符串相关的搜索函数在未找到任何内容时返回 -1。
  • 我相信这背后的原因是 MaxInt 是 2GB,这是 32 位 Delphi 中字符串的最大大小。这是因为单个进程最多可以拥有 2GB 内存
于 2012-10-08T12:21:47.560 回答
6

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

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

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

Once you get to 64-bit delphi, however, you could theoretically have strings LARGER than an Integer can hold, and moving to Cardinal wouldn't fix all your potential problems. However, the chance of anyone having a 2+ GB string is probably nil, and Delphi 64-bit compiler doesn't allow a >2 GB string, anyway. In my testing, I can achieve an almost 1 GB String in 64 bit Delphi. So the practical length limit for a Win64 string is about a billion (1073741814) characters, which is using nearly 2 GB of actual RAM. At that limit, I either get EIntOverflow or EAccessViolation, and it seems I am hitting Delphi run time library (RTL) bugs, not properly defined limits, so your mileage may vary.

于 2012-10-08T18:29:19.723 回答