Struct casting

  • Thread starter Edward Rutherford
  • Start date
E

Edward Rutherford

This was pulled up at a code review. Code was similar to this:

struct foo {
int bar;
int baz;
} f;

int * i = (int *) f;

The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice
--- maybe on theoretical perverse implementations like the ones you guys
like to play with :)

Who is right here by the letter of the law?

Cheers
Edward
 
H

Harald van Dijk

This was pulled up at a code review. Code was similar to this:

struct foo {
  int bar;
  int baz;

} f;

I'm assuming you meant } *f;
int * i = (int *) f;

because otherwise this should be a hard error.
The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice

A pointer to a struct can be safely converted to a pointer to its
first member, and back. Padding before the first member is not
allowed.

That said, for various other reasons, relating to readability and
maintainability, it may well be better to just write

int *i = &f->bar;
 
T

Tobias Blass

This was pulled up at a code review. Code was similar to this:

struct foo {
int bar;
int baz;
} f;

int * i = (int *) f;

The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice
--- maybe on theoretical perverse implementations like the ones you guys
like to play with :)

Who is right here by the letter of the law?
The standard guarantees that there is no padding before the first element, so
this conversion is absolutely valid. It may cause problems when another variable
is placed before the 'bar',though, and I wouldn't bet that the person that adds
the new member will change all these assignments.
 
T

tom st denis

This was pulled up at a code review. Code was similar to this:

struct foo {
  int bar;
  int baz;

} f;

int * i = (int *) f;

The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice
--- maybe on theoretical perverse implementations like the ones you guys
like to play with :)

Who is right here by the letter of the law?

Is there a question of alignment though?

Generally, that sort of code would fail review with me and my cohorts
[er...coworkers]. If you wanted a specific element of a struct you
should pick it by name.

Tom
 
I

Ian Collins

The standard guarantees that there is no padding before the first element, so
this conversion is absolutely valid.

Not as written. There's a * in the declaration f or an & in the
initialisation of i missing.
It may cause problems when another variable
is placed before the 'bar',though, and I wouldn't bet that the person that adds
the new member will change all these assignments.

As mentioned else-thread,

int *i = &f->bar;

would be better.
 
I

Ian Collins

FWIW, C++ compilers frequently put the vtable pointer at the beginning
of classes/structs that contain virtual functions (IOW, the "padding"
at the beginning of the struct would be the vtable pointer)..

In C, while two structures need to have the same layout in a common
leading area, I'm not sure there's a guarantee that the pointer to the
beginning of the beginning of the structure is required to be a
pointer to the first member.

There is.
 
J

James Kuyper

Is there a question of alignment though?

No, there is not. The standard guarantees that conversion of a pointer
to a struct to a pointer to the type of the first member produces a
valid pointer to that first member.
Generally, that sort of code would fail review with me and my cohorts
[er...coworkers]. If you wanted a specific element of a struct you
should pick it by name.

Agreed. In addition to protecting against the possibility that bar might
be moved to a different position, it's also more type-safe, since a type
cast is no longer necessary.
 
J

James Kuyper

On 12/05/2011 05:10 PM, Robert Wessel wrote:
....
In C, while two structures need to have the same layout in a common
leading area, I'm not sure there's a guarantee that the pointer to the
beginning of the beginning of the structure is required to be a
pointer to the first member.

There is: 6.7.2.1p13.
 
K

Keith Thompson

Edward Rutherford said:
This was pulled up at a code review. Code was similar to this:

struct foo {
int bar;
int baz;
} f;

int * i = (int *) f;

That should be

int *i = (int*)&f;
The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice
--- maybe on theoretical perverse implementations like the ones you guys
like to play with :)

There cannot be any padding before the first member. The code is safe,
but poor style. Presumably the member "bar" was given a name for a
reason; just use that name:

int *i = &f.bar;

Unless, for some odd reason, you specifically want the address of
the first member of the struct, and you're sure it's an int without
being sure of its name.
 
B

Ben Pfaff

Edward Rutherford said:
Who is right here by the letter of the law?

Others already gave the answer. Here's the quote from the
standard:

A pointer to a structure object, suitably converted, points
to its initial member (or if that member is a bit-field,
then to the unit in which it resides), and vice versa.
 
J

jacob navia

Le 05/12/11 22:52, Edward Rutherford a écrit :
This was pulled up at a code review. Code was similar to this:

struct foo {
int bar;
int baz;
} f;

int * i = (int *) f;

The only way this could go wrong is if the struct contains padding before
its first member, but I don't think this would ever happen in practice
--- maybe on theoretical perverse implementations like the ones you guys
like to play with :)

Who is right here by the letter of the law?

Cheers
Edward

This is a mistake. Instead of writing

int *i = &f.bar; // 16 characters

you write

int * i = (int *) f; // 20 characters

AND (most important):

Now, the "bar" member MUST be the first member of the
structure! And, as Murphy's Law states, sombody will
add a member to the struct foo in the new version,
2 years from here.

WHO will remember that in another file there was an
implicit dependency as to "bar" being the first member?

This code is unmaintainable, it provokes a bug if
ANY modification is done to "bar" within the structure.

Suppose that "bar" member is eliminated. Instead of
provokinbg a compiler error and forcing you to change
the dependent code, the code will silently compile
and provoke a VERY hard debugging session.

I see no justification whatsoever for writing code
like that.

jacob

P.S. Anyway your code doesn't compile, as others have said.
 
M

Malcolm McLean

int * i = (int *) f; // 20 characters

I see no justification whatsoever for writing code
like that.
As the snippet goes, no.

But "bar" might in fact be type a identifier field

foodoo(struct foo *f)
{
switch( *( int *) f)
{
case REALLYABAR:
dobar(f); return;
case REALLYAFOO:
dofoo(f); return;
}
}

This is arguably better than switch(foo->bar) because it makes clear
that code will onyl work if the identifier is the first member.
 
I

Ian Collins

As the snippet goes, no.

But "bar" might in fact be type a identifier field

foodoo(struct foo *f)
{
switch( *( int *) f)
{
case REALLYABAR:
dobar(f); return;
case REALLYAFOO:
dofoo(f); return;
}
}

This is arguably better than switch(foo->bar) because it makes clear
that code will onyl work if the identifier is the first member.

Better still is to give the first member a name that identifies is as
the type identifier field. The X XEvent union is a good example of this.
 
L

Lauri Alanko

There cannot be any padding before the first member. The code is safe,
but poor style. Presumably the member "bar" was given a name for a
reason; just use that name:

int *i = &f.bar;

Unless, for some odd reason, you specifically want the address of
the first member of the struct, and you're sure it's an int without
being sure of its name.

It's a common idiom to simulate poor man's subtyping for structs by
placing the supertype struct as the first member of the subtype
struct, and then casting between struct pointer types. It may even be
that the members of the structs are hidden from client code (or at
least considered private), so the public interface might only say that
a subtype struct pointer can be cast to the supertype pointer (and
back again) without saying anything about the names of the members.

Then again, the direct address-of-member approach, along with a
containerof macro, gives you multiple inheritance, which might be
useful.


Lauri
 
J

Joe keane

Who is right here by the letter of the law?

Letter of law, 'good idea' is often different.

It's right, but when someone comes along and mofidies your code,
is it likely to do what they expect?

Of course you put a comment that that member needs to be first, right?
 

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,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top