On 6/2/2010 1:11 PM, Andrew Shepson wrote:
> Hello group!
>
> i'm currently working on code where i find myself repeatedly having to
> allocating two objects to store some private data, e.g.:
>
> whio_stream * whio_stream_for_dev( struct whio_dev * dev, bool
> takeOwnership )
> {
> if( ! dev ) return 0;
> whio_stream * str = (whio_stream*)malloc( sizeof(whio_stream) );
> if( ! str ) return 0;
> *str = whio_stream_dev_init;
> whio_stream_dev * sd = (whio_stream_dev*) (str + sizeof
> (whio_stream));
This is surely wrong -- but see below.
> if( ! sd )
> {
> free(str);
> return 0;
> }
> str->implData = sd;
> sd->dev = dev;
> sd->ownsDev = takeOwnership;
> return str;
>
> }
>
> The significant bit there is the two mallocs.
I see only one. Is this, perhaps, a neither-fish-nor-fowl
jumble of the "before" and the "after" into a "don't go there?"
> Today i figured i'd try
> to consolidate that, and turned the above code into:
>
> whio_stream * whio_stream_for_dev( struct whio_dev * dev, bool
> takeOwnership )
> {
> if( ! dev ) return 0;
> whio_stream * str = (whio_stream*)malloc( sizeof(whio_stream) +
> sizeof(whio_stream_dev) );
> if( ! str ) return 0;
> *str = whio_stream_dev_init;
> whio_stream_dev * sd = (whio_stream_dev*) (str + sizeof
> (whio_stream));
This is wrong. Remember what happens when you add an integer
to a pointer: You "step" in units of the pointed-to type. So if
sizeof(whio_stream) is 12, say, you've got `str+12' and you're
trying to produce an address 144 bytes beyond where `str' points.
You could correct this by writing
...sd = (whio_stream_dev*)(str + 1);
But that would quite likely be wrong, too! There's something
called "alignment," which means that objects of certain types can
only begin at addresses that are divisible (when considered as
numbers) by certain divisors. Perhaps an `int' must begin on a
4-byte boundary, or a `whio_stream' on an 8-byte boundary. It's
up to the implementation to decide what types need alignment, and
how much.
... and if `whio_stream' is 12 bytes long and needs 4-byte
alignment (let's say), while `whio_stream_dev' is 8 bytes long
and needs 8-byte alignment, the `sd' pointer will aim at a spot
where it's not legal for a `whio_stream_dev' to begin. There's
no telling what will happen: Your code might run incorrectly, or
slowly, or not at all.
Perhaps the simplest way to fix both problems is to use a
one-off struct to hold both objects:
struct {
whio_stream str;
/* compiler will insert padding here if needed */
whio_stream_dev sd;
} *ptr;
ptr = malloc(sizeof *ptr);
if (ptr == NULL) return 0;
ptr->str = whio_stream_dev_init;
ptr->str.implData = &ptr->sd;
ptr->sd.dev = &ptr->dev;
ptr->sd.ownsDev = takeOwnership;
return &ptr->str;
--
Eric Sosman
lid