9

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

回答

例如,假设一个类

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

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

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

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

如果这完全是胡说八道,请指出来给我:)

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

4

6 回答 6

15

不,绝对不是。Delphi 的习惯用法是在这里使用整数。不要与语言抗争。在 32 位环境中,列表中不会有更多元素,除非您尝试构建位图。

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

于 2009-04-28T12:03:19.853 回答
10

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

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

于 2009-04-28T14:09:52.700 回答
9

关于您的陈述,“我认为始终使用尽可能不通用(因此最特殊)的类型通常是一个很好的原则。” - 实际上,我认为使用最不引起您的焦虑和麻烦的数据类型是一个很好的原则。

通常对我来说这是一个有符号的整数,因为:

  1. 我通常没有包含 2 31个或更多元素的列表。
  2. 你也不应该有那么大的列表:-)
  3. 我不喜欢在我的代码中有特殊边缘情况的麻烦。

但这确实是一个风格问题。如果代码的“纯度”对您来说比代码的简洁更重要,那么您的方法是最好的(通过修改以捕捉边缘情况)。我自己,我更喜欢简洁,因为边缘情况往往会使代码混乱并减少理解。

于 2009-04-28T12:01:35.503 回答
8

不。

这不仅仅是违反编程习惯,它是对编译器使用无符号算术的明确请求,这要么容易出现异常行为(如果你不防范溢出)或不相关的运行时异常(如果你防范溢出,临时溢出将是致命的,即使最终结果是肯定的,即使最终结果是正数,当您在添加之前进行减法时也是如此,而且我指的是 CPU 操作码级别的操作顺序,这可能与您的内容无关紧要代码)。

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

无符号类型用于原始存储规范、按位运算、ASM 代码、嵌入式控制器和其他特殊用途。当您进行高级编程时,您应该忘记您曾经听说过无符号类型。

于 2009-04-28T21:02:54.237 回答
3

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

于 2009-04-28T11:57:18.760 回答
3

边界条件经常会出现问题。允许一种可能变为负面的类型可能只会改变问题。也许它以一种更容易调试的方式改变它,也许不是。我开始使用整数来计算这样的循环,但后来改用基数来帮助我捕捉错误。

于 2009-04-28T11:58:42.953 回答