Allocating single memory block for multiple structures

  • Thread starter Everton da Silva Marques
  • Start date
E

Everton da Silva Marques

Hi,

I need to allocate, using malloc(), a single
memory block to hold two structures and a
variable length string. Is it safe (portable,
alignment-wise) to sum up the sizeof's of those
structures and add the length of the string,
as in this snippet?

const char *canonname = "domain.tld";
struct addrinfo *ai;
struct sockaddr_in *sa;

ai = (struct addrinfo *) malloc(sizeof(struct addrinfo) +
sizeof(struct sockaddr_in) +
strlen(canonname) + 1);

sa = (struct sockaddr_in *) (((char *) ai) + sizeof(struct addrinfo));
sa->sin_family = PF_INET;
sa->sin_port = htons(80);
sa->sin_addr.s_addr = INADDR_ANY;

ai->ai_addr = (struct sockaddr *) sa;
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_canonname = ((char *) ai) +
sizeof(struct addrinfo) +
sizeof(struct sockaddr_in);
strcpy(ai->ai_canonname, canonname);

Thanks,
Everton
 
E

Eric Sosman

Everton said:
Hi,

I need to allocate, using malloc(), a single
memory block to hold two structures and a
variable length string. Is it safe (portable,
alignment-wise) to sum up the sizeof's of those
structures and add the length of the string,
as in this snippet?

No.
const char *canonname = "domain.tld";
struct addrinfo *ai;
struct sockaddr_in *sa;

ai = (struct addrinfo *) malloc(sizeof(struct addrinfo) +
sizeof(struct sockaddr_in) +
strlen(canonname) + 1);

sa = (struct sockaddr_in *) (((char *) ai) + sizeof(struct addrinfo));

Here's the problem: A `struct sockaddr_in' might require
stricter alignment than a `struct addrinfo', so just sliding
forward by `sizeof(struct addrinfo)' bytes may not arrive at
an address where it's legitimate for a `struct sockaddr_in'
to begin.
sa->sin_family = PF_INET;
sa->sin_port = htons(80);
sa->sin_addr.s_addr = INADDR_ANY;

ai->ai_addr = (struct sockaddr *) sa;
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_canonname = ((char *) ai) +
sizeof(struct addrinfo) +
sizeof(struct sockaddr_in);

You're safe on this one, though, because an array of
`char' can begin at any location without concern for
alignment issues.
strcpy(ai->ai_canonname, canonname);

Although the code above is not portable and not safe,
there is a safe and portable and simple alternative:

struct addrstuff {
struct addrinfo ai;
struct sockaddr_in sa;
} *both;

both = malloc(sizeof *both + strlen(canonname) + 1);
if (both == NULL)
crash_and_burn();

both->sa.sin_family = PF_INET;
/* etc. */
both->ai.ai_addr = (struct sockaddr *) &both->sa;
/* etc. */
both->ai.ai_canonname = (char *)(both + 1);
strcpy (both->ai.ai_canonname, canonname);

Since the compiler knows the alignment requirement of
every type, it knows whether to insert padding bytes in
the middle of a `struct addrstuff' so that both elements
will be properly aligned.

It may also add padding at the very end of a `struct
addrstuff' so that everything would remain properly aligned
if you decided to allocate an array of them, but from your
point of view this is wasted space. It'll probably be a
small amount of waste, but if you Really Really want to
uglify the code to save those few bytes, you could write

both = malloc(offsetof(struct addrstuff, sa)
+ sizeof(struct sockaddr_in)
+ strlen(canonname) + 1);
...
both->ai.ai_canonname = (char*)(&both->sa + 1);

Personally, I don't think this is worth while nowadays.
 
G

Gordon Burditt

I need to allocate, using malloc(), a single
memory block to hold two structures and a
variable length string. Is it safe (portable,
alignment-wise) to sum up the sizeof's of those
structures and add the length of the string,
as in this snippet?

No. Consider what happens if you put the string, or a fixed-length
prime-length character array, FIRST. Chances are a struct following
it is going to be misaligned if it requires alignment at all.

There is no guarantee that a struct sockaddr_in doesn't require
stricter alignment than a struct addrinfo.
const char *canonname = "domain.tld";
struct addrinfo *ai;
struct sockaddr_in *sa;

ai = (struct addrinfo *) malloc(sizeof(struct addrinfo) +
sizeof(struct sockaddr_in) +
strlen(canonname) + 1);

sa = (struct sockaddr_in *) (((char *) ai) + sizeof(struct addrinfo));
sa->sin_family = PF_INET;
sa->sin_port = htons(80);
sa->sin_addr.s_addr = INADDR_ANY;

ai->ai_addr = (struct sockaddr *) sa;
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_canonname = ((char *) ai) +
sizeof(struct addrinfo) +
sizeof(struct sockaddr_in);
strcpy(ai->ai_canonname, canonname);

Gordon L. Burditt
 
E

Everton da Silva Marques

Eric Sosman said:
Although the code above is not portable and not safe,
there is a safe and portable and simple alternative:

struct addrstuff {
struct addrinfo ai;
struct sockaddr_in sa;
} *both;

In this example, is it safe to cast
(struct addrstuff *) to (struct addrinfo *),
so to access members in both->ai ?
Like this:

struct addrinfo *ai = (struct addrinfo *) both;
ai->ai_addr = ...

I mean:

1) is member ordering guaranteed in struct's ?
2) does the first member always align at
the begining of the struct?

BTW, awesome answer, thanks!
 
S

Sam Dennis

Everton said:
In this example, is it safe to cast (struct addrstuff *) to (struct
addrinfo *), so to access members in both->ai ?

Yes, but wholly unnecessary and a possible cause of trouble in the
future, as you might decide to change the order of the structure's
members later.
struct addrinfo *ai = (struct addrinfo *) both;
ai->ai_addr = ...

both->ai.ai_addr = ...
1) is member ordering guaranteed in struct's ?

Yes, basically.
 
J

J. J. Farrell

In this example, is it safe to cast
(struct addrstuff *) to (struct addrinfo *),
so to access members in both->ai ?
Like this:

struct addrinfo *ai = (struct addrinfo *) both;
ai->ai_addr = ...
Yes.

I mean:

1) is member ordering guaranteed in struct's ?
Yes.

2) does the first member always align at
the begining of the struct?

Yes.
 
X

xarax

Gordon Burditt said:
No. Consider what happens if you put the string, or a fixed-length
prime-length character array, FIRST. Chances are a struct following
it is going to be misaligned if it requires alignment at all.

There is no guarantee that a struct sockaddr_in doesn't require
stricter alignment than a struct addrinfo.
/snip/

You will need to calculate the alignment of
each struct, and then calculate the offset
of the next location that is compatible with
that alignment.

You can use a macro like this to calculate
the alignment of a type X:

#define alignof(X) (sizeof(struct{X a;char b})-sizeof(struct{X a;}))

The parameter X is a type name (usually a typedef name
or a type name that is syntactically correct for the
macro expansion). The macro will expand to a constant
expression. Some compilers, like M$ visual C++ will
issue a warning about the unnamed struct, but just turn
off that stupid warning.
 

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,766
Messages
2,569,569
Members
45,045
Latest member
DRCM

Latest Threads

Top