我不是Pascal的新手,但直到现在我仍然不知道为什么Delphi和Free Pascal通常声明参数并将值返回为有符号整数,而我看到它们应该始终为正。例如:
Pos()
返回Integer类型。是否可能是否定的?SetLength()
将NewLength
参数声明为Integer类型。字符串是否有负长度?System.THandle
声明为Longint。句柄是否有负数?有很多决定,比如Delphi和Free Pascal。这背后有什么考虑因素?
答案 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)
答案 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。在这个限制,我得到EIntOverflow
或EAccessViolation
,似乎我遇到了Delphi运行时库(RTL)错误,没有正确定义的限制,所以你的里程可能会有所不同。