为什么Delphi和Free Pascal通常更喜欢带符号整数数据类型而不是无符号数字类型?

问题描述 投票:18回答:4

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

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

[Delphi和Free Pascal中有很多决定。这是出于什么考虑?

delphi pascal freepascal
4个回答
17
投票

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

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

THandle的情况要简单得多。直到D4为止,Delphi都没有适当的32位无符号,只有31位基数。 (由于32位无符号整数不是整数的子范围,因此以后的无符号int是int64的子集,这将问题移至仅在D2010中添加的uint64)[]

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

但是winapi情况不同于一般情况。

[稍后添加

一些Pascal(和Modula2 / 3)实现方式通过将整数设置为大于wordize的大小来规避此陷阱,并要求所有数值类型声明适当的子范围,例如下面的程序。

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

   var x : 0..20;
       y : -10..10;

   begin
     // any expression of x and y has a range -10..20

嗯,开始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()的返回值用于索引字符串,因此也应对其进行签名。

这是该主题的另一个堆栈溢出讨论:Should I use unsigned integers for counting members?

当然,我在这里疯狂地猜测。也许没有设计,只是出于习惯,树立了使用有符号值的先例,并将其奉承。

  • 某些与字符串相关的搜索函数在什么都没有找到时返回-1。
  • 我相信这背后的原因是MaxInt为2GB,这是32位Delphi中字符串的最大大小。这是因为a single process can have up to 2GB memory
  • 使用带符号整数有很多原因,甚至在您不打算返回负值时也可能适用。

    想象一下,我编写了调用Pos的代码,并且我想对结果进行数学运算。如果(Pos('x',s)-5)返回Pos('x',s),您是否希望得到一个否定的结果1引发范围检查异常,下溢并成为一个很大的无符号数(约40亿),或者变为负数?对于很少考虑这些情况的新用户来说,这两种情况都是问题的根源,但是长期以来的传统

    是,通过使用Integer结果,检查负和零结果而不使用它们是您的工作作为字符串偏移量。对于初学者和高级程序员来说,使用整数是有利的,并且不会使“负”值滑落并成为大的无符号值或引发范围异常。

    [其次,请记住,在开始编程时,通常会在引入无符号类型(如Integer)之前引入Cardinal(有符号)类型。初学者通常使用Pos之类的功能,因此使用会产生最不友好的副作用的类型是有意义的。大于绝对需要的范围没有负面影响(Pos可能需要的范围是1到delphi中的最大字符串长度)。在Pos中使用Cardinal类型在32位Delphi中具有零收益,选择它肯定有不利的影响。

    但是,一旦达到64位delphi,您就可以理论上

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

    13
    投票

    嗯,开始THandle的声明不正确。它在Windows标头中未签名,在Delphi中应该是未签名的。实际上,我认为在最近发布的Delphi中已对此进行了更正。


    6
    投票
    • 某些与字符串相关的搜索函数在什么都没有找到时返回-1。

    6
    投票

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

    © www.soinside.com 2019 - 2024. All rights reserved.