using signed int for values that should never be negative

Started by
28 comments, last by SiCrane 16 years, 7 months ago
The game engine my company has licensed does this all the time - it will use signed math for a simple for loop. It seems to me that this is bad practice and can, in some circumstances, result in severe security vulnerabilities. Should I always be using unsigned types for types that are never negative, or am I overreacting and adding complexity where it is completely unnecessary?
Advertisement
I have one question for you to consider. When is 4294967295 preferable to -1 when it comes to invalid values?
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
I wouldn't say that using signed integers for values that will never be negative is bad practice as such, it's certainly quite common practice and you can use -1 to represent an uninitialised state which can be handy.

I personally however do use unsigned ints quite extensively, like: for (unsigned int i = 0; i < something; ++i)

If I see a signed integer I don't automatically assume it'll be used for negative values but the possibility exists, if I see an unsigned integer then I know it will always be positive, being explicit about your intentions never hurts.

Ofcourse, signed integers usually have a lower maximum (positive) range than an unsigned integer, so sometimes the decision is effectively made for you based on what you need.
Quote:Original post by Promit
I have one question for you to consider. When is 4294967295 preferable to -1 when it comes to invalid values?


I think he might be asking this:
for (int i = 0; i < 10; i++) {  a = i; <-- array index cannot be negative in a sane application};



Personally, I try to match signedness with context.

Using unsigned types isn't always just gravy, and I had to deal with a very nasty side-effect of it.

The loop as simple:
size_t MAX = (a - b);size_t i = 0;while ( i < MAX ) // do something


But... due to various circumstances, MAX was initialized to -1, and hilarity ensued. If signed types were allowed, negative values would properly terminate the loop.

So it's not a sure-fire way to solve or prevent bugs.


There was also another conflict I encounter.

While designing a Buffer class, I chose STL's types. Size was size_t, since buffer size is either 0 or positive number.

But then I used pointer difference to obtain used size (curr_ptr - begin_ptr), which is typed as ptrdiff_t. Obviously, ptrdiff_t is signed, although.

Here's where compiler was hinting at something - even if I design a class correctly, and make sure it works, the language no longer guarantees that pointers are truly properly initialized.

So if compiler is warning you about sign conversions, it's usually a sign you're avoiding something, or trying to make something messy. In my case, it was perfectly warranted, since I was deliberately mixing incompatible types, even though I was trying to be smart about it.

Use whatever you see fit. But make sure you take into consideration the warnings compiler might start issuing over your choices. They'll usually expose very subtle situations.
for (int i = 0; i < 10; i++) {  a = i; <-- array index cannot be 4 billion in a sane application either.};

This is a personal view and obviously not widespread in use (as judged from the posts here) but I always try to use the sign according to context. So if a loop should not be negative then unsigned will be used; if a generated random value should be positive then I'll use unsigned, etc (actually this issue of signed and unsigned becomes absolutely critical within some pseudo-random number generator algorithms). I feel it is just good practice to be aware of the allowable or expected limits of all parts of the code no matter how small, and to code accordingly.

--random
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Quote:Original post by Sneftel
for (int i = 0; i < 10; i++) {  a = i; <-- array index cannot be 4 billion in a sane application either.};


er, why would i be 4 billion, and why would having i be -1 make a difference?

Quote:Original post by random_thinker
This is a personal view and obviously not widespread in use (as judged from the posts here) but I always try to use the sign according to context. So if a loop should not be negative then unsigned will be used; if a generated random value should be positive then I'll use unsigned, etc (actually this issue of signed and unsigned becomes absolutely critical within some pseudo-random number generator algorithms). I feel it is just good practice to be aware of the allowable or expected limits of all parts of the code no matter how small, and to code accordingly.

--random

I think to some extent this can be bad - for example, using a char to represent an ascii value is fine, but chances are it just means 3 bytes are wasted because everything is int-aligned.
using signed in a for loop is totally normal. Not a "security risk". I once tried to do this but then you end up using the value to pass into a function which expects signed int, and end up with a nasty mess of casts.

int is the simple and the default thing to use, whenever you don't have extremely large values to deal with (or bitmasks).
Quote:Original post by thedustbustr
Quote:Original post by Sneftel
for (int i = 0; i < 10; i++) {  a = i; <-- array index cannot be 4 billion in a sane application either.};


er, why would i be 4 billion, and why would having i be -1 make a difference?

The point is that unsigned values cause bugs and vulnerabilities in exactly the same places as signed values. The computer doesn't do the range checking for you either way.

This topic is closed to new replies.

Advertisement