Be carefully with unsigned types in C++

Translations: de

In addition to the integer types short int, int, long int and so on, in C/C++ there are unsigned types that can only store non-negative values. The unsigned types are marked as unsigned (unsigned int etc.), the signed types signed like signed int. The predicate signed can also be omitted. An exception is the type char, which is primarily intended for characters. If used as an shortest integer type, which is also possible, it depends on the implementation whether the type works with or without sign. signed or unsigned must be specified here if the programme is to work with all compilers and systems.

The unsigned types have the advantage that the absulte value of the numbers can be larger, since the sign bit is not needed [1]. Since the standards for C and C++ leave the definition of the exact number ranges for these types to the implementation, only one typical number range can be mentioned here:

signed and unsigned types
type min max
signed int -2,147,483,648 2.147.483.647
unsigned int 0 4.294.967.295

Typically, one can assume a doubling of the maximum value for the unsigned types.

The following rules apply to the integer types in C/C++:

Since in C and C++ many type conversions are done implicitly, the use can be sometimes difficult: an unexpected conversion of a negative number into an unsigned number results in a wrong value:

unsigned int base = 10;
int a = -17;
cout << ( a % base ) << endl; // output: 9

The output here is not -7, as one might expect here. The reason is that the int number -17 is converted to an unsigned int number (4294967279), and then modulo 10 is calculated. This conversion is typical: If an operand of a binary operation is is unsigned, the other operand is also converted to an unsigned type.

Since this is not always easy to keep track of, the general recommendation is not to use mixed operations of unsigned and signed types. This can be achieved by an explicit conversion:

unsigned int base = 10;
int a = -17;
cout << ( a % (int)base ) << endl; // output: -7

Another risk in using unsigned types is that the range limit 0 is often reached. Now, loops are often programmed in such a way that the loop is run until the run variable leaves a desired range:

for (unsigned int a = 0; a < 100; a++) // OK.
        cout << a << endl;
for (unsigned int a = 99; a >= 0; a--) // Endless loop.
        cout << a << endl;

While the first loop works without problems, the second loop does not stop. The problem: an unsigned int variable never becomes less than zero, the test a >= 0 is always true [2]. If a has the value 0 and is decremented again, it is set to the largest (positive) value - C and C++ do not test for overflows or range overruns.

The problem is not so easy to solve. A functioning loop with decreasing values of a has an astonishing form:

for (unsigned int a = 99; a < 100; a--)
        cout << a << endl;

[1]This explanation is a bit simplified. Typically the sign is not stored in a separate bit.
[2]A good compiler warnes here, that a>=1 is always true.