alignment when allocating an array of struct

F

Francois Grieu

Hello, I'm asking myself all kind of questions on allocating
an array of struct with proper alignment.

Is the following code oorrect ?

I'm most interested by the statement
t = malloc(n*sizeof(r))
and (to a degree) by the surrounding error checking.


#include <stdlib.h>

// a record of system-dependent size and alignment
typedef struct r
{
char f0;
long f1;
char f2;
} r;

int main(int argc, char *argv[])
{
long n; // number of elements to allocate
r *t; // array of n records of type r
if (argc==2 && // check argv[1] is our single parameter
(n = atol(argv[1]))>0 && // convert to a positive number
(size_t)(n*sizeof(r))/n==sizeof(r) && // check for size overflow
// allocate n records of type r; is that OK ?
(t = malloc(n*sizeof(r)))!=NULL)
{
// some use the array of n records
long j;
for(j=0;j<n;++j)
t[j].f1 = j; // use a (conseivably) aligned field
free(t);
}
return 0;
}


On systems where alignment must occur for proper acess to
the field f1, is sizeof(r) guaranteed to be rounded up
appropriately, and/or malloc guaranteed to return approriately
aligned memory ?

Is the calloc() library function guaranteed to perform something
extra (beside zeroing the allocated memory) ? like, perhaps,
rounding up the size passed, or checking that the product does
not overflow, or making extra alignment ?

As an aside: one of my C compilers barks if I change sizeof(r)
to sizeof r. Is this compiler defective ? No it is NOT C++.


TIA,

Francois Grieu
 
J

jameskuyper

Francois said:
Hello, I'm asking myself all kind of questions on allocating
an array of struct with proper alignment.

Is the following code oorrect ?

I'm most interested by the statement
t = malloc(n*sizeof(r))
and (to a degree) by the surrounding error checking.


#include <stdlib.h>

// a record of system-dependent size and alignment
typedef struct r
{
char f0;
long f1;
char f2;
} r;

int main(int argc, char *argv[])
{
long n; // number of elements to allocate
r *t; // array of n records of type r
if (argc==2 && // check argv[1] is our single parameter
(n = atol(argv[1]))>0 && // convert to a positive number
(size_t)(n*sizeof(r))/n==sizeof(r) && // check for size overflow
// allocate n records of type r; is that OK ?
(t = malloc(n*sizeof(r)))!=NULL)
{
// some use the array of n records
long j;
for(j=0;j<n;++j)
t[j].f1 = j; // use a (conseivably) aligned field
free(t);
}
return 0;
}


On systems where alignment must occur for proper acess to
the field f1, is sizeof(r) guaranteed to be rounded up
appropriately,

The actual size of a type must be an integer multiple of any alignment
requirement it has, in order for arrays of that type to work as
defined by the standard.
... and/or malloc guaranteed to return approriately
aligned memory ?

The malloc(), calloc(), and realloc() functions are all required to
return either a null value indicating an error, or a non-null pointer
to memory that is correctly aligned for all possible uses.
Is the calloc() library function guaranteed to perform something
extra (beside zeroing the allocated memory) ? like, perhaps,
rounding up the size passed, or checking that the product does
not overflow, or making extra alignment ?

None of that is guaranteed, except possibly "extra alignment",
whatever that means. The memory will be correctly aligned.
As an aside: one of my C compilers barks if I change sizeof(r)
to sizeof r. Is this compiler defective ? No it is NOT C++.

The parenthesis are mandatory when the argument is a type name, and
optional when the argument is an expression.
 
E

Eric Sosman

Francois said:
Hello, I'm asking myself all kind of questions on allocating
an array of struct with proper alignment.

Is the following code oorrect ?

I'm most interested by the statement
t = malloc(n*sizeof(r))
and (to a degree) by the surrounding error checking.


#include <stdlib.h>

// a record of system-dependent size and alignment
typedef struct r
{
char f0;
long f1;
char f2;
} r;

int main(int argc, char *argv[])
{
long n; // number of elements to allocate
r *t; // array of n records of type r
if (argc==2 && // check argv[1] is our single parameter
(n = atol(argv[1]))>0 && // convert to a positive number
(size_t)(n*sizeof(r))/n==sizeof(r) && // check for size overflow
// allocate n records of type r; is that OK ?
(t = malloc(n*sizeof(r)))!=NULL)
{
// some use the array of n records
long j;
for(j=0;j<n;++j)
t[j].f1 = j; // use a (conseivably) aligned field
free(t);
}
return 0;
}


On systems where alignment must occur for proper acess to
the field f1, is sizeof(r) guaranteed to be rounded up
appropriately, and/or malloc guaranteed to return approriately
aligned memory ?

Yes and yes. For the first, consider an array `r array[2];'.
Both array[0] and array[1] must be properly aligned, and the
sizeof the array is twice the size of one of its elements, that
is, twice the sizeof(r). So sizeof(r) must include whatever
padding might be needed to make the alignment work properly.
It's not a matter of rounding up the result of sizeof; it's that
the r instance contains its own padding and the padding counts
as part of the sizeof an r.

For the second, the Standard requires that malloc() return
memory that is suitably aligned for *any* type whatsoever. In
particular, this means that the memory is suitably aligned for
an r.
Is the calloc() library function guaranteed to perform something
extra (beside zeroing the allocated memory) ? like, perhaps,
rounding up the size passed, or checking that the product does
not overflow, or making extra alignment ?

As with malloc(), no "rounding up" is needed because any
padding is already accounted for in the sizeof the type. And
as with malloc(), the returned memory must be suitably aligned
for any type at all, including the type r.

As for overflow, I believe a proper calloc() implementation
must detect it and return NULL rather than being oblivious to
it and possibly returning a pointer to a too-small area. The
Standard says that calloc() either allocates enough space for
the implied array or fails to allocate it, and leaves no room
for a calloc() that "sort of" works. However, I'm not 100% sure
that my reading is correct -- and I'm even *less* sure that all
calloc() implementations perform the detection I think is required.
As an aside: one of my C compilers barks if I change sizeof(r)
to sizeof r. Is this compiler defective ? No it is NOT C++.

The compiler is right. r is a type name (an alias for a
type name, actually), so writing `sizeof r' is like writing
`sizeof int' -- and is equally wrong.
 
F

Francois Grieu

sizeof(r) must include whatever padding might be needed to make
the alignment work properly.
It's not a matter of rounding up the result of sizeof; it's that
the r instance contains its own padding and the padding counts
as part of the sizeof an r.

Ah... sizeof(r) itself is already properly rounded up. That's fine,
and clears most of my concerns. But, just to be sure, how is that
expressed in the (C89/90) standard ?

(..) As for overflow, I believe a proper calloc() implementation
must detect it and return NULL rather than being oblivious to
it and possibly returning a pointer to a too-small area. The
Standard says that calloc() either allocates enough space for
the implied array or fails to allocate it, and leaves no room
for a calloc() that "sort of" works. However, I'm not 100% sure
that my reading is correct -- and I'm even *less* sure that all
calloc() implementations perform the detection I think is required.

OK: because some calloc implementations wrongly do not check for
overflow, it should be tested externaly, leaving few reasons
(beside self-documentation) to use calloc versus malloc when
clearing the memory is not required.

Is there something wrong with my overflow test ?
(size_t)(n*sizeof(r))/n==sizeof(r)
Anything less contorted ?

The compiler is right. r is a type name (an alias for a
type name, actually), so writing `sizeof r' is like writing
`sizeof int' -- and is equally wrong.

Got it: sizeof variable is fine, sizeof type is wrong.
Thanks for everything.


Francois Grieu
 
E

Eric Sosman

Francois said:
Ah... sizeof(r) itself is already properly rounded up. That's fine,
and clears most of my concerns. But, just to be sure, how is that
expressed in the (C89/90) standard ?

I don't have either the original ANSI nor the first ISO
version of the Standard handy, so I don't know. In N2794 (a
pre-C99 draft; I've lost a disk and haven't retrieved my
backup of the actual Standard yet), 6.5.3.4p3 says in part

[...] When applied to an operand that has structure or
union type, the result is the total number of bytes in
such an object, including internal and trailing padding.
Is there something wrong with my overflow test ?
(size_t)(n*sizeof(r))/n==sizeof(r)
Anything less contorted ?

Since `n' is a signed long, this risks overflow with
undefined behavior on machines where size_t promotes to
long instead of the other way around. Try converting to
size_t before multiplying, so the arithmetic is unsigned
and "overflow" is well-behaved:

((size_t)n * sizeof(r)) / sizeof(r) == n

This particular rearrangement works even when n==0, and
it also catches negative n when size_t promotes to long.
I think that it also catches negative n when long promotes
to size_t, provided sizeof(r)>1. (Besides, dividing by
a compile-time constant is likely to be faster than by a
variable; just THINK of the nanoseconds you'll save! ;-)
 
K

Keith Thompson

Francois Grieu said:
Got it: sizeof variable is fine, sizeof type is wrong.
Thanks for everything.

Right. More generally, there are two forms of size expression:

sizeof unary-expression
sizeof ( type-name )

The unary-expression may be a variable name, but it doesn't have to
be. For example, ``sizeof 42'' is equivalent to ``sizeof int''.

(The use of "unary-expression" rather than "expression" means that,
for example, ``sizeof 2 + 2'' doesn't apply the sizeof operator to
``2 + 2''; instead, it means ``(sizeof 2) + 2''. It's a way of
expressing operator precedence.)
 
F

Francois Grieu

Eric Sosman said:
Francois said:
Ah... sizeof(r) itself is already properly rounded up. That's fine,
and clears most of my concerns. But, just to be sure, how is that
expressed in the (C89/90) standard ?

I don't have either the original ANSI nor the first ISO
version of the Standard handy, so I don't know. In N2794 (a
pre-C99 draft; I've lost a disk and haven't retrieved my
backup of the actual Standard yet), 6.5.3.4p3 says in part

[...] When applied to an operand that has structure or
union type, the result is the total number of bytes in
such an object, including internal and trailing padding.

I confirm this is also in ISO/IEC 9899:1999.

Since `n' is a signed long, this risks overflow with
undefined behavior on machines where size_t promotes to
long instead of the other way around. Try converting to
size_t before multiplying, so the arithmetic is unsigned
and "overflow" is well-behaved:

((size_t)n * sizeof(r)) / sizeof(r) == n

This particular rearrangement works even when n==0, and
it also catches negative n when size_t promotes to long.
I think that it also catches negative n when long promotes
to size_t, provided sizeof(r)>1. (Besides, dividing by
a compile-time constant is likely to be faster than by a
variable; just THINK of the nanoseconds you'll save! ;-)

Took me an iteration to realize that this works even if
size_t is much smaller than long. Thanks!


Francois Grieu
 
B

Barry Schwarz

Hello, I'm asking myself all kind of questions on allocating
an array of struct with proper alignment.

Is the following code oorrect ?

I'm most interested by the statement
t = malloc(n*sizeof(r))
and (to a degree) by the surrounding error checking.


#include <stdlib.h>

// a record of system-dependent size and alignment
typedef struct r
{
char f0;
long f1;
char f2;
} r;

int main(int argc, char *argv[])
{
long n; // number of elements to allocate
r *t; // array of n records of type r
if (argc==2 && // check argv[1] is our single parameter
(n = atol(argv[1]))>0 && // convert to a positive number

atol is not a very good function for this purpose. Better to use
strtol and take advantage of its error checking capabilities prior to
using the result in the if.
(size_t)(n*sizeof(r))/n==sizeof(r) && // check for size overflow

What is the type of the product of a long and a size_t. Not just on
your system but in general. If size_t is an unsigned int, I think the
result would be a long and overflow could occur. Better to eliminate
the possibility and move the cast inside the parentheses to get
((size_t)n*sizeof(r))

Even better still would be to change n to a size_t and use strtoul
instead of strtol or atol above.
// allocate n records of type r; is that OK ?
(t = malloc(n*sizeof(r)))!=NULL)

1 - Your parentheses are unbalanced. You are missing a ).

2 - Wouldn't you like to be able to put out a different error message
depending on whether argc was not 2 or malloc failed or ...?

While the short-circuit mechanism inherent in && can make this complex
if logically correct, it is a maintenance nightmare.
{
// some use the array of n records
long j;
for(j=0;j<n;++j)
t[j].f1 = j; // use a (conseivably) aligned field

Under what conditions would you expect an array element or struct
member to not be aligned?
free(t);
}
return 0;
}


On systems where alignment must occur for proper acess to
the field f1, is sizeof(r) guaranteed to be rounded up

Consider the havoc it would cause if you defined a simple array of
struct and the size was not rounded up as needed. Yes it is
guaranteed.
appropriately, and/or malloc guaranteed to return approriately
aligned memory ?

malloc and friends are also guaranteed to return an address properly
aligned for any and every possible type of object, scalar or
aggregate.
Is the calloc() library function guaranteed to perform something
extra (beside zeroing the allocated memory) ? like, perhaps,
rounding up the size passed, or checking that the product does

On what basis would it round up more than the product? It has no idea
what type of pointer you are assigning the resulting address to.
not overflow, or making extra alignment ?

Since the alignment is guaranteed to be suitable for any type of
object, what possible extra alignment is there?

Since calloc allocates space for an array and size_t can hold the size
of the largest possible array, I suggest that consistency demands that
passing parameters to calloc where the product would exceed SIZE_MAX
invokes undefined behavior
As an aside: one of my C compilers barks if I change sizeof(r)
to sizeof r. Is this compiler defective ? No it is NOT C++.

The parentheses are required unless the operand is an object. r is
not an object. It is a typedef alias for struct r and both
expressions are types, not objects. You could use *t with or without
parentheses since *t is an object of type r. Any of your compilers
that do not bark on sizeof r are broken.


Remove del for email
 
K

Keith Thompson

Barry Schwarz said:
[...]
As an aside: one of my C compilers barks if I change sizeof(r)
to sizeof r. Is this compiler defective ? No it is NOT C++.

The parentheses are required unless the operand is an object. r is
not an object. It is a typedef alias for struct r and both
expressions are types, not objects. You could use *t with or without
parentheses since *t is an object of type r. Any of your compilers
that do not bark on sizeof r are broken.

Correction: the parentheses are required unless the operand is an
expression. For example ``sizeof 42'' is valid.

The operand of a sizeof operator is either an expression (specifically
a "unary-expression") or a type name in parentheses.
 
D

David Thompson

OK: because some calloc implementations wrongly do not check for
overflow, it should be tested externaly, leaving few reasons
(beside self-documentation) to use calloc versus malloc when
clearing the memory is not required.
I would say no reasons except clearing. And remember that calloc or
memset(,0,) clearing to all-bits-zero MIGHT not be correct for fltpt
and pointers, although in practice it usually is. To be absolutely
100% guaranteed portable, you need to do something like
static r empty /* = {0} implied for static but can say anyway;
a decent compiler will infer const, but could say that too */ ;
for( i = 0; i < n; i++ ) newarray = empty;

Of course if you actually want to initialize to something not zeros,
but constant, make the obvious change.
Is there something wrong with my overflow test ?
(size_t)(n*sizeof(r))/n==sizeof(r)
Anything less contorted ?
That works, but I think n <= (size_t)-1 / sizeof(r) is clearer as well
as slightly simpler. In C99 can substitute SIZE_MAX.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top