为什么Delphi和Free Pascal通常更喜欢有符号整数数据类型到无符号整数?

时间:2012-10-08 12:07:59

标签: delphi pascal freepascal

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

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

有很多决定,比如Delphi和Free Pascal。这背后有什么考虑因素?

4 个答案:

答案 0 :(得分:16)

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

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

THandle案例要简单得多。 Delphi没有正确的32位无符号直到D4,但只有31位基数。 (因为32位无符号整数不是整数的子范围,后面的无符号整数是int64的子集,它将问题移动到uint64,这只是在D2010中添加的)

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

但是winapi案例与一般情况不同。

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

第一个假设所有内容都是整数的子集,第二个允许编译器再次将所有内容再次缩小以适应寄存器,特别是如果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

答案 1 :(得分: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讨论:Should I use unsigned integers for counting members?

当然,我在这里疯狂地猜测。也许没有任何设计,只是出于习惯,使用有符号值的先例已经确定并且已经成为现实。

答案 2 :(得分:6)

  • 当找不到任何内容时,某些与字符串相关的搜索函数返回-1。
  • 我相信这背后的原因是MaxInt是2GB,这是32位Delphi中字符串的最大大小。这是因为a single process can have up to 2GB memory

答案 3 :(得分:6)

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

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

其次,请记住,在开始编程时,通常会在引入无符号类型(如Integer之前)引入Cardinal(带符号)类型。初学者经常使用像Pos这样的函数,并且使用会创建最不友好的副作用的类型是有意义的。如果范围大于您绝对需要的范围,则没有负面的副作用(Pos可能需要的范围是1到最大字符串长度在delphi中)。在32位Delphi中使用Pos的Cardinal类型没有任何好处,并且肯定有选择它的缺点。

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

相关问题