Re: A malloc question



Why Tea wrote:
Eric Sosman wrote:
Why Tea wrote:
Richard Tobin wrote:
In article <1167319643.682777.100860@xxxxxxxxxxxxxxxxxxxxxxxxxxx>,
Why Tea <ytlim1@xxxxxxxxx> wrote:

p = malloc(100 + sizeof(SomeStruct)); /* 100 is not multiples of sizeof(SomeStruct) */
/* do something before freeing it */
Be sure you know what you're doing here. You can't, for example,
reliably store 100 chars followed by a struct in this memory, because
the alignment of p+100 may not be suitable for the struct.
Thanks Richard. In fact I was reading someone else's code. The
intention of the code is to have a memory allocation similar to this:

|<- SomeStruct ->|<- UserStruct ->

Hence,
SomeStruct *p;
p = malloc(sizeof(SomeStruct) + sizeof(UserStruct)); /* UserStruct can
be anything */
...
free(p);

(p+1) points to the beginning of UserStruct, so users can store
anything in there.

My understanding is, the paddings (if any) for memory alignment are
included in the calculation of sizeof(). So the code should work
properly. Is my understanding correct?
Padding bytes are part of the struct, and so are included
in the sizeof count. But that doesn't solve the problem of
getting proper alignment for the UserStruct part! Imagine

typedef struct { int x; } SomeStruct;
typedef struct { double y; } UserStruct;

and suppose (1) that there are no padding bytes, (2) that an
int occupies four bytes and must be aligned on a four-byte
boundary, and (3) that a double occupies eight bytes and must
be aligned on an eight-byte boundary. Then the code above will
request twelve bytes, and the allocated memory will begin on an
eight-byte boundary (because the memory returned by malloc() is
properly aligned for any use whatever, hence it's aligned at
least as strictly as a double would require). If p points to
an eight-byte boundary, then p+1 points four bytes further
along, to a four-byte boundary that is not an eight-byte
boundary, and a UserStruct can't begin there.

Things will work all right if UserStruct's alignment is no
stricter than SomeStruct's, but there's no a priori way to know
that this is true. If the actual type of UserStruct is known
(and not just its size), the simplest approach may be

struct { SomeStruct s; UserStruct u; } *p;
p = malloc(sizeof *p);
if (p == NULL) ...
p->s.x = 42;
p->u.y = 42.0;
...
free (p);

In a situation where only sizeof(UserStruct) is known and
not its actual type, a traditional but slightly iffy approach
is to try to figure out what the strictest alignment is:

typedef struct {
int i; long l; long long ll;
float f; double d; long double ld;
char *cp; int *ip; long *lp; long long *llp;
float *fp; double *dp; long double *ldp;
struct whocares *sp;
union whoknows *up;
void (*funcp)(void);
} Worst;
#define PADDED(n) ( ((n) + sizeof(Worst) - 1) \
/ sizeof(Worst) * sizeof(Worst) )
SomeStruct *p = malloc(PADDED(sizeof(SomeStruct))
+ size_of_user_struct);
if (p == NULL) ...
/* Now p points to the SomeStruct, and
* (char*)p + PADDED(sizeof(SomeStruct))
* points to the UserStruct
*/

One drawback to this approach is that it may overestimate
the strictness of UserStruct's alignment. Another is that it's
not possible to enumerate all possible types in the Worst
definition ...

Eric, thanks for the very detailed answer. But there is still something
that is not clear to me.
Suppose the following:
sizeof(SomeStruct) = 8
sizeof(UserStruct) = 16
malloc() will see a request of 24 byte allocation. How does it work out
whether alignment is at 4 byte, 8 byte or 16 byte boundary?

Since malloc() is part of the implementation, it can use
"insider information" that isn't available outside the boardroom.
Among other things, malloc() knows the alignment requirements of
all types on its own implementation. For example, malloc() might
"just know" that on a particular machine the strictest alignment
required for any type at all is eight bytes; knowing this, malloc()
can simply return eight-byte-aligned memory for every request, and
then it doesn't need to worry about whether the caller will use the
memory for three eight-byte longs or twelve two-byte shorts or
twenty-four characters or a struct of several elements or ...

The C Standard says that a successful malloc() obtains memory
that is "suitably aligned so that it may be assigned to a pointer
to any type of object and then used to access such an object." In
effect, the Standard not only sanctions but requires malloc() to
use the strategy outlined above. There is an angels-on-pinheads
quibble about whether malloc(4), say, needs to satisfy alignment
requirements that only apply to objects of eight bytes or larger;
if long and double and long double are the only types requiring
eight-byte alignment and they are all eight bytes long, clearly
the caller is not going to store one of them (or a struct that
holds one of them) in the memory obtained by malloc(4). If the
program does so it invokes undefined behavior anyhow, so malloc(4)
might return a more loosely-aligned memory block. Whether this is
legal or not has been debated a few times; both sides of the argument
have their adherents.

Based on the assumption above, if we were to assume the stricter
UserStruct comes first, will it avoid the problem?

Probably. It is conceivable, I suppose, that a bizarre machine
might have alignment requirements that are not multiples of each
other: four-byte alignment for int, say, and ten-byte alignment
for long double. On such a machine the only way an int and a
long double could be adjacent would be if one was just before and
one just after a twenty-byte boundary -- and since malloc() would
need to return twenty-byte-aligned memory, it would never be
possible to juxtapose the two objects at the start of a malloc'ed
memory area. (The second would always be mis-aligned, no matter
which one it was.) Personally, I have never run into a machine
with such bizarre alignment requirements and I never expect to,
but the C Standard doesn't rule it out.

A more practical problem is to determine which of the two
types has the stricter alignment. There's a cute little hack
floating about that lets you make a pretty good guess about the
alignment needed for a known type -- Google +alignof +offsetof --
but you need the actual type name, not just the type's size.

--
Eric Sosman
esosman@xxxxxxxxxxxxxxxxxxx
.



Relevant Pages

  • Malloc statistics patch
    ... Now that the UMA changes to use critical sections instead of per-cpu mutexes ... memory allocation patch previous posted. ... - Introduce per-cpu malloc type statistics, ... - If we're willing to abandon 5.x backport, we can clean up 'struct ...
    (freebsd-performance)
  • Re: The annotated annotated annotated C standard part 3
    ... Beautiful Code, written in 1990, for memory allocation) is destroyed ... When the ability to use language is so checked by the ... You could avoid a problem I understand exists in malloc(), ... the caller to pass an instance of this struct. ...
    (comp.programming)
  • Re: Need clarity on structure alignment
    ... I remember seeing something that left me with the impression that C guarantees that a struct will be aligned on a pointer-size boundary (using ... If he uses malloc() to obtain memory for a struct, ...
    (comp.lang.c)
  • Re: The annotated annotated annotated C standard part 3
    ... Beautiful Code, written in 1990, for memory allocation) is destroyed ... When the ability to use language is so checked by the ... You could avoid a problem I understand exists in malloc(), ... the caller to pass an instance of this struct. ...
    (comp.programming)
  • Re: A malloc question
    ... reliably store 100 chars followed by a struct in this memory, ... the alignment of p+100 may not be suitable for the struct. ... the paddings for memory alignment are ... getting proper alignment for the UserStruct part! ...
    (comp.lang.c)