By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,886 Members | 1,107 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,886 IT Pros & Developers. It's quick & easy.

alignment when allocating an array of struct

P: n/a
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
Jan 4 '08 #1
Share this Question
Share on Google+
6 Replies


P: n/a
Francois Grieu wrote:
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.

--
Er*********@sun.com
Jan 4 '08 #2

P: n/a
Francois Grieu wrote:
On 4 jan, 19:32, Eric Sosman <Eric.Sos...@sun.comwrote:
>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 ?
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! ;-)

--
Er*********@sun.com
Jan 4 '08 #3

P: n/a
Francois Grieu <fg****@gmail.comwrites:
On 4 jan, 19:32, Eric Sosman <Eric.Sos...@sun.comwrote:
[...]
> 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.
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.)

--
Keith Thompson (The_Other_Keith) <ks***@mib.org>
[...]
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jan 5 '08 #4

P: n/a
In article <1199487009.440461@news1nwk>,
Eric Sosman <Er*********@sun.comwrote:
Francois Grieu wrote:
>On 4 jan, 19:32, Eric Sosman <Eric.Sos...@sun.comwrote:
>>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 ?

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.

>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! ;-)
Took me an iteration to realize that this works even if
size_t is much smaller than long. Thanks!
Francois Grieu
Jan 7 '08 #5

P: n/a
On Fri, 04 Jan 2008 18:50:05 +0100, Francois Grieu <fg****@gmail.com>
wrote:
>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
Jan 8 '08 #6

P: n/a
Barry Schwarz <sc******@doezl.netwrites:
On Fri, 04 Jan 2008 18:50:05 +0100, Francois Grieu <fg****@gmail.com>
wrote:
[...]
>>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.

--
Keith Thompson (The_Other_Keith) <ks***@mib.org>
[...]
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jan 8 '08 #7

This discussion thread is closed

Replies have been disabled for this discussion.