This is a historical C design error that has also been repeated in C ++.
It goes back to 16-bit computers, and the error decided to use all 16 bits to represent sizes up to 65536, giving up the ability to represent negative sizes.
It would not be a mistake if the unsigned value were a "non-negative integer" (the size cannot be logically negative), but this is a problem with the language conversion rules.
Given the rules for converting the language, the unsigned type in C does not represent a non-negative number, but instead it looks more like a bitmask (the mathematical term is actually " ℤ/n ring member "). To understand why to consider that for the language C and C ++
unsigned - unsigned gives unsigned resultsigned + unsigned gives and unsigned result
both of them obviously make no sense if you read unsigned as a "non-negative number".
Of course, saying that the size of the object is a member of the ℤ/n ring, it makes no sense at all, and here it is, where the error is.
Practical implications:
Every time you deal with the size of an object , be careful because this value is unsigned , and this type in C / C ++ has many properties that are illogical to a number. Always remember that unsigned does not mean “non-negative integer”, but “a member of the algebraic ring ℤ/n ” and, most dangerous, in the case of a mixed operation, a int converted to unsigned int and not vice versa.
For example:
void drawPolyline(const std::vector<P2d>& pts) { for (int i=0; i<pts.size()-1; i++) { drawLine(pts[i], pts[i+1]); } }
is a mistake because if it skips an empty dot vector, it will perform illegal operations (UB). The reason is that pts.size() is unsigned .
The rules of the language convert 1 (integer) to 1{mod n} , will subtract in ℤ/n , resulting in (size-1){mod n} , will also convert i to the representation {mod n} and will perform the comparison in ℤ/n .
C / C ++ actually defines the < operator in ℤ/n (rarely performed in mathematics), and you get access to pts[0] , pts[1] ... and so on to huge numbers, even if the input vector was empty .
The right cycle could be
void drawPolyline(const std::vector<P2d>& pts) { for (int i=1; i<pts.size(); i++) { drawLine(pts[i-1], pts[i]); } }
but I usually prefer
void drawPolyline(const std::vector<P2d>& pts) { for (int i=0,n=pts.size(); i<n-1; i++) { drawLine(pts[i], pts[i+1]); } }
in other words, get rid of unsigned as soon as possible and just work with regular ints.
Never use unsigned to represent the size of containers or counters, because unsigned means " ℤ/n member" and container size is not one of these things. Unsigned types are useful, but NOT for representing the size of objects.
The standard C / C ++ library, unfortunately, made this wrong choice, and it is too late to fix it. However, you are not required to make the same mistake.
According to Bjarne Straustrup :
Using unsigned instead of int to get another bit to represent positive integers is almost never a good idea. Attempts to ensure that some values are positive by declaring unsigned variables are usually defeated by implicit conversion rules