sizeof struct returning unexpected results

Discussion in 'C Programming' started by Sean, Aug 20, 2004.

  1. Sean

    Sean Guest

    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.
     
    Sean, Aug 20, 2004
    #1
    1. Advertisements

  2. Sean

    Mr Wibble Guest

    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
     
    Mr Wibble, Aug 20, 2004
    #2
    1. Advertisements

  3. Sean

    Tom St Denis Guest

    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
     
    Tom St Denis, Aug 20, 2004
    #3
  4. Sean

    Lew Pitcher Guest

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1
    How do you determine this? sizeof measures in char units, not bit units.

    You know this because...?
    You know this because...?
    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.
    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-----
     
    Lew Pitcher, Aug 20, 2004
    #4
  5. Sean

    Eric Sosman Guest

    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.
     
    Eric Sosman, Aug 20, 2004
    #5
  6. 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.
    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
     
    Jens.Toerring, Aug 20, 2004
    #6
  7. Sean

    Jack Klein Guest

    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.
     
    Jack Klein, Aug 21, 2004
    #7
  8. Sean

    Mabden Guest

    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.
     
    Mabden, Aug 21, 2004
    #8
  9. Sean

    Alex Fraser Guest

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

    Alex
     
    Alex Fraser, Aug 21, 2004
    #9
  10. Sean

    Mabden Guest

    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.
     
    Mabden, Aug 21, 2004
    #10
  11. 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().
     
    Keith Thompson, Aug 26, 2004
    #11
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.