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

Ast*_*oth 17 delphi pascal freepascal

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

  • Pos()返回Integer的类型.有可能成为负面的吗?
  • SetLength()NewLength参数声明为Integer的类型.字符串是否有负长度?
  • System.THandle宣称为Longint.手柄有负数吗?

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

Mar*_*ort 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
Run Code Online (Sandbox Code Playgroud)

  • 在最近的版本中,Delphi已经开始在适当的情况下使用无符号类型的Windows API类型.例如,`THandle`现在映射到`NativeUInt`. (2认同)

Dav*_*nan 13

好吧,因为一个开始THandle声明不正确.它在Windows标头中是未签名的,在Delphi中应该是这样.事实上,我认为这在最近的Delphi版本中得到了纠正.

我认为签署无签名的偏好主要是历史性的,并不是特别重要.但是,我可以想到一个重要的例子.考虑for循环:

for i := 0 to Count-1 do
Run Code Online (Sandbox Code Playgroud)

如果i是无符号且Count为0,则此循环从0运行到$FFFFFFFF不是您想要的.使用带符号的整数循环变量可以避免该问题.

Pascal是这里语法的受害者.等效的C或C++循环没有这样的麻烦

for (unsigned int i=0; i<Count; i++)
Run Code Online (Sandbox Code Playgroud)

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

这也可能Length()是字符串或动态数组返回有符号值的原因.因此,为了保持一致性,SetLength()应接受签名值.并且假设返回值Pos()用于索引字符串,它也应该被签名.

这是关于该主题的另一个Stack Overflow讨论:我应该使用无符号整数来计算成员吗?

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

  • @RemyLebeau另一方面,C系列语言没有一个循环只能评估一次终止条件.所以如果循环条件是`i <obj.count()`那么每次调用`count()`方法.显然,本地变量处理这个问题,但灵活性需要付出代价.我总是喜欢语言设计师针对最常见的情况进行优化. (4认同)
  • @Astaroth Marco是[该主题的权威](http://www.freepascal.org/aboutus.var).他的回答是正确的接受. (2认同)

who*_*ddy 6

  • 当找不到任何内容时,某些与字符串相关的搜索函
  • 我相信这背后的原因是MaxInt是2GB,这是32位Delphi中字符串的最大大小.这是因为单个进程最多可以有2GB内存

  • 在Delphi中,Pos()在找不到任何内容时返回0. (3认同)

War*_* P 6

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

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

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

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