Mike Wahler said:
Using a signed integer, overflow will give undefined behavior.
Using an unsigned integer, overflow gives well-defined behavior,
but an incorrect value. Which is easier to detect?
In this particular example, we probably want to call malloc() with width *
height to create the pixels for our image. So any values of width * height
that overflow SIZE_MAX are potential problems.
And because of the way that ANSI have defined the behaviour of signed and
unsigned types, it is actually easier to do this using unsigned rather than
signed arithmetic, so you have a point, in this particular case.
However if we were to use a different allocation scheme internally, then the
point would no longer hold.
Also, if create_image() is called with huge parameters, there are two
possibilities. Either they have been entered by a human who genuinely wants
a huge image for some reason, or they are corrupt values (eg random memory).
As a humans we know that if the function is called with a demand for an
image 1000000 by 1000000 pixels then it is impossible that such round vlaues
could have arisen by chance, and it must be someone wanting a giant image.
Such a person doesn't want an assertion fail, or to be told that his
parameters are invalid, because an image of a milion pixels square is
clearly a logical possibility. He wants to be told "sorry, the computer does
not have enough memory to fulfil your request".
However there is no way a computer can distinguish such a call from a
request for 1352678 by 2511044 pixels, which is typical garbage.
It will have succeeded in performing what was requested of it.
If the request was wrong, it's the coder's fault. Choosing
signed over unsigned can't prevent it.
It's a bug in the function. A huge value could overflow to a small value, so
the call to malloc() succeeds, and UB when you try to access the pixels.
It allows the possiblity of undefined behavior.
whilst size_t doesn't.
If we call malloc(width * height) with huge values, then if width and height
are ints then this is UB. In this case UB is actually good, because it means
the computer is allowed to perform correct behaviour (terminating the
program with an error message). Defined wrong behaviour is far more
dangerous than UB.
It always has well-defined behavior. (And can represent the
size of any object. No other type provides this guarantee).
This is the problem. In my opinion ANSI have dug C into a hole with size_t.
In a narrow technical sense they are right - malloc() can legitmately be
called with a request for more memory than will fit in INT_MAX, so let's
have a special type. But then that means that strings can be longer than
INT_MAX as well, so strlen() has to return a size_t. Then if strings can be
longer than INT_MAX, then an index into a character array must be size_t as
well, And in fact it applies to all objects, so if we represent the number
of employees in a payroll function by an int that is wrong as well, strictly
it must be size_t. So without really realising they were doing it, ANSI made
a fundamental change to the language. And this is when more modern language
like Java have done away with unsigned types altogether, because of the
problems they cause.