我应该使用无符号整数来计算成员吗?

时间:2009-04-28 11:52:39

标签: delphi integer-overflow

我应该为计数类成员使用无符号整数吗?

答案

例如,假设一个类

TList <T> = class
private
  FCount : Cardinal;
public
  property Count : Cardinal read FCount;
end;

这确实有道理,不是吗?列表中存储的项目数不能为负数,那么为什么不使用无符号整数类型呢?我认为总是使用最不通用的(最特殊的)类型是一个很好的原则。

现在,迭代列表如下所示:

for I := 0 to List.Count - 1 do
  Writeln (List [I]);

当列表中存储的项目数为零时,编译器会尝试评估

List.Count - 1

导致一个很好的整数溢出(确切地说是下溢)。结合调试器没有显示发生异常的适当位置的事实,这对我来说很难找到。

让我补充一点,如果你关闭溢出检查,结果错误将更难跟踪,因为那时你经常会访问不属于你的内存 - 这会导致未定义的行为。

从现在开始,我将为所有计数成员使用普通整数来避免这种情况。

如果这完全是废话,请指出给我:)

(我花了一个小时跟踪代码中的整数溢出,所以我决定分享一下 - 当然大多数人都会知道,但也许我可以节省一些时间。)

6 个答案:

答案 0 :(得分:15)

不,绝对不是。 Delphi习语是在这里使用整数。不要对抗语言。 在32位环境中,除了尝试构建位图之外,列表中没有更多元素。

让我们明确一点:每个必须使用你的代码的程序员都会讨厌使用Cardinal而不是整数。

答案 1 :(得分:10)

无符号整数几乎总是比它们值得更麻烦,因为你通常最终会在表达式中混合有符号和无符号整数。这意味着需要扩展类型(并且可能具有性能损失)以获得正确的语义(理想情况下编译器根据语言定义执行此操作),否则您需要在范围检查中非常小心。 / p>

以C / C ++为例:size_t是内存大小和分配的整数类型,并且是无符号的,但是ptrdiff_t是减去一个指针时得到的偏移的类型从另一个,必然是签署。想知道你在阵列中分配了多少元素?也许您从first元素地址中减去last+1元素地址并除以sizeof(element-type)?好吧,现在你只是混合了有符号和无符号整数。

答案 2 :(得分:9)

关于你的陈述“我认为总的来说,总是使用最不通用的(最特别的)最好的类型是一个很好的原则。” - 实际上我认为这是一个很好的原则来使用数据类型会让您最不安心和麻烦。

一般来说,对我来说,这是一个签名的int:

  1. 我通常没有包含2 31 或更多元素的列表。
  2. 你不应该有那么大的名单: - )
  3. 我不喜欢在我的代码中遇到特殊边缘情况的麻烦。
  4. 但这确实是一个风格问题。如果代码的“纯度”对于您而言比简洁代码更重要,那么您的方法最好(通过修改来捕获边缘情况)。我自己,我更喜欢简洁,因为边缘情况往往会使代码混乱并减少理解。

答案 3 :(得分:8)

别。

这不仅仅违背了编程习惯用法,它是对编译器使用无符号算法的明确请求,它要么容易出现异常行为(如果你不防止溢出),要么发生不相关的运行时异常(如果你守卫)对于溢出,临时溢出将是致命的,当你在添加之前减去fi时,即使最终结果是肯定的,我指的是操作的CPU操作码级别排序,这可能与你没有什么琐碎的关系在你的代码中。)

请记住,“无符号”会将转换为“正面”,它会转换为“没有符号”,这是不同的。选择“无符号”一词是有充分理由的(在Delphi中将其命名为“Cardinal”是一个糟糕的选择IMO)。

无符号类型适用于原始存储规范,按位操作,ASM代码,嵌入式控制器和其他专业用途。当你进行高级编程时,你应该忘记你曾经听说过无符号类型。

答案 4 :(得分:3)

道德:尽可能使用迭代器和foreach,因为它完全避免了这个问题。

答案 5 :(得分:3)

边界条件经常出现问题。允许可能出现负面影响的类型可能会改变问题。也许它以一种更容易调试的方式改变它,也许不是。我开始使用整数来计算这样的循环,但后来切换到红衣主教以帮助我发现错误。