|
Home > Archive > Unix Programming > July 2004 > traumatized by pointer casting
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
traumatized by pointer casting
|
|
| j0mbolar 2004-07-09, 8:50 pm |
| I was reading page 720 of unix network programming,
volume one, second edition. In this udp_write function
he does the following:
void udp_write(char *buf, <everything else omitted)
struct udpiphdr *ui;
struct ip *ip;
ip = (struct ip *) buf;
ui = (struct udpiphdr *) buf;
I couldn't believe what I was seeing at first.
When I checked the headers for their type definitions
one of them contained bit fields in the struct
and the other struct just contained some unsigned shorts.
A pointer to char is not guaranteed to be
properly aligned for a pointer to struct. In fact
I don't see how this could even work period, even on the
most perverse of systems. I'm right, right? This really
shouldn't work?
| |
| Rich Teer 2004-07-09, 8:50 pm |
| On Fri, 9 Jul 2004, j0mbolar wrote:
> A pointer to char is not guaranteed to be
> properly aligned for a pointer to struct. In fact
> I don't see how this could even work period, even on the
> most perverse of systems. I'm right, right? This really
> shouldn't work?
It's not guaranteed by any standard, but in practical terms,
assuming the same programming model, a pointer is a pointer
(at least as far as their size is concerned). That is, one
32-bit or 64-bit address is the same size as any other 32-bit
or 64-bit address.
--
Rich Teer, SCNA, SCSA
President,
Rite Online Inc.
Voice: +1 (250) 979-1638
URL: http://www.rite-online.net
| |
|
| j0mbolar wrote:
> I was reading page 720 of unix network programming,
> volume one, second edition. In this udp_write function
> he does the following:
>
> void udp_write(char *buf, <everything else omitted)
>
> struct udpiphdr *ui;
> struct ip *ip;
>
> ip = (struct ip *) buf;
> ui = (struct udpiphdr *) buf;
>
> I couldn't believe what I was seeing at first.
> When I checked the headers for their type definitions
> one of them contained bit fields in the struct
> and the other struct just contained some unsigned shorts.
>
> A pointer to char is not guaranteed to be
> properly aligned for a pointer to struct. In fact
> I don't see how this could even work period, even on the
> most perverse of systems. I'm right, right? This really
> shouldn't work?
First, you are going from a char array to a pointer to a
structure as found on page 718.
The ip structure is found in netinet/ip.h .
How did you get so far into the book without noticing that
you will typecast to generic pointers?
Here's a basic one from the first few pages in the book:
snipped from unp.h
#define SA struct sockaddr
from page 6:
[snip]
struct sockaddr_in servaddr;
[snip]
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0 )
--
The code you point out does basically the same thing. buf is
just an array instead of a pointer to a structure. So you have a string
of '0', '0', . . . .
And since you are using pointers, these items find their place
in the structure indirectly. You cannot typecast a structure
directly.
buff is defined on page 718 as
char buf[sizeof(struct udpiphdr) + 100];
since buf is an array, you do not have use & to pass it
in a function that asks for pointer parameters.
buf
equals
&buf[0]
and the upd_write function on line 43 is:
upd_write(buf, nbytes);
--
I hope that helps.
Brian
--
* Remove x's to send me mail *
| |
| Frank Cusack 2004-07-09, 8:50 pm |
| On 9 Jul 2004 17:45:54 -0700 j0mbolar@engineer.com (j0mbolar) wrote:
> A pointer to char is not guaranteed to be
> properly aligned for a pointer to struct. In fact
> I don't see how this could even work period, even on the
> most perverse of systems. I'm right, right? This really
> shouldn't work?
Right! How can this work?
For the folks who didn't get it:
char buf[100];
struct foo {
long a;
long b;
char c;
int d;
};
struct foo *foo_p = (struct foo *) buf;
has the problem that buf has byte-alignment, whereas struct foo has
long alignment.
foo_p->a is not guaranteed to be referenceable. On some arches it would
cause an unaligned read (slow), on some it just wouldn't work at all.
/fc
| |
| Keith Thompson 2004-07-09, 8:50 pm |
| Rich Teer <rich.teer@rite-group.com> writes:
> On Fri, 9 Jul 2004, j0mbolar wrote:
>
> It's not guaranteed by any standard, but in practical terms,
> assuming the same programming model, a pointer is a pointer
> (at least as far as their size is concerned). That is, one
> 32-bit or 64-bit address is the same size as any other 32-bit
> or 64-bit address.
No, not really. It's common for all pointer types to have the same
size and representation, but there are alignment requirements.
For example:
char buf[100];
char *ptr = buf + 1;
*((int*)ptr) = 42;
is likely to cause a trap on many systems.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
| |
| Keith Thompson 2004-07-09, 8:50 pm |
| Frank Cusack <fcusack@fcusack.com> writes:
> On 9 Jul 2004 17:45:54 -0700 j0mbolar@engineer.com (j0mbolar) wrote:
>
> Right! How can this work?
>
> For the folks who didn't get it:
>
> char buf[100];
> struct foo {
> long a;
> long b;
> char c;
> int d;
> };
> struct foo *foo_p = (struct foo *) buf;
>
> has the problem that buf has byte-alignment, whereas struct foo has
> long alignment.
>
> foo_p->a is not guaranteed to be referenceable. On some arches it would
> cause an unaligned read (slow), on some it just wouldn't work at all.
It's certainly true that foo_p isn't guaranteed to be aligned to any
boundary bigger than a byte. On the other hand, a declared array
object is likely to be word-aligned. (If you count on this, of
course, your program will fail at the most inconvenient possible
moment.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
| |
| David Schwartz 2004-07-10, 5:54 pm |
|
"j0mbolar" <j0mbolar@engineer.com> wrote in message
news:2d31a9f9.0407091645.5ad284d7@posting.google.com...
>I was reading page 720 of unix network programming,
> volume one, second edition. In this udp_write function
> he does the following:
>
> void udp_write(char *buf, <everything else omitted)
>
> struct udpiphdr *ui;
> struct ip *ip;
>
> ip = (struct ip *) buf;
> ui = (struct udpiphdr *) buf;
>
> I couldn't believe what I was seeing at first.
> When I checked the headers for their type definitions
> one of them contained bit fields in the struct
> and the other struct just contained some unsigned shorts.
Right, that's because a UDP packet is guaranteed to have a specific
layout at the bit/byte level.
> A pointer to char is not guaranteed to be
> properly aligned for a pointer to struct.
Nevertheless, there is in fact a UDP packet beginning at that address,
and it does in fact have the layout. This is not just any pointer to char,
it's a pointer that he probably got back from a function like 'malloc',
which means it's guaranteed to be adequately aligned to point to any type.
> In fact
> I don't see how this could even work period, even on the
> most perverse of systems. I'm right, right? This really
> shouldn't work?
It will work on most systems. However, it could break on systems where
the buffer is not sufficiently aligned. I'm betting his code made sure the
buffer is sufficiently aligned.
DS
| |
|
| On 9 Jul 2004 17:45:54 -0700, j0mbolar@engineer.com (j0mbolar) wrote:
>I was reading page 720 of unix network programming,
>volume one, second edition. In this udp_write function
>he does the following:
>
>void udp_write(char *buf, <everything else omitted)
>
>struct udpiphdr *ui;
>struct ip *ip;
>
>ip = (struct ip *) buf;
>ui = (struct udpiphdr *) buf;
>
>I couldn't believe what I was seeing at first.
>When I checked the headers for their type definitions
>one of them contained bit fields in the struct
>and the other struct just contained some unsigned shorts.
>
>A pointer to char is not guaranteed to be
>properly aligned for a pointer to struct. In fact
>I don't see how this could even work period, even on the
>most perverse of systems. I'm right, right? This really
>shouldn't work?
It will work on any platform which doesn't enforce alighment
requirements, and on any platform at all provided the pointer passed
is suitably aligned, as I imagine the book's code will do.
If you really want to ensure it's always aligned, add a wrapper
something like this:
void a_udp_write(char *buf,size_t size,...)
{
char *abuf,*tbuf=buf;
int a=0;
if ((buf&15)!=0)
{
a++;
abuf=(char*)malloc(size+16);
tbuf=abuf & ~15;
memcpy(tbuf,buf,size);
}
udp_write(tbuf,size,...);
if (a!=0)
free(abuf);
}
Then you can be certain udp_write() is always working with an aligned
buffer (on a 16 byte boundary here), even if the caller didn't.
James.
| |
| Default User 2004-07-10, 5:54 pm |
| j0mbolar wrote:
> A pointer to char is not guaranteed to be
> properly aligned for a pointer to struct. In fact
> I don't see how this could even work period, even on the
> most perverse of systems. I'm right, right? This really
> shouldn't work?
Yes but, we're talking platform-specific stuff here. This sort of thing
is so common in UNIX socket programs, that there just aren't likely to
be implementations where it doesn't work. It would destroy too much
legacy code.
The moral, don't look to books like that for ISO standard C. Look to it
as one of the finest tutorials in network programming around. Trust that
the code he has in there has been used and abused for many a year.
Brian Rodenborn
| |
| Dan Pop 2004-07-12, 5:57 pm |
| In <2d31a9f9.0407091645.5ad284d7@posting.google.com> j0mbolar@engineer.com (j0mbolar) writes:
>I was reading page 720 of unix network programming,
>volume one, second edition. In this udp_write function
>he does the following:
>
>void udp_write(char *buf, <everything else omitted)
>
>struct udpiphdr *ui;
>struct ip *ip;
>
>ip = (struct ip *) buf;
>ui = (struct udpiphdr *) buf;
>
>I couldn't believe what I was seeing at first.
>When I checked the headers for their type definitions
>one of them contained bit fields in the struct
>and the other struct just contained some unsigned shorts.
>
>A pointer to char is not guaranteed to be
>properly aligned for a pointer to struct. In fact
>I don't see how this could even work period, even on the
>most perverse of systems. I'm right, right? This really
>shouldn't work?
It is the caller's job to ensure that the first argument of udp_write()
is properly aligned for these struct's. If dynamical allocation is used,
this condition is trivially satisfied.
Dan
--
Dan Pop
DESY Zeuthen, RZ group
Email: Dan.Pop@ifh.de
| |
| Dave Thompson 2004-07-19, 2:51 am |
| On Sat, 10 Jul 2004 16:49:55 +0100, James <jas@spamcop.net> wrote:
> On 9 Jul 2004 17:45:54 -0700, j0mbolar@engineer.com (j0mbolar) wrote:
> [Stevens v1 2e p720]
[vbcol=seagreen]
> It will work on any platform which doesn't enforce alighment
> requirements, and on any platform at all provided the pointer passed
> is suitably aligned, as I imagine the book's code will do.
>
> If you really want to ensure it's always aligned, add a wrapper
> something like this:
>
> void a_udp_write(char *buf,size_t size,...)
The parameter could better be const char *, but back in the '80s that
wasn't reliably portable, and before that didn't exist at all.
> {
> char *abuf,*tbuf=buf;
> int a=0;
>
> if ((buf&15)!=0)
That requires an explicit conversion (cast) -- and then formally as
far as C is concerned the result is implementation-defined, although
it should be correct on any Unix and probably any platform where
BSD-ish sockets make sense at all.
> {
> a++;
> abuf=(char*)malloc(size+16);
Cast not needed in C if malloc is correctly declared, as it must be,
preferably by #include'ing <stdlib.h>; (clc) FAQ 7.6. Should check the
return from malloc() etc. for nonnull before using.
> tbuf=abuf & ~15;
Not needed; the value returned by {m,c,re}alloc, if successful, must
be sufficiently aligned for any object type (including struct of
anything) on the implementation. And if it was needed it would be
backwards; you would need to round *up*, into the space allocated.
In general there is no way to automatically determine what the
strictest alignment is, although you can get an upper bound for any
predetermined (finite) set of types by something like
offsetof ( struct { char a; union { long l; double d; } u } , u ).
For IPv4+UDP in particular, we know that no header field requires more
than 4-byte alignment.
> memcpy(tbuf,buf,size);
> }
> udp_write(tbuf,size,...);
> if (a!=0)
> free(abuf);
Instead of a separate flag variable a, why not just initialize abuf to
NULL, and then unconditionally free() it -- if (still) null it does
nothing, and if non-null it frees the malloc'ed space.
Or, since UDP is lossy anyway, could just do
if( (ulong)buf & 15 ) return;
or to be "nice" about it
... { errno = EPULLTHEOTHERONE; return; }
:-)
> }
>
> Then you can be certain udp_write() is always working with an aligned
> buffer (on a 16 byte boundary here), even if the caller didn't.
>
>
> James.
- David.Thompson1 at worldnet.att.net
|
|
|
|
|