How come C allow structure members to be addressed like an array ?

P

pereges

#include <stdio.h>

typedef struct
{
double x, y, z;
}vector;

int main(void)
{
int i;
vector v;
double *cord;

v.x = 10;
v.y = 1;
v.z = 2;

cord = &v.x;

for(i = 0; i < 3; i++)
{
printf("%f\n", cord);
}
return 0;
}

here's the output i get:
10.000000
1.000000
2.000000

which is the same as v

i don't how it happens as i was just trying some random ideas but
great stuff really. helped me to reduce some of my code to almost
1/3rd its size.
 
E

EventHelix.com

#include <stdio.h>

typedef struct
{
  double x, y, z;

}vector;

int main(void)
{
  int i;
  vector v;
  double *cord;

  v.x = 10;
  v.y = 1;
  v.z = 2;

 cord = &v.x;

 for(i = 0; i < 3; i++)
 {
  printf("%f\n", cord);
 }
return 0;

}

here's the output i get:
10.000000
1.000000
2.000000

which is the same as v

i don't how it happens as i was just trying some random ideas but
great stuff really. helped me to reduce some of my code to almost
1/3rd its size.


This is not portable but at the low level there is no difference
between a structure containing three doubles and an array of three
doubles in this case.

You cannot assume this, as some implementation might introduce pads
for byte alignment in structures.

The following articles should help:

http://www.eventhelix.com/RealTimeMantra/Basics/CToAssemblyTranslation.htm
http://www.eventhelix.com/RealTimeMantra/Basics/CToAssemblyTranslation2.htm
http://www.eventhelix.com/RealTimeMantra/Basics/CToAssemblyTranslation3.htm
 
T

Thomas Lumley

#include <stdio.h>

typedef struct
{
double x, y, z;

}vector;

The elements of a structure are laid out in memory in the order
they appear in the structure. In principle there could be empty
space between the elements (padding), but it is not likely when
all the elements have the same type -- a contiguous layout must
be possible in that case, and is likely to be efficient, since
it is what an array would use.

Still, the Standard allows padding and does not require
implementations to be efficient, sensible, or cooperative with
the user.
int main(void)
{
int i;
vector v;
double *cord;

v.x = 10;
v.y = 1;
v.z = 2;

cord = &v.x;

This is legitimate, since v.x is a double
for(i = 0; i < 3; i++)
{
printf("%f\n", cord);


This obviously has no chance of working unless all the
structure members are double. Even if they are, there
could (in theory) be padding between them, in which case
cord+1 wouldn't point to v.y. Even if it does, I think
that taking a pointer to v.x, incrementing it so it points
beyond the end of v.x, and then dereferencing it, has
undefined behaviour.

}
return 0;

}


-thomas
 
K

Keith Thompson

pereges said:
#include <stdio.h>

typedef struct
{
double x, y, z;
}vector;

int main(void)
{
int i;
vector v;
double *cord;

v.x = 10;
v.y = 1;
v.z = 2;

cord = &v.x;

for(i = 0; i < 3; i++)
{
printf("%f\n", cord);
}
return 0;
}

here's the output i get:
10.000000
1.000000
2.000000

which is the same as v

i don't how it happens as i was just trying some random ideas but
great stuff really. helped me to reduce some of my code to almost
1/3rd its size.


C doesn't really allow this. More specifically, it lets you get away
with trying it (the compiler isn't required to issue a diagnostic),
but provides absolutely no guarantee that it will work. Compilers are
allowed substantial freedom in how they lay out members of structures.
The first member must be at offset 0, and they must be allocated in
declared order, but the compiler is allowed to insert padding between
members and/or after the last one.

Usually (practically always), such padding is inserted to satisfy
alignment requirements. For a structure containing multiple members
all of the same type, it's very unlikely that any such padding would
be required. In other words, you're not likely to run across an
implementation where your code doesn't happen to work.

Which means that you have a bug that's very difficult to track down
because the symptom rarely if ever appears.

If treating your vector as an array makes your code 1/3 its previous
size, why not just declare it as an array in the first place? It
might be convenient to wrap the array in a structure. For example:

typedef struct {
double v[3];
} vector;

(I'd declare it as "struct vector" and drop the typedef, but that's a
matter of taste.)

If you insist, you could even define macros that let you refer to the
members by their original names:

#define x v[0]
#define y v[1]
#define z v[2]

though this borders on obfuscation.
 
R

Richard Tobin

EventHelix.com said:
This is not portable but at the low level there is no difference
between a structure containing three doubles and an array of three
doubles in this case.

Fields in a struct are aligned in "an implementation-defined manner
appropriate to [their] type". The clear implication is that the
padding is a property of the type (rather than each individual
instance) and so it would be unreasonable for the padding of
successive members of the same type in a struct to be different from
that in an array - which is to say, there should be no padding between
fields of the same type.

Can anyone come up with a reason (other than perverseness) why an
implementation would not do this?

-- Richard
 
P

pereges

one can solve this problem using a vector iterator function like below
but i am not sure if the solution is generic :

#include <stdio.h>

typedef struct
{
double x, y, z;

}vector;

double vector_iterator(vector *a, int i)
{
double ret;
if(i == 0)
ret = a->x;
if(i == 1)
ret = a->y;
if(i == 2)
ret = a->z;
return ret;
}

int main(void)
{
int i;
vector v;
double coord;

v.x = 10;
v.y = 1;
v.z = 2;

for(i = 0; i < 3; i++)
{
coord = vector_iterator(&v, i);
printf("%f\n", coord);
}
return 0;

}
 
T

Tomás Ó hÉilidhe

Others have explained the padding situation, but if you want re-
assurance, you might add a routine to the start of your code that
makes sure it's OK:

void EnsureItWorks(void)
{
Vector const v = { 99, 56, 42 };

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;

puts("ERROR: There's padding in the Vector struct.");

exit(EXIT_FAILURE);
}

int main(void) { EnsureItWorks(); }


(I know that floating-point arithmetic isn't exact but as far as I
know it's exact for integer values... but I'm open to correction!)
 
B

badc0de4

Tomás Ó hÉilidhe said:
void EnsureItWorks(void)
{
Vector const v = { 99, 56, 42 };

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;

*BANG*
(&v.x)[1] or (&v.x)[2] is a trap representation :)
 
I

Ian Collins

Tomás Ó hÉilidhe said:
Others have explained the padding situation, but if you want re-
assurance, you might add a routine to the start of your code that
makes sure it's OK:

void EnsureItWorks(void)
{
Vector const v = { 99, 56, 42 };

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;

puts("ERROR: There's padding in the Vector struct.");

exit(EXIT_FAILURE);
}

int main(void) { EnsureItWorks(); }
A very verbose way of writing

int main()
{
assert( sizeof(vector)==sizeof(double)*3);
}
 
V

viza

Hi

Tomás Ó hÉilidhe wrote:
if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);

The two are not equivalent and neither is perfect. Better is:

assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );

viza
 
I

Ian Collins

viza said:
Hi

Tomás Ó hÉilidhe wrote:
if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);

The two are not equivalent and neither is perfect. Better is:

assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );
Why?

Anyway, if offsetof(vector,z) == sizeof(double)*2, how can
offsetof(vector,y) be anything other than sizeof(double)?
 
B

Ben Bacarisse

Ian Collins said:
Tomás Ó hÉilidhe said:
Others have explained the padding situation, but if you want re-
assurance, you might add a routine to the start of your code that
makes sure it's OK:

void EnsureItWorks(void)
{
Vector const v = { 99, 56, 42 };

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;

(&v.x)[0] is guaranteed to be correct. The other two can be better
tested just by ensuring that

&v.x + 2 == &v.z

The value test could give a false positive if the padding between
elements happened to have the "right" value.
A very verbose way of writing

int main()
{
assert( sizeof(vector)==sizeof(double)*3);
}

Except the structure can have padding at the end and still be suitable.
 
B

Ben Bacarisse

Ian Collins said:
viza said:
Hi

Tomás Ó hÉilidhe wrote:
if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);

The two are not equivalent and neither is perfect. Better is:

assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );
Why?

Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
Anyway, if offsetof(vector,z) == sizeof(double)*2, how can
offsetof(vector,y) be anything other than sizeof(double)?

Agreed. I still prefer my address test, though.

By the way, This is all about making the best of a bad job. To the
OP: if you want to index things, make them an array as Keith Thompson
suggested. In addition to using #define to name the elements one
could also do:

typedef struct {
double v[3];
} vector;

enum { x, y, z };

vector point;

so that one can write point.v[x] and so on (even x[&point] if you
want to make your readers do a double-take!).
 
I

Ian Collins

Ben said:
Ian Collins said:
viza said:
Hi

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);
The two are not equivalent and neither is perfect. Better is:

assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );
Why?

Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
OK, make it

assert( sizeof(vector[2])==sizeof(double)*6 );

Or even a compile time assert:

const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );
Agreed. I still prefer my address test, though.
Fair enough, but the simplified offsetof (or sizeof) test has the
(small) advantage of not requiring an instance of vector to test.
Saying that, the address test could be evaluated at compile time, so the
vector would be eliminated by an optimiser.
 
K

Keith Thompson

Ian Collins said:
Ben said:
Ian Collins said:
viza wrote:
Hi

if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);
The two are not equivalent and neither is perfect. Better is:

assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );

Why?

Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
OK, make it

assert( sizeof(vector[2])==sizeof(double)*6 );

Or even a compile time assert:

const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );

I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".

[snip]
 
V

vippstar

Ian Collins said:
Ben said:
viza wrote:
Hi
if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);
The two are not equivalent and neither is perfect. Better is:
assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );
Why?
Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
OK, make it
assert( sizeof(vector[2])==sizeof(double)*6 );
Or even a compile time assert:
const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );

I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".
vector is not an array, it's a struct.
This:
struct vector { double x, y, z; };
sizeof(struct vector)
Needs not to evaluate to sizeof (double) * 3.
If it were a vector (array), then yes, it is guaranteed and you are
correct.
But vector is a struct.

Thomas: Please change your name to ASCII.
 
I

Ian Collins

Keith said:
Ian Collins said:
Ben Bacarisse wrote:
Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
OK, make it

assert( sizeof(vector[2])==sizeof(double)*6 );

Or even a compile time assert:

const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );

I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".
It would, my last suggestion was pointless. It would still fail if
there were padding at the end of the struct. I would still use the one
above, the chances of padding at the end but not in the middle are slim.
 
K

Keith Thompson

Ian Collins said:
Ben Bacarisse wrote:
Ian Collins <[email protected]> writes:
viza wrote:
Hi
if (99 == (&v.x)[0] &&
56 == (&v.x)[1] &&
42 == (&v.x)[2]) return;
puts("ERROR: There's padding in the Vector struct.");
exit(EXIT_FAILURE);
A very verbose way of writing
assert( sizeof(vector)==sizeof(double)*3);
The two are not equivalent and neither is perfect. Better is:
assert( offsetof(vector,y) == sizeof(double)
&& offsetof(vector,z) == sizeof(double)*2 );

Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.
OK, make it
assert( sizeof(vector[2])==sizeof(double)*6 );
Or even a compile time assert:
const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );

I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".
vector is not an array, it's a struct.
This:
struct vector { double x, y, z; };
sizeof(struct vector)
Needs not to evaluate to sizeof (double) * 3.
If it were a vector (array), then yes, it is guaranteed and you are
correct.
But vector is a struct.

Yes, I understand that. But vector[2] is an array type. My point
was that this:
assert(sizeof(vector[2]) == sizeof(double) * 6);
is equivalent to this:
assert(sizeof(vector) == sizeof(double) * 3);

In both cases, the asserted expression isn't guaranted to be true --
which is the whole point of the assert().
 
K

Keith Thompson

Ian Collins said:
Keith said:
Ian Collins said:
Ben Bacarisse wrote:
Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.

OK, make it

assert( sizeof(vector[2])==sizeof(double)*6 );

Or even a compile time assert:

const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );

I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".
It would, my last suggestion was pointless. It would still fail if
there were padding at the end of the struct. I would still use the one
above, the chances of padding at the end but not in the middle are slim.

I'd say the chances of padding at the end are higher than the chances
of padding in the middle.

Consider
struct foo { char a; char b; char c; };

It's plausible that sizeof(struct foo) == 4, with a padding byte at
the end.

This is less likely to happen for a struct containing doubles, though.
 
I

Ian Collins

Keith said:
Ian Collins said:
Keith said:
Ben Bacarisse wrote:
Tomás's code is over complex (one test is enough) and could fail in
unusual cases (see my other posting) and your test will be thrown if
there is padding at the end of the struct.

OK, make it

assert( sizeof(vector[2])==sizeof(double)*6 );

Or even a compile time assert:

const int n = 1/( sizeof(vector[2])==sizeof(double)*6 );
I think that this:

sizeof(vector) == sizeof(double) * 3

would suffice. An array of two thingies must have twice the size of a
single thingie, for any applicable value of "thingie".
It would, my last suggestion was pointless. It would still fail if
there were padding at the end of the struct. I would still use the one
above, the chances of padding at the end but not in the middle are slim.

I'd say the chances of padding at the end are higher than the chances
of padding in the middle.

Consider
struct foo { char a; char b; char c; };

It's plausible that sizeof(struct foo) == 4, with a padding byte at
the end.

This is less likely to happen for a struct containing doubles, though.
That why I suggested the compile time assert, it's unlikely, but worth
picking up as soon as possible.
 

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,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top