Far too many C programmers think that unsigned ints are an appropriate type to use simply because the quantity in question can never be negative, e.g. the length of a list or the size of a memory allocation. What they fail to appreciate is that almost inevitably such values will get used in subtraction operations down the road, and then your chances of having a corner-case bug grow exponentially.
I doubt more than 1% of non-security-hardened extant C programs are completely safe when any unsigned quantity in the program may acquire a value with the high bit set.
> Far too many C programmers think that unsigned ints are an appropriate type to use simply because the quantity in question can never be negative, e.g. the length of a list or the size of a memory allocation.
size_t is the type for such things and it is defined by the standard to be unsigned. I would like to point out that since it is the standard that defined it, it is not the fault of an average programmer.
> What they fail to appreciate is that almost inevitably such values will get used in subtraction operations down the road, and then your chances of having a corner-case bug grow exponentially.
C has always had the approach that the language is hands off; meaning that if the programmer wants to shoot themselves in the foot they very well can. Furthermore, not using an unsigned int is not going to help the issue for two reasons: you might very well need the entire address space provided by the 32 bits and somewhere down the line you will eventually need to cast to size_t when you call malloc. The solution is to simply make sure that you test for overflow/underflow where appropriate or choose a different language.
The C standard specifies a good proportion of bad ideas, though mostly out of backward compatibility.
Just consider size_t and its friend ptrdiff_t. Suppose you're trying to copy a slice out of an array. If the array indices are size_t, the length of your slice is ptrdiff_t, which you cannot then safely pass to malloc.
There are two solid solutions; (a) limit allocation to the positive range of the signed integer type (e.g. 2GB in 32-bit apps), or (b) use the next signed type up. C, being a systems-level language, doesn't want to take the limitations of (a) nor the platform dependence of (b). The result is that it makes it very hard to get right for average programmers not used to writing OSes and standard libraries.
It's no theoretical issue. When I was working on the Delphi compiler, I had to audit the runtime library to eliminate integer overflow issues. It wasn't trivial to catch them all, and they can become security bugs with medium difficulty. It was eye-opening enough to make me wary.
I doubt more than 1% of non-security-hardened extant C programs are completely safe when any unsigned quantity in the program may acquire a value with the high bit set.