Groovy hepcat Mark A. Odell was jivin' on 27 Oct 2003 15:58:13 GMT in
comp.lang.c.
Re: Copying struct and unsigned char buffer's a cool scene! Dig it!
Roy Hills <ro******@hotmail.com> wrote in
news:9a********************************@4ax.com :
When I'm reading from or writing to a network socket, I want to use a
struct to represent the structured data, but must use an unsigned char
buffer for the call to sendto() or recvfrom().
I have two questions:
1. Is it generally safe to "overlay" the structure on the buffer,
e.g.:
unsigned char buffer[BUFSIZ];
struct header {
whatever;
};
struct header *hdr = (struct header *) buf;
or is it safer / more portable to declare a seperate struct and use
memcpy() to copy it to or from the buffer? Obviously, it would be
That's the way to go. But copy struct members individually. (See
below.)
more efficient to avoid the memcpy() if possible.
The overlay seems to work fine, but I'm always concerned that I'm
doing something non-portable or ineligant whenever I cast a pointer.
You can overlay it safely. You're just not supposed to trust what happens
if you write via one var and read via the other, e.g. buffer[34] = 'a';
followed by if (hdr->elementN == 'a') where elementN happens to contain
byte 34 of buffer[].
Not so. There may be problems with pointer alignment. It may be that
(on some implementation) structs are only stored at even addresses,
while chars and arrays thereof may be stored at odd addresses. In that
case a pointer to struct is not guarenteed to be able to point at an
array of char. In such a situation, even trying to point a pointer to
struct at an array of char may cause undefined behaviour.
================================================== ==================
6.3.2.3 Pointers
....
7 A pointer to an object or incomplete type may be converted to a
pointer to a different object or incomplete type. If the resulting
pointer is not correctly aligned50) for the pointed-to type, the
behavior is undefined. ...
================================================== ==================
I've seen both approaches used in practice.
2. How should I deal with alignment?
That can be accomplished by allocating N-1 bytes of storage where N is
your alignment requirement. But I don't think this will work for you since
you probably want your struct to start at the beginning of buffer not
offset by some alignment munging.
This approach is also inherently non-portable, since he must know
the alignment characteristics of the implementation he is using, and
modify the code for each new implementation with different alignment
characteristics.
I've read that there's no guarantee that struct members will be
adjacent in memory because of alignment requirements. So in theory, I
shouldn't try to overlay or memcpy() a struct to or from a buffer at
all. However, just about every program I've seen seems to do this with
no ill effects or lack of portability.
If you fill in data via the struct you should be able to copy the struct
to another same type struct w/o issue.
Which doesn't really help him with his problem.
Are there any general guidelines on when I'm likely to run into struct
Yes. The guideline is to always assume that structs are padded, but
you don't know where and how much. (Because, on some implementations,
that will actually be the case.)
Of course, if you don't care about portability you can drop this
assumption and look up your compiler manual. But it sounds like you
(the OP) are concerned about portability. (Very good!) In that case,
assume the worst.
For that reason you should not copy an entire struct to an unsigned
char buffer in one go. Instead, as I said above, you should copy
struct members one by one. Have an extra pointer ready to keep track
of where to copy the next struct member to/from. Use memcpy() to copy
each member (to minimise alignment issues, as discussed above). You
could maybe write functions to copy a struct to and from a buffer.
Eg.:
#include <string.h>
struct foo
{
int a;
double b;
char c[42];
};
void foo2buf(unsigned char *dst, struct foo *src)
{
unsigned char *off;
off = dst;
memcpy(off, src->a, sizeof src->a);
off += sizeof src->a;
memcpy(off, src->b, sizeof src->b);
off += sizeof src->b;
memcpy(off, src->c, sizeof src->c);
}
void buf2foo(struct foo *dst, unsigned char *src)
{
unsigned char *off;
off = src;
memcpy(dst->a, off, sizeof dst->a);
off += sizeof dst->a;
memcpy(dst->b, off, sizeof dst->b);
off += sizeof dst->b;
memcpy(dst->c, off, sizeof dst->c);
}
Even better, make these functions work more portably by copying only
items of a predetermined size and representation (ie., endianness,
representation of negative numbers, etc.). (You may have to do some
manipulating of data, converting C data types to/from some other form,
to achieve this.) This is left as an exercise.
--
Dig the even newer still, yet more improved, sig!
http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?