整数是否过多用作数据类型?


9

大多数应用程序开发人员是否在真正要使用无符号整数的地方使用有符号整数?我一直在做,我的同事也在做。我没有看到很多其他广泛的代码库(Delphi VCL除外),并且互联网上的示例通常使用整数。VCL开发人员使用自己的数据类型(这是声明变量的最懒惰的方式)。

像这样的代码似乎有些可怕

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

什么时候可以写成

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

从功能上讲,这些记录几乎总是相同的(并且希望即使在64位Delphi中也将继续相同)。但是非常多的人会遇到转化问题。

但是使用无符号整数也有缺点。主要是因为将两者混在一起是多么烦人。

真正的问题是,这是否是实际上已被考虑或纳入最佳实践的东西?通常只取决于开发人员吗?


5
彼得,您是否仅在寻找有关Delphi的答案?
亚当李尔

3
@Anna了解Delphi数据类型的工作方式将是一个非常好的答案。我有把握地确定C程序员可以理解并回答这个问题。
彼得·特纳

Answers:


9

我在Delphi中不使用无符号整数类型的原因之一是,当它们与有符号整数混合时会产生问题。这是一次咬我的人:

for i := 0 to List.Count - 1 do
  //do something here

我已经i声明为无符号整数(毕竟,它是一个从0开始的列表的索引,它永远List.Count不必为负,对吗?),但是当它为0时,它不会像预期的那样使循环短路0 - 1计算出一个非常高的正数。糟糕!

在混合有符号和无符号整数固有的潜在安全问题与范围问题之间(如果您需要大于的正数high(signed whatever),那么很可能最终也需要大于的正数high(unsigned whatever),因此移动增大到下一个更大的大小,而不是从相同大小的有符号的切换到无符号的通常是正确的操作,)在表示大多数数据时,我真的没有发现无符号整数的太多用途。


2
某种相关的是,使用可能小于必需的数据类型(而不是无符号与有符号的数据类型)的主要风险之一是,如果退出条件大于您的计划,则实际上可能会导致无限循环随着计数器一次又一次溢出。事后看来,这听起来很愚蠢,但是我曾经写过一个程序,该程序应该遍历每个可能的字节值,最终花了大约15分钟的时间才使自己确信无法使用字节计数器。
亚伦诺特,2011年

@Aaronaught:不在德尔福。(至少不会,除非您做一些愚蠢的操作,例如禁用内置的溢出检查。)当计数器溢出时,您将最终遇到异常,而不是无限循环。它仍然是一个错误,但更容易查找。
梅森惠勒

如果你这么说。我总是在Delphi中禁用溢出检查。在无休止地受到哈希码和校验和之类的误报攻击之后,我完全放弃了该“功能”。但我想您是对的,它会捕获到该特定错误。
亚伦诺特,2011年

@Aaronaught:是的,您想将其禁用,例如哈希码和专门用于溢出和环绕的校验和之类的东西。但是对于并非旨在溢出和回绕的通用计算,这是一项重要的安全功能,将其关闭就像在没有安全带的情况下驾驶。
梅森惠勒

也许您已经忘记了,但是在较早版本的Delphi中,溢出检查和编译器指令令人难以置信的错误。我可以生动地记得在看到调试器直接停在{$ O-} / {$ O +}块中间以欢快地报告溢出之后,多次将我的头发拔掉。过了一会儿,我无法忍受了,只是全局禁用了它。再次,是的,这本来可以解决这个问题,但我仍然不认为值得误报的数量。对于每个人自己,当然!
亚伦诺特,2011年

3

老实说,我倾向于习惯使用Integers。我习惯了这样一个事实:它们在大多数情况下都提供足够大的范围,并允许负值(例如-1)。确实,很多时候使用字节/字/短整数会更合适。现在考虑一下,我可以专注于以下方面:

  • 透视。Tilemap的大小限制为192x192个图块,因此我可以使用字节来寻址图块和循环。但是,如果要增加地图的大小,我将不得不进行所有使用,并用例如word替换它。当我需要允许非地图对象时,我将不得不再次更改为smallint。

  • 循环。我经常写一个循环“从i:= 0到Count-1”,如果“ i”是字节并且Count = 0则该循环从0到255发生。这不是我想要的。

  • 统一 记住和应用“ var i:integer;”更容易。而不是在每种情况下都停下来,然后想想:“嗯。在这里,我们正在处理0..120范围..字节。还不够.. Arrgh!“ “为什么在这个地方是smallint而不是shortint?”

  • 结合。当我需要将两个或多个类组合在一起时,它们可能出于目的使用了不同的数据类型,因此使用更广泛的类型可以跳过不必要的转换。

  • -1。即使值在0..n-1范围内,我也经常需要设置“无值/未知/未初始化/空”值,这通常是-1。

使用Integers可以跳过所有这些问题,而不必在不需要的地方进行低级优化,而进入更高级别并专注于更实际的问题。

PS何时使用其他类型?

  • 计数器,它们永远不会是负数,并且在类之外是只读的。
  • 性能/内存原因,迫使在某些地方使用较短的数据类型。

1

最佳实践是使用适合所用数据(预期数据)需求的数据类型。

C#示例:如果只需要支持0到255,我将使用一个字节。

如果我需要支持1,000,000否定和肯定的,那么int。

大于42亿,然后使用很长时间。

通过选择正确的类型,程序将使用最佳的内存量,而不同的类型将使用不同的内存量。

这是来自MSDN的C#int参考。

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer

在C#(或一般的.net)中,long和ulong在128位计算机上会变成128位吗?因为在Delphi中,Integer数据类型在32位计算机上为32位,而在64位计算机上显然为64位。
彼得·特纳

1
@Peter Turner:不,在C#int中只是代码的简写System.Int32,无论代码在什么计算机上运行。
nikie 2011年

@nikie,就是那样type int System.Int32或类似的效果吗?可以在框架的未来版本中轻松地对其进行更改吗?
彼得·特纳

@Peter Turner /尼基(sizeof(int).ToString()); ==>返回4(sizeof(Int64).ToString()); ==>在我的64位Windows操作系统上返回8。正如nikie所说,一个int实际上就是Int32。
乔恩·雷诺

1
有一点要注意,那不是所有类型都符合公共语言规范uint是这种不兼容类型之一,这意味着不应在公开使用的API中使用它,以免破坏以除编写库所用语言之外的.NET语言使用该API的能力。这也是.NET框架的原因API本身是用int哪里uint会做。
亚当李尔

1

无符号整数类型只能用于表示基数的语言中的基数。由于运行C的计算机发生工作的方式,无符号整数类型表现为mod-2 ^ n代数环的成员(这意味着溢出的计算将可预测地“包装”),并且该语言指定在许多情况下,此类类型为要求表现得像抽象代数环,即使这种行为与基数或数学整数的行为不一致。

如果平台完全支持基数和代数环的单独类型,那么我建议基数应使用基数类型(以及需要使用环类型包装的东西)进行处理。这样的类型不仅可以存储两倍于带符号类型的数字,而且接收此类参数的方法也不必检查其是否为负数。

但是,鉴于基数类型相对缺乏,通常最好只使用整数来表示数学整数和基数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.