sizeof struct returning unexpected results

S

Sean

I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.

Here is a test struct I wrote:

struct Tester {
unsigned short first;
unsigned int second;
};

Checking the sizeof variables declared to be of this type, I get 64
bits, when really the size of a short is 16 bits, and the size of an
int is 32 bits. Declaring the struct to have two shorts produces
expected results, and declaring it to have one short also produces
expected results.

I do understand what is going on here. The size is being adjusted
or "aligned" (I don't want to misuse that term so correct me if I used
it wrong) to the size of the largest constituent element. However, I
would like to prevent this as it does have the end result of munging
communication as it relates to this protocol.

I originally discovered this in D, where I had a struct which
contained shorts, ints, and a long. I was able to reproduce it in C,
so I figure it's a more basic issue with a generic response
(hopefully).

What can I do to have a struct, declared as above, either return a
size of 6, or be manipulated so as to present itself as a string of 48
bits?

Thanks everyone, for your time.
 
M

Mr Wibble

I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.

Here is a test struct I wrote:

struct Tester {
unsigned short first;
unsigned int second;
};

Checking the sizeof variables declared to be of this type, I get 64
bits, when really the size of a short is 16 bits, and the size of an
int is 32 bits. Declaring the struct to have two shorts produces
expected results, and declaring it to have one short also produces
expected results.

I do understand what is going on here. The size is being adjusted
or "aligned" (I don't want to misuse that term so correct me if I used
it wrong) to the size of the largest constituent element. However, I
would like to prevent this as it does have the end result of munging
communication as it relates to this protocol.

I originally discovered this in D, where I had a struct which
contained shorts, ints, and a long. I was able to reproduce it in C,
so I figure it's a more basic issue with a generic response
(hopefully).

What can I do to have a struct, declared as above, either return a
size of 6, or be manipulated so as to present itself as a string of 48
bits?

Thanks everyone, for your time.

Depending upon your compiler you should investigate a method to allow
the structure to be packed.

With Gcc you may use __attribute__ ((packed)) for example.

Eg.

struct Tester {
unsigned short first;
unsigned int second;
} __attribute__ ((packed));

l8r
 
T

Tom St Denis

Sean said:
Here is a test struct I wrote:

struct Tester {
unsigned short first;
unsigned int second;
};

Checking the sizeof variables declared to be of this type, I get 64
bits, when really the size of a short is 16 bits, and the size of an
int is 32 bits. Declaring the struct to have two shorts produces
expected results, and declaring it to have one short also produces
expected results.

Your compiler is padding the structure so that's its a multiple of 8 bytes.
The compiler is free to do this and usually it's to make optimizations
possible [and loads/stores quicker].

If you want exact sizes you'll have to use compiler specific ``attributes''
which are off topic here.

Tom
 
L

Lew Pitcher

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.

Here is a test struct I wrote:

struct Tester {
unsigned short first;
unsigned int second;
};

Checking the sizeof variables declared to be of this type, I get 64
bits,

How do you determine this? sizeof measures in char units, not bit units.

when really the size of a short is 16 bits,

You know this because...?
and the size of an
int is 32 bits.

You know this because...?
Declaring the struct to have two shorts produces
expected results, and declaring it to have one short also produces
expected results.

I do understand what is going on here. The size is being adjusted
or "aligned" (I don't want to misuse that term so correct me if I used
it wrong) to the size of the largest constituent element.

No, not really.

The elements of your struct are individually aligned on boundaries that
your compiler has determined they must be aligned on. A hypothetical
example would be that
sizeof (short) is 2,
short elements must be aligned on a 2 char boundary,
sizeof (int) is 4, and
int elements must be aligned on a 4 char boundary,
meaning that

struct {
short n1;
int n2;
};

resulting in placement of n1 taking up 2 chars, and n2 not being
able to start at the next available char because it needs to be on
a 4 char boundary. The compiler would 'inject' some padding between
the short and the int so as to make the int fall on a 4 char boundary.

However,

struct {
int n2;
short n1;
};

would result in the placement of n2 taking up 4 chars, and n1 being
able to start on a 2 char boundary. In this case the compiler would
not have to inject padding.
However, I
would like to prevent this as it does have the end result of munging
communication as it relates to this protocol.

This is a compiler-dependant issue. You have to go back to your
compiler's documentation and find a way to tell it to use different
alignment rules. In some compilers, this can be a #pragma preprocessor
directive, while others will use a commandline option.

This is not a C issue.

[snip]

- --

Lew Pitcher, IT Consultant, Enterprise Application Architecture
Enterprise Technology Solutions, TD Bank Financial Group

(Opinions expressed here are my own, not my employer's)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (MingW32)

iD8DBQFBJi4fagVFX4UWr64RAnQsAKCJpqzpHOC4CIW6L9Vq5MS8GmQlkgCeIA48
PsiyPVMAB/jXI2XZABRzS4E=
=38vT
-----END PGP SIGNATURE-----
 
E

Eric Sosman

Sean said:
I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.

This is Question 2.12 in the comp.lang.c Frequently
Asked Questions (FAQ) list

http://www.eskimo.com/~scs/C-faq/top.html

.... and you're not going to like the answer.
 
J

Jens.Toerring

Sean said:
I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.
Here is a test struct I wrote:
struct Tester {
unsigned short first;
unsigned int second;
};
Checking the sizeof variables declared to be of this type, I get 64
bits, when really the size of a short is 16 bits, and the size of an
int is 32 bits. Declaring the struct to have two shorts produces
expected results, and declaring it to have one short also produces
expected results.
I do understand what is going on here. The size is being adjusted
or "aligned" (I don't want to misuse that term so correct me if I used
it wrong) to the size of the largest constituent element. However, I

No, its get aligned to whatever the compiler thinks is reasonable.
Part of the problem is that on some systems it's e.g. not allowed
to access integers that don't start on a address that can be divided
by 4. And when a short int has a size of 2 and there would be no
padding the int would start on an address that only can be divided
by 2 but not by 4 and an read or write access to that int would crash
the program.
would like to prevent this as it does have the end result of munging
communication as it relates to this protocol.
What can I do to have a struct, declared as above, either return a
size of 6, or be manipulated so as to present itself as a string of 48
bits?

Some compilers let you invoke some compiler-specific magic that
makes it leave out the padding for the structure - see your
compiler documentation for that. But there's no portable way to
do it.

If you want to stay portable use an array of unsigned chars, long
enough to hold the data, instead of a structure. Copy them into (or
out of) the array using memcpy() and finally write that array to
the file or read it back, i.e. something like this:

unsigned char buf[ sizeof( unsigned short ) + sizeof ( unsigned int ) ];
unsigned short first;
unsigned int second;
FILE *fp;

....

memcpy( buf, &first, sizeof first );
memcpy( buf + sizeof first, &second, sizeof second );
if ( fwrite( buf, sizeof buf, 1, fp ) != sizeof buf )
exit( EXIT_FAILURE );

....

if ( fread( buf, sizeof buf, 1, fp ) != sizeof buf )
exit( EXIT_FAILURE );
memcpy( &first, buf, sizeof first );
memcpy( &second, buf + sizeof first, sizeof second );

If this looks too ugly hide it away in a function;-)

Regards, Jens
 
J

Jack Klein

Depending upon your compiler you should investigate a method to allow
the structure to be packed.

With Gcc you may use __attribute__ ((packed)) for example.

Eg.

struct Tester {
unsigned short first;
unsigned int second;
} __attribute__ ((packed));

l8r

This is actually an extremely bad idea. On some platforms it will
only produce code that executes more slowly. On others, violating the
hardware alignment requirements will result in hardware exceptions
killing the program.

And it's off-topic here.
 
M

Mabden

Sean said:
I have a struct that I wrote to test a protocol. The idea I had was
to just declare the elements of the struct in the order in which they
are sent and received as defined by the protocol. However, writing
this struct to a file produces unexpected results.

Here is a test struct I wrote:

struct Tester {
unsigned short first;
unsigned int second;
};

You could try reversing the order. By putting the int first it will be
aligned to whatever boundary it requires "outside" of the structure. Then
the short _may_ be able to skip the padding, if it is allowed to align right
behind the int. It's worth testing, just to know for future reference on
that compiler / platform.
 
A

Alex Fraser

Mabden said:
You could try reversing the order. By putting the int first it will be
aligned to whatever boundary it requires "outside" of the structure. Then
the short _may_ be able to skip the padding, if it is allowed to align
right behind the int.

On most implementations there will still be padding after the unsigned
short, and sizeof(struct Tester) will be unchanged.

Alex
 
M

Mabden

Alex Fraser said:
On most implementations there will still be padding after the unsigned
short, and sizeof(struct Tester) will be unchanged.

Of course. That is why I said to "try it" and it "may work", and in the part
you snipped, that "It's worth testing, just to know for future reference on
that compiler / platform."

But thanks for the useful insights you provided.
 
K

Keith Thompson

Jack Klein said:
[...]
Depending upon your compiler you should investigate a method to allow
the structure to be packed.

With Gcc you may use __attribute__ ((packed)) for example.

Eg.

struct Tester {
unsigned short first;
unsigned int second;
} __attribute__ ((packed));

This is actually an extremely bad idea. On some platforms it will
only produce code that executes more slowly. On others, violating the
hardware alignment requirements will result in hardware exceptions
killing the program.

Methods that allow packing structures are compiler-specific and
extremely non-portable (see question 2.12 in the C FAQ).

However, if a compiler does provide such a mechanism, it will almost
certainly do whatever is necessary to generate the right code to
access the members without killing the program. In the case of
"struct Tester" above, this might involve using two instructions to
access the halves of the (misaligned) member "second", or using the
equivalent of memcpy().
 

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,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top