Caution required with integer conversions Wednesday 2nd January 2008

This applies to both C and C++, although the details are a little different in the more obscure corners. Consider this C++ snippet which is designed to position a streambuf somewhere a bit before its end:

pos = sb->pubseekoff( -128 - thing.GetSize(), ios_base::end, ios_base::in );

How did this fail spectacularly?

OK, here are the types.

sb is a std::streambuf
thing.GetSize() returns an unsigned int
pubseekoff takes a streambuf::off_type which is some sort of signed type for representing stream offsets.

Here’s what happened on my implementation.

128 is an integer constant, and fits in an int, so it’s an int.
After integral promotions, it’s still an int (phew!).

-128 is, therefore an int.

thing.GetSize() is an unsigned int, so -128 gets converted to an unsigned int for the binary ‘-‘ operation. (Uh-oh!)

Finally this unsigned int gets converted into whatever streambuf::off_type is. If off_type behaved like a signed int then (implementation defined!) you might get the conversion to wrap around back to the negative numbers to a value that you were expecting in the first place.

On the other hand, off_type might act like a 64-bit signed integer type, with unsigned int being a 32-bit unsigned type. In this case, the very large positive unsigned 32-bit number that you were hoping was actually a reasonably small negative number will quite happily stay large and positive in the conversion. The function call then ends up trying to seek 3.99 GBytes beyond the end of the stream. Whoops.

Comments are closed.