On 04/19/2012 03:45 AM,
wrote:
> I have a structure defined as:
>
> struct MyStruct {
> <some fields of different types>
> unsigned char buffer[MAX_BUFLEN];
> };
>
> The array buffer[] can store different types of variables: int,
> unsigned int, long, unsigned long, null-terminated strings (of
> course, MAX_BUFLEN >= sizeof(long)).
>
> Am I allowed to use a cast to long or int, as in the following
> instructions?
> struct MyStruct s;
> (long *)s.buffer = -100000L;
> (unsigned long *)s.buffer = +100000UL;
> (int *)s.buffer = -100;
> (unsigned int *)s.buffer = +100U;
> long l = *(long *)s.buffer;
> unsigned long ul = *(unsigned long)s.buffer;
> int i = *(int *)s.buffer;
> unsigned int ui = *(unsigned int *)s.buffer;
>
> I think this could work on some platforms, but is not fully portable
> (mostly on 16- and 32-bits). It would work only if buffer[] field is
> correctly aligned in the structure, but it's not guaranteed (it may
> depends from the previous fields in the structure).
>
> I think there are two solutions.
> Avoiding to use cast and use memcpy (but I don't like it):
> long l = -100000L;
> unsigned long ul = 100000UL;
> int i = -100;
> unsigned int ui = +100;
> memcpy(s.buffer, &l, sizeof(l));
> memcpy(s.buffer, &ul, sizeof(ul));
> memcpy(s.buffer, &i, sizeof(i));
> memcpy(s.buffer, &ui, sizeof(ui));
> memcpy(&l, s.buffer, sizeof(l));
> memcpy(&ul, s.buffer, sizeof(ul));
> memcpy(&i, s.buffer, sizeof(i));
> memcpy(&ui, s.buffer, sizeof(ui));
That works, but is not the best solution.
> Moving the buffer[] field at the top of the structure, so it is
> aligned for sure and I can use casting.
The structure will have an alignment requirement at least as great as
the most strictly aligned type of any member. If the other members of
the structure include an 'int', then putting buffer at the beginning is
sufficient to ensure that it's correctly aligned for conversion to
'int'. If the other members include a long, then putting buffer at the
beginning will be sufficient to ensure it's correctly aligned for a
long. Otherwise, there's no guarantee. Even if those cases apply, it's a
bad idea to rely upon something like that: your code using buffer could
break if you remove or change the types of other members of the struct.
> Another solution could be to define MyStruct as:
>
> struct MyStruct {
> <some fields of different types>
> struct {
> unsigned char buffer[MAX_BUFLEN];
> } buf_aligned;
> };
The fact that you think this makes a difference suggests that you
believe structs always have universal alignment. That may be true on
many systems, but there's no such requirement.
> Are thre other solutions?
Yes - what you're trying to do is precisely what unions were invented to
handle. You should define a discriminated union structure:
struct MyStruct {
enum {UNKNOWN, INT, UINT, LONG, ULONG} member_type;
union {
int i;
unsigned int ui;
long l;
unsigned long ul;
} member;
};
The fields member.i, member.ui, member.l, and member.ul are all
guaranteed to be correctly aligned, and are all stored in overlapping
memory locations. Therefore, only one member of the union can be in use
at any given time. Set member_type to indicate which one that is -
that's the feature that makes this a "discriminated" union. The names
I've used for the enumeration constants aren't very good; they should be
longer, to avoid conflict with other identifiers, but I hope you get the
idea. If you're using the same constants in many other contexts, you
should move the definition of the enumeration outside the definition of
the struct.
--
James Kuyper