Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use unsigned integers for counting members?

Should I use unsigned integers for my count class members?

Answer

For example, assume a class

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

That does make sense, doesn't it? The number of items stored in a list can't be negative, so why not use an unsigned integer type for it? I think it's in general a good principle to always use the least general (ergo the most special) type possible.

Now, iterating over a list looks like this:

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

When the number of items stored in the list is zero, the compiler tries to evaluate

List.Count - 1

which results in a nice Integer overflow (underflow to be exact). Combined with the fact that the debugger does not show the appropriate location where the exception occured, this was very hard to find for me.

Let me add that if you have overflow checking turned off, the resulting errors will be even harder to track, because then you will often access memory that doesn't belong to you - and that results in undefined behaviour.

I will be using plain Integers for all my count members from now on to avoid situations like this.

If that's complete nonsense, please point it out to me :)

(I just spent an hour tracking an integer overflow in my code, so I decided to share that - most people on here will know that of course, but perhaps I can save someone some time.)

like image 525
jpfollenius Avatar asked Apr 28 '09 11:04

jpfollenius


3 Answers

No, definitely not. Delphi idiom is to use integers here. Don't fight the language. In a 32 bit environment you'll not have more elements in the list, except if you try to build a bitmap.

Let's be clear: every programmer who is going to have to use your code is going to hate you for using a Cardinal instead of an integer.

like image 77
Stephan Eggermont Avatar answered Oct 03 '22 05:10

Stephan Eggermont


Unsigned integers are almost always more trouble than they're worth because you usually end up mixing signed and unsigned integers in expressions at some point. That means that the type will need to be widened (and probably have a performance hit) to get correct semantics (ideally the compiler does this as per language definition), or else you'll need to be very careful in your range checking.

Take C/C++ for example: size_t is the type of the integer for memory sizes and allocation, and is unsigned, but ptrdiff_t is the type for the offset you get when you subtract one pointer from another, and necessarily is signed. Want to know how many elements you've allocated in an array? Perhaps you subtract the first element address from the last+1 element address and divide by sizeof(element-type)? Well, now you've just mixed signed and unsigned integers.

like image 20
Barry Kelly Avatar answered Oct 03 '22 05:10

Barry Kelly


Regarding your statement that "I think it's in general a good principle to always use the least general (ergo the most special) type possible." - actually I think it's a good principle to use the data type that will cause you least angst and trouble.

Generally for me that's a signed int since:

  1. I don't usually have lists with 231 or more elements in them.
  2. You shouldn't have lists that big either :-)
  3. I don't like the hassle of having special edge cases in my code.

But it's really a style issue. If 'purity' of code is more important to you than brevity of code, your method is best (with modifications to catch the edge cases). Myself, I prefer brevity since edge cases tend to clutter the code and reduce understanding.

like image 25
paxdiablo Avatar answered Oct 03 '22 04:10

paxdiablo