Richard Heathfield said:
Keith Thompson said:
There are more things in mathematics, Horatio... and I'm sure I need not
complete the misquote for you.
Good point. I probably should have nailed down what I meant by
"mathematical result" sooner, and/or used a different phrase.
However, note that C99 6.2.5p9 says:
A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting
unsigned integer type is reduced modulo the number that is one
greater than the largest value that can be represented by the
resulting type.
The word "result" here can only refer to what I've been calling the
"mathematical result". You're complaining about my lack of precision,
but the standard's wording here is no more precise than mine.
}
"If any" being the key words there.
[...]
In the following, an expression enclosed in << and >> is to be
interpreted as being evaluated over the entire infinite set of
integers.
If X and Y are values of type size_t, a call malloc(X * Y) will
attempt to allocate <<(X * Y) % (SIZE_MAX+1)>> bytes. If this is not
equal to <<X * Y>>, then the result is affected by what the standard
doesn't call "overflow" (let's call it "wraparound"). This is the
behavior clearly specified by the standard, but I can't think of any
circumstances in which it's the *desired* behavior. It's almost
certainly a logical error.
If I write malloc(X * Y), it's because I *want* to allocate <<X * Y>>
bytes. The result of the multiplication will be silently reduced
whether I want it to be or not. The only real solution, given the
language as it currently exists, is to be very careful not to let this
kind of logical error occur in the first place (because the language
is of no help in diagnosing it if I'm not sufficiently careful). This
is non-trivial. A more general solution might be to modify the
language so that cases where X * Y wraps around can be detected, but
that's not going to happen any time soon, if ever.
Somebody (jacob, I think) suggested using calloc(X, Y) rather than
malloc(X * Y), assuming that calloc() correctly detects wraparound on
the multiplication. I disagree with this for two reasons. First,
it's already been pointed out that not all implementations of calloc()
get this right. Second, zeroing the allocated memory is likely to be
wasteful.
If this is a concern, you can write a wrapper around malloc() that
takes two arguments (as calloc() does) and checks whether the result
wraps around. Something like this (obviously untested):
void *malloc_wrapper(size_t x, size_t y)
{
if (/* x*y wraps around */) {
return NULL;
}
else {
return malloc(x * y);
}
}
Implementing the wraparound test is left as an exercise.