Typecasting Pointers

B

brian

Quick question:

if I have a structure:

struct foo {

unsigned char *packet;
unsigned char *ip_src;
};

and another structure:

struct foo2 {

unsigned char *packet;
};

and I do:

struct foo *a;
struct foo2 *b;

a = malloc(sizeof(struct foo));
if (a == NULL)
return NULL;

(void)memset(a, 1, sizeof(struct foo));

b = (struct foo2 *)a;

--

Would foo2 just strip off the *packet?

And what happens to *ip?

I am trying to understand how C handles the typecast if
one structure pointed to has a different number of members
than the other. I am trying to understand how C99 treats
this situation. Both of my pointers should be correctly
aligned.

Thanks,

Brian
 
B

brian

Jack Klein wrote:
[snip]
You put "-- ", which is a usenet and email signature delimiter, in the
middle of your post. My newsreader quite properly snipped it, and
everything that followed it. And like an awful lot of people these
days, you did not put a signature delimiter before your actual
signature. Sigh.

In any case, about the only guarantee that you have is based on the
fact that a pointer to a structure, when suitably cast, points to the
structure's first member. That means that on most platforms, you can
cast a pointer to either type of structure to a pointer to pointer to
char and use it to access the first member of each structure, which is
a pointer to char.

Anything beyond that is undefined behavior. Here is the relevant
paragraph from C99:

========
6.2.7 Compatible type and composite type
1 Two types have compatible type if their types are the same.
Additional rules for determining whether two types are compatible are
described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers,
and in 6.7.5 for declarators.46) Moreover, two structure, union, or
enumerated types declared in separate translation units are compatible
if their tags and members satisfy the following requirements: If one
is declared with a tag, the other shall be declared with the same tag.
If both are complete types, then the following additional requirements
apply: there shall be a one-to-one correspondence between their
members such that each pair of corresponding members are declared with
compatible types, and such that if one member of a corresponding pair
is declared with a name, the other member is declared with the same
name. For two structures, corresponding members shall be declared in
the same order. For two structures or unions, corresponding bit-fields
shall have the same widths. For two enumerations, corresponding
members shall have the same values.
========

Thanks for your help. I will post in this format:

[quoted post]
[my post]
[dashes]
[sig]

So as long as both pointers start on the same alignment boundary, I
should be fine with the cast. I will just be sure to include cast
alignment (-Wcast-align) warnings when I compile.
 
J

Jack Klein

Quick question:

if I have a structure:

struct foo {

unsigned char *packet;
unsigned char *ip_src;
};

and another structure:

struct foo2 {

unsigned char *packet;
};

and I do:

struct foo *a;
struct foo2 *b;

a = malloc(sizeof(struct foo));
if (a == NULL)
return NULL;

(void)memset(a, 1, sizeof(struct foo));

b = (struct foo2 *)a;

You put "-- ", which is a usenet and email signature delimiter, in the
middle of your post. My newsreader quite properly snipped it, and
everything that followed it. And like an awful lot of people these
days, you did not put a signature delimiter before your actual
signature. Sigh.

In any case, about the only guarantee that you have is based on the
fact that a pointer to a structure, when suitably cast, points to the
structure's first member. That means that on most platforms, you can
cast a pointer to either type of structure to a pointer to pointer to
char and use it to access the first member of each structure, which is
a pointer to char.

Anything beyond that is undefined behavior. Here is the relevant
paragraph from C99:

========
6.2.7 Compatible type and composite type
1 Two types have compatible type if their types are the same.
Additional rules for determining whether two types are compatible are
described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers,
and in 6.7.5 for declarators.46) Moreover, two structure, union, or
enumerated types declared in separate translation units are compatible
if their tags and members satisfy the following requirements: If one
is declared with a tag, the other shall be declared with the same tag.
If both are complete types, then the following additional requirements
apply: there shall be a one-to-one correspondence between their
members such that each pair of corresponding members are declared with
compatible types, and such that if one member of a corresponding pair
is declared with a name, the other member is declared with the same
name. For two structures, corresponding members shall be declared in
the same order. For two structures or unions, corresponding bit-fields
shall have the same widths. For two enumerations, corresponding
members shall have the same values.
========
 
R

Richard Bos

[ Snip ]
You put "-- ", which is a usenet and email signature delimiter, in the
middle of your post.

No, he didn't; that line did not contain a space.
My newsreader quite properly snipped it, and everything that followed it.

Get a better newsreader. Forte Free Agent didn't snip a thing.

(Yes, I know.)
And like an awful lot of people these days, you did not put a
signature delimiter before your actual signature. Sigh.

YMMV, but I do not consider a single name to be a signature...

....and yours is far from McQ. I wouldn't have complained about that,
but...

Richard
 
B

brian

Keith Thompson wrote:
[snip]
I see that you're using Mozilla Thunderbird 0.5 (consider upgrading to
0.9). If you read its documentation, it should tell you how to set up
a signature file (probably called ".signature" that will automatically
be included, with the proper delimiter, at the bottom of each post.

I am using the latest available openBSD port of Thunderbird. I know
there is a later version, but I do not have the time to security audit
the latest version, nor do I know the C standard well enough to do a
good job. I am okay with using out of date software that has
been audited.

Also, I really have no need for a signature. My name should be enough.
It doesn't take that long to type five characters.

If you have suggestions for other e-mail/newsreaders, I will check the
port list and try using one of those. openBSD doesn't have the most
extensive collection of ports or most up to date, but the ports work.

Brian

ps Am I good concerning the typecast? I was a little unsure of the
standard and wanted to verify my understanding.
 
K

Keith Thompson

brian said:
Thanks for your help. I will post in this format:

[quoted post]
[my post]
[dashes]
[sig]

So as long as both pointers start on the same alignment boundary, I
should be fine with the cast. I will just be sure to include cast
alignment (-Wcast-align) warnings when I compile.

------

Thanks,

Brian

The standard signature delimiter is a line consisting of two hyphens
and a single trailing space. A line of six hyphens is just a line of
six hyphens.

I see that you're using Mozilla Thunderbird 0.5 (consider upgrading to
0.9). If you read its documentation, it should tell you how to set up
a signature file (probably called ".signature" that will automatically
be included, with the proper delimiter, at the bottom of each post.

And incidentally, it's seldom necessary to quote the entire post to
which you're responding. Trim out anything that's not relevant to
your followup. See most of the articles in this newsgroup for good
examples.
 
B

brian

This should be to 0 or NULL. My mistake.

[snip]
This makes no sense. The only thing being typecast is a pointer to
struct and not a struct itself. A pointer doesn't care about the size
of or quantity of members in the struct it points to.

I understand that I am just typecasting pointers. I was just trying to
confirm that the following holds true:

[struct foo * ][member 1][member 2]

[struct foo2 *][member 1]

that the memory addresses will overlap, so I can use a struct foo2
pointer to access the values in struct foo member 1 after I have
assigned values to whatever is pointed to. That the address of the
first member in foo will be the same address in foo2 by virtue of
malloc(). I'm sorry for not being more clear.

[snip]
> The assignment would still be legal since the standard
requires all pointers to struct to have the same alignment and
representation but you cannot dereference b portably.

And I cannot dereference portably because the alignment restrictions
could be different?

Thanks,

brian
 
B

Barry Schwarz

Quick question:

if I have a structure:

struct foo {

unsigned char *packet;
unsigned char *ip_src;
};

and another structure:

struct foo2 {

unsigned char *packet;
};

and I do:

struct foo *a;
struct foo2 *b;

a = malloc(sizeof(struct foo));
if (a == NULL)
return NULL;

So a now points to a dynamically allocated struct foo.
(void)memset(a, 1, sizeof(struct foo));

This sets every byte of that struct to the value 1. Since the struct
consists of nothing but pointers, odds are that none of the members of
the struct contain a useful (or even valid) value.
b = (struct foo2 *)a;

The address returned by malloc is guaranteed to be properly aligned
for any object so b is valid and you can use it to treat the area as a
struct foo2.
Would foo2 just strip off the *packet?

foo2 is the tag for a struct. It doesn't "do" anything. The address
that b points is the start of a struct foo and is guaranteed to be the
address of the member packet in that struct. It also happens to be
the start of a mythical struct foo2 and is therefore also the address
of the member packet in that struct.
And what happens to *ip?

It is still there in the allocated area. Assigning the value in a to
the pointer b did nothing to the data in the area a points to.
I am trying to understand how C handles the typecast if
one structure pointed to has a different number of members
than the other. I am trying to understand how C99 treats

This makes no sense. The only thing being typecast is a pointer to
struct and not a struct itself. A pointer doesn't care about the size
of or quantity of members in the struct it points to.
this situation. Both of my pointers should be correctly
aligned.

The pointers are correctly aligned by virtue of malloc. If you had
coded
struct foo x;
a = &x;
b = (struct foo2*)a;
there would be no guarantee that b would be correctly aligned for a
struct foo2. The assignment would still be legal since the standard
requires all pointers to struct to have the same alignment and
representation but you cannot dereference b portably.



<<Remove the del for email>>
 
B

brian

Barry Schwarz wrote:

[snip]
Not really. NULL would not portable because some systems define NULL
to be (void*)0. 0 is not portable either since their is no guarantee
that a pointer value of all bits 0 has any meaning.

Then, what is the best way to initialize the structure? Should I even
bother? Now, I'm really stuck on this piece.

[snip]
There is only one address, the one returned by malloc. The fact that
you have stored this address in two different pointers doesn't change
that.

Awesome. I just wanted to confirm this to be true with that cast. I
wasn't sure after reading the standard.

[snip]

That stinks. I designed my program to have a structure in my header
file, and I used functions prototypes like

int somefunc(struct foo **);

to pass the structure around the program after allocating space for it.
Is there a portable way to pass variable values around a program?

Brian
 
F

Flash Gordon

This should be to 0 or NULL. My mistake.

<snip>

That doesn't help much. Since the second argument is an int and NULL
could be ((void *)0) a call of
(void)memset(a, NULL, sizeof(struct foo));
could require a diagnostic.

Even a 0 does not help much as this would set it to all bits 0 and all
bits 0 might not be the correct value.
 
B

Barry Schwarz

This should be to 0 or NULL. My mistake.

Not really. NULL would not portable because some systems define NULL
to be (void*)0. 0 is not portable either since their is no guarantee
that a pointer value of all bits 0 has any meaning.
[snip]
This makes no sense. The only thing being typecast is a pointer to
struct and not a struct itself. A pointer doesn't care about the size
of or quantity of members in the struct it points to.

I understand that I am just typecasting pointers. I was just trying to
confirm that the following holds true:

[struct foo * ][member 1][member 2]

[struct foo2 *][member 1]

that the memory addresses will overlap, so I can use a struct foo2
pointer to access the values in struct foo member 1 after I have
assigned values to whatever is pointed to. That the address of the
first member in foo will be the same address in foo2 by virtue of
malloc(). I'm sorry for not being more clear.

There is only one address, the one returned by malloc. The fact that
you have stored this address in two different pointers doesn't change
that.
[snip]
The assignment would still be legal since the standard
requires all pointers to struct to have the same alignment and
representation but you cannot dereference b portably.

And I cannot dereference portably because the alignment restrictions
could be different?

Yup.



<<Remove the del for email>>
 
B

Barry Schwarz

Barry Schwarz wrote:

[snip]
Not really. NULL would not portable because some systems define NULL
to be (void*)0. 0 is not portable either since their is no guarantee
that a pointer value of all bits 0 has any meaning.

Then, what is the best way to initialize the structure? Should I even
bother? Now, I'm really stuck on this piece.

If you define the structure, you can initialize it as part of the
definition:
struct foo a = {0};
This will cause each member of the struct to be set to the appropriate
zero value ('\0' for char, 0 for integers, 0.0 floating points, and
NULL for pointers.

If you allocate the struct, as was done up-thread, then you don't have
much choice but to do it member by member. However, if you have a
defined struct that was initialized, you can initialize the allocated
one by a simple assignment:
struct foo *b = malloc(sizeof *b);
b = a;
[snip]
There is only one address, the one returned by malloc. The fact that
you have stored this address in two different pointers doesn't change
that.

Awesome. I just wanted to confirm this to be true with that cast. I
wasn't sure after reading the standard.

[snip]

That stinks. I designed my program to have a structure in my header
file, and I used functions prototypes like

A header file should have only the struct declaration, not any
definition.
int somefunc(struct foo **);

to pass the structure around the program after allocating space for it.

Why would you use a pointer to pointer to struct when a simple pointer
to struct will let the function update the members at will? The
struct ** makes sense if the function will allocate the struct so it
can update the pointer directly but your comment says the struct is
already allocated.
Is there a portable way to pass variable values around a program?

Function arguments seem to work for most cases.

WHY do you want the address of a struct foo to be treated as the
address of a struct foo2? You might want to consider embedding both
structs in a union.


<<Remove the del for email>>
 
B

Barry Schwarz

Barry Schwarz wrote:

[snip]
(void)memset(a, 1, sizeof(struct foo));

This should be to 0 or NULL. My mistake.


Not really. NULL would not portable because some systems define NULL
to be (void*)0. 0 is not portable either since their is no guarantee
that a pointer value of all bits 0 has any meaning.

Then, what is the best way to initialize the structure? Should I even
bother? Now, I'm really stuck on this piece.

If you define the structure, you can initialize it as part of the
definition:
struct foo a = {0};
This will cause each member of the struct to be set to the appropriate
zero value ('\0' for char, 0 for integers, 0.0 floating points, and
NULL for pointers.

If you allocate the struct, as was done up-thread, then you don't have
much choice but to do it member by member. However, if you have a
defined struct that was initialized, you can initialize the allocated
one by a simple assignment:
struct foo *b = malloc(sizeof *b);
b = a;

That should be *b = a;


<<Remove the del for email>>
 
M

Michael Wojcik

Barry said:
[brian wrote:]
(void)memset(a, 1, sizeof(struct foo));

This should be to 0 or NULL. My mistake.


Not really. NULL would not portable because some systems define NULL
to be (void*)0. 0 is not portable either since their is no guarantee
that a pointer value of all bits 0 has any meaning.

Then, what is the best way to initialize the structure? Should I even
bother? Now, I'm really stuck on this piece.

There are various alternatives, but I prefer structure assignment with
a static const initializer:

#include <stdlib.h>

static struct foo *alloc_foo(void)
{
static const struct foo foo0 = {0};
struct foo *newfoo = malloc(sizeof *newfoo);

if (newfoo)
*newfoo = foo0;

return newfoo;
}

The {0} is actually unnecessary, since static variables without an
explicit initializer are implicitly initialized to {0}, but I prefer
to make it explicit.

If multiple functions in a file need the initializer, put it at file
scope. If functions in multiple files need the initializer, you
could make it non-static, with an extern declaration in the header
that defines struct foo, but that raises issues of constraints on
and collisions of externally-visible names, so in some cases it may
be better simply to define one initializer per file (or refactor so
that only one file ever initializes dynamically-allocated variables
of that type - often the best choice).

By the way, returning to the question of Usenet signatures: your
messages should include a proper signature delimiter (a line
containing only two hyphens and a space) before your signature,
even if it only includes your name.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top