EmbDev.net

Forum: ARM programming with GCC/GNU tools sizeof struct


Author: Steve Bliss (steve17)
Posted on:

Rate this post
0 useful
not useful
I am a bit surprised that structs that contain only chars are padded to
have a size that is a multiple of 4 bytes.  For instance:


      struct Char1   {     // sizeof == 4
         char char1;
         };

      struct Char2   {     // sizeof == 4
         char char1;
         char char2;
         };

      struct Char3   {     // sizeof == 4
         char char1;
         char char2;
         char char3;
         };

      struct Char4   {     // sizeof == 4
         char char1;
         char char2;
         char char3;
         char char4;
         };

      struct Char5   {     // sizeof == 8
         char char1;
         char char2;
         char char3;
         char char4;
         char char5;
         };

The GNU compiler for the Atmel 8 bit machines (avr-gcc) doesn't pad
these structures.  Neither does Microsoft's compiler (targeting the PC
of course) do this padding.

Is this normal?  Is there an option to disable this padding?  Is there a
better forum for asking these questions?

I'm using arm-elf-gcc version 4.1.1

Thanks,
Steve

Author: coubi (Guest)
Posted on:

Rate this post
0 useful
not useful
Steve Bliss wrote:
> I am a bit surprised that structs that contain only chars are padded to
> have a size that is a multiple of 4 bytes.  For instance:
>
>
>       struct Char1   {     // sizeof == 4
>          char char1;
>          };
>
>       struct Char2   {     // sizeof == 4
>          char char1;
>          char char2;
>          };
>
>       struct Char3   {     // sizeof == 4
>          char char1;
>          char char2;
>          char char3;
>          };
>
>       struct Char4   {     // sizeof == 4
>          char char1;
>          char char2;
>          char char3;
>          char char4;
>          };
>
>       struct Char5   {     // sizeof == 8
>          char char1;
>          char char2;
>          char char3;
>          char char4;
>          char char5;
>          };
>
> The GNU compiler for the Atmel 8 bit machines (avr-gcc) doesn't pad
> these structures.  Neither does Microsoft's compiler (targeting the PC
> of course) do this padding.
>
> Is this normal?  Is there an option to disable this padding?  Is there a
> better forum for asking these questions?
>
> I'm using arm-elf-gcc version 4.1.1
>
> Thanks,
> Steve


Hi Steve, the overhead of your structs is caused by the 4 bytes
alignment.
To avoid this alignment padding use the packed attribute :

struct Char1   {
    char char1;
} __attribute__(packed);

See also section 5.31 of the GCC manual

Coubi

Author: Martin Thomas (mthomas) (Moderator)
Posted on:

Rate this post
0 useful
not useful
coubi wrote:
> Steve Bliss wrote:
>> [...]
>> The GNU compiler for the Atmel 8 bit machines (avr-gcc) doesn't pad
>> these structures.  Neither does Microsoft's compiler (targeting the PC
>> of course) do this padding.

IRC I have seen padding or alignments with a MS Compiler too. But maybe
you did disable it or a default option set be a "wizard" disabled it.

>> Is this normal?  Is there an option to disable this padding?  Is there a
>> better forum for asking these questions?

Yes, as explained by Coubi.

>>
>> I'm using arm-elf-gcc version 4.1.1
>>
>> Thanks,
>> Steve
>
>
> Hi Steve, the overhead of your structs is caused by the 4 bytes
> alignment.
> To avoid this alignment padding use the packed attribute :
>
> struct Char1   {
>     char char1;
> } __attribute__(packed);
>
> See also section 5.31 of the GCC manual

I have had problems with single brackets. Maybe two are needed:
__attribute((packed)). But it should be explained in the section Coubi
mentioned.

Maybe these "fragments" will help too (not tested, just to get the
idea):

#define USE_ATTR
// if ndef'd use pragma

#ifdef USE_ATTR

#define PACKED __attribute__((_packed_))
typedef struct elem_ {
  unsigned char b1;
  unsigned char b2;
} PACKED elem;

#else

#pragma pack(1)
typedef struct elem_ {
  unsigned char b1;
  unsigned char b2;
} elem;
#pragma pack()
// packing switched back to initial state

#endif

AFAIK the usual method with GNU/arm-*-gcc is using an attribute (with
the RV compiler too, but a different syntax). But the pragma works too
(tested with arm-elf-gcc 4.1.2 but not with the code above) and as far
as I know it's compatible with the method used for the IAR ARM-compiler.
At least I have seen source-code for the IAR-EWARM using the packed
pragma with the same syntax.

Martin Thomas

Author: Steve Bliss (steve17)
Posted on:

Rate this post
0 useful
not useful
coubi wrote:
> To avoid this alignment padding use the packed attribute :
>
> struct Char1   {
>     char char1;
> } __attribute__(packed);
>
> See also section 5.31 of the GCC manual
>
> Coubi

Thanks for the quick reply.  The packed attribute does the job.  By the
way, it needs double parenthese. __attribute__((packed))

Of course it could have undesirable side effects if I had some 16 bit or
32 bit objects in the structure.  But I don't, so the packed attribute
would work okay.

I am used to structures having holes in order to give good alignment for
multi-byte operands.  I think all compilers do that.  But no alignment
is required for chars, so that is why I'm a bit surprised at the hole at
the end.

Oh well, no matter how a compiler is implemented, someone will be
annoyed :)

Author: Steve Bliss (steve17)
Posted on:

Rate this post
0 useful
not useful
Steve Bliss wrote:
>
> I am used to structures having holes in order to give good alignment for
> multi-byte operands.

Hmmm.  Apparently there is no way to edit my own posts.  Of course I
meant multi-byte objects, not operands.

Author: Steve Bliss (steve17)
Posted on:

Rate this post
0 useful
not useful
Martin Thomas wrote:

> IRC I have seen padding or alignments with a MS Compiler too. But maybe
> you did disable it or a default option set be a "wizard" disabled it.

It's possible the "wizard" did it, but I don't think so.  I believe all
compilers will put holes in structures to align the various member
objects.  The MS compiler does this.  But it my case, there are no
alignment requirements for any of the members because they are all
chars.  I think this is just a difference between MS and GCC.

GCC is probably aligning the entire structure on a 32 bit address, and
by making each struct have a size that is a multiple of 32 bits, an
array of such structs would have each struct aligned at 32 bits.

Here's a quote from the MS compiler help:
"When the sizeof operator is applied to a class, struct, or union type,
the result is the number of bytes in an object of that type, plus any
padding added to align members on word boundaries."

Note that there is no padding added to align members because the members
are all chars.  The padding is added to the end of the struct.

> I have had problems with single brackets. Maybe two are needed:
> __attribute((packed)). But it should be explained in the section Coubi
> mentioned.

You are correct, it does need double parentheses, as I found out by
trial and error.

> Maybe these "fragments" will help too (not tested, just to get the
> idea):
>
> #define USE_ATTR
> // if ndef'd use pragma
>
> #ifdef USE_ATTR
>
> #define PACKED __attribute__((_packed_))
> typedef struct elem_ {
>   unsigned char b1;
>   unsigned char b2;
> } PACKED elem;
>
> #else
>
> #pragma pack(1)
> typedef struct elem_ {
>   unsigned char b1;
>   unsigned char b2;
> } elem;
> #pragma pack()
> // packing switched back to initial state
>
> #endif

> Martin Thomas

It did indeed help.  Using a #define for the _attribute_ thing is a
good idea.
It allows me to compile the same code with the MS compiler without
having to put a "#ifdef _WIN32" thing around each _attribute_, as the
MS compiler doesn't understand _attribute_.  But with your #define
PACKED idea, I can simply define PACKED as nothing when using the MS
compiler.

By the way, the MS compiler does understand #pragma pack but I'm not
sure the syntax is identical.

That reminds me of a problem I caused myself a few years ago.  I had a
#pragma pack in a header file.  This caused some problems that were
mysterious and hard to figure out.  It took months for me to do so.  The
solution was to use #pragma pack(push) before changing the packing
alignment, and #pragma pack(pop) afterward.  I was using an MS compiler.

You see, if the regular packing alignment isn't "popped", then the
#pragma pack applies to everything included in the source file after the
header file is included.  So it would affect all header files included
after the one using the pragma.  Can you imagine the wierd things it was
doing to all the C++ classes included in the source files after the one
with the #pragma pack?

The moral of the story is don't use #pragma pack in a header file unless
you use the push and pop.

Thanks again, everyone.
Steve

Author: Clifford Slocombe (clifford)
Posted on:

Rate this post
0 useful
not useful
Steve Bliss wrote:
> I am a bit surprised that structs that contain only chars are padded to
> have a size that is a multiple of 4 bytes.

You shouldn't be surprised; it is allowed by the ISO standard for
structure members to be aligned in a manner most efficient on the target
platform.

On an 8bit device such as AVR, no packing is required. On a 32bit device
it is likely. I suspect you will also get different results between ARM7
and ARM9 since the latter has additional instructions for non-aligned
8-bit addressing. There may also be a difference between ARM and Thumb
mode code generation.

Either way, unless you have good reason to pack the structure, such as
matching some defined file format or communications framing, you should
let the compiler do as it sees fit. Packing may result in additional
code generation and/or slower code.

Clifford

Author: Steve Bliss (steve17)
Posted on:

Rate this post
0 useful
not useful
Clifford Slocombe wrote:
> You shouldn't be surprised; it is allowed by the ISO standard for
> structure members to be aligned in a manner most efficient on the target
> platform.

I'm not surprised that structure members are aligned, but that's not
what is happening here.  The structure members are all chars and are not
being aligned.  They are all "packed" inherently.  Only the size of the
structure is  being extended.  To put it another way the "hole" in the
structure is at the end of the structure.

If each char was placed on a 32 bit boundary by putting a 3 byte hole
between each char, then maybe that would enhance performance at the
expense of taking 4 times as much memory.  But that's not being done.
Only the "sizeof" is being changed, the char alignment isn't affected.
But if I wanted to do that, I could do it myself anyway.


>
> On an 8bit device such as AVR, no packing is required. On a 32bit device
> it is likely. I suspect you will also get different results between ARM7
> and ARM9 since the latter has additional instructions for non-aligned
> 8-bit addressing. There may also be a difference between ARM and Thumb
> mode code generation.

Again, the "packing" is a red herring.  The chars exist one right after
another whether I use the packed attribute or not.  Only the "sizeof"
the structure changes.


> Either way, unless you have good reason to pack the structure, such as
> matching some defined file format or communications framing, you should
> let the compiler do as it sees fit. >
> Clifford

Precisely so.  These are message that are exchanged between computers.
I don't want to use the "packed" attribute, and I probably won't, but it
would be nice if the "sizeof" operator gave a useful result.

Again using the "packed" attribute doesn't pack anything.  It only makes
the sizeof operator give me the size of the message.  As I don't like
using the "packed" attribute I probably will pad out all my messages to
be a multiple of 4 bytes, or I could write a function to count the
chars.  That doesn't seem all that efficient to me.

Steve

Author: Clifford Slocombe (clifford)
Posted on:

Rate this post
0 useful
not useful
Steve Bliss wrote:
> ... Only the size of the
> structure is  being extended.  To put it another way the "hole" in the
> structure is at the end of the structure.
>
How have you determined that? If that is what is happening, it is
likeley that it is done to achieve efficient array access. So that
adjacent elements are aligned optimally.

Steve Bliss wrote:
>
> ... These are message that are exchanged between computers.
> I don't want to use the "packed" attribute, and I probably won't, but it
> would be nice if the "sizeof" operator gave a useful result.
>
sizeof is giving a useful result, it is giving the amount of space the
compiler has allocated to that type. If for example you placed such
structures in an array, sizeof would indicate the 'separation' of the
elements, which would be exactly what you would want to know.

The problem is not the 'usefulness' of sizeof, but rather the fact that
you have little control over it.

> ... As I don't like
> using the "packed" attribute I probably will pad out all my messages to
> be a multiple of 4 bytes, or I could write a function to count the
> chars.  That doesn't seem all that efficient to me.

That is probably not a good idea, you are making assumptions about the
behaviour of the compiler and the architecture at both ends of your
link. Making it work by mere observation of such behaviour is a
dangerous practice. Adding 'padding' may not align the members in the
way you expect or in the same way on two different architectures, or
even with different compiler options or optimizations, or different
compiler versions.

On top of that the two ends of the link may have different
'endian-ness'. You may think that is not a problem with just char types,
but it is if the compiler places the char value at the 'wrong' end of
the alignment.

I understand your lack of enthusiasm for attributes or pragmas - they
are non-portable. I would suggest two portable solutions:

1) Implement serializer/deserializer routines. A serialiser
programatically extracts the member data and transmits it or places int
in an array in the expected order. The deserializer reverses this
process and serially reads bytes from the input or a buffer and
programatically places them in the structure.

e.g.
int serialise( struct Char3* packet, char* buffer )
{
    int size = 0 ;
    buffer[size] = packet->char1 ;
    size++ ;

    buffer[size] = packet->char2 ;
    size++ ;

    buffer[size] = packet->char3 ;
    size++ ;

    return size ;
}

int deserialise( struct Char3* packet, char* buffer )
{
    int size = 0 ;
    packet->char1 = buffer[size] ;
    size++ ;

    packet->char3 = buffer[size] ;
    size++ ;

    packet->char2 = buffer[size] ;
    size++ ;

    return size ;
}


2) Use a char array rather than a structure, with an associated set of
offset macros or enumerations.

enum Char3
{
    CHAR1_OFFSET,
    CHAR2_OFFSET,
    CHAR3_OFFSET,
    SIZEOF_CHAR3
} ;

char packet[SIZEOF_CHAR3] = { 1, 2, 3 )

packet[CHAR1_OFFSET] = 0 ;
packet[CHAR2_OFFSET] = 1 ;
packet[CHAR3_OFFSET] = 10 ;

transmit( packet, SIZEOF_CHAR3 ) ;



Clifford

Author: Clifford Slocombe (clifford)
Posted on:

Rate this post
0 useful
not useful
Clifford Slocombe wrote:
> I understand your lack of enthusiasm for attributes or pragmas - they
> are non-portable.

I meant to add here: But your proposition is even less so, and unlike
the attribute method would not generate a compiler error or warning on a
compiler that did not support it. Note that #pragma wouldn't produce an
error either, even on a compiler that did not support it - it would just
be ignored.

Author: steve17 (Guest)
Posted on:

Rate this post
0 useful
not useful
Clifford Slocombe wrote:
> Steve Bliss wrote:
>> ... Only the size of the
>> structure is  being extended.  To put it another way the "hole" in the
>> structure is at the end of the structure.
>>
> How have you determined that?

How do you think I determined that.  Maybe I looked?

> If that is what is happening, it is
> likeley that it is done to achieve efficient array access. So that
> adjacent elements are aligned optimally.

But no alighment is taking place.  I've seen a lot of computers and I've
never seen one that aligned chars, and I never expect to.  That would be
a strange machine.  Not surprisingly, this compiler on this machine is
not aligning chars.  I gave examples in my first post.  Alignment would
imply leaving holes between members.  Please explain how four chars
would be aligned for optimal access in a struct that has a size of 4.

Like in all computers that ever existed, chars are placed adjacently.
In a struct (or array) containg at leas four chars, at least one would
be at a 32 bit boundary, one would be at a 16 bit boundary, and the
other two would be at no particular boundary.  If chars weren't placed
adjacently, that would make a mess of character strings, wouldn't it?

If there were multibyte objects in the struct, then aligning the structs
in an array would make sense.  But as there are no multibyte members in
these structs, there is no alignment.  Just wasted space and a sizeof
that is useless to me.

My guess is that to make the compiler simpler, all structs are given a
size that is a multiple of 4 bytes so if there is an array of them, they
will all be suitably aligned.  But in the case of structs with only
chars, there is no benefit from this.  Only wasted space.
>
> Steve Bliss wrote:
>
>> ... As I don't like
>> using the "packed" attribute I probably will pad out all my messages to
>> be a multiple of 4 bytes, or I could write a function to count the
>> chars.  That doesn't seem all that efficient to me.
>
> That is probably not a good idea, you are making assumptions about the
> behaviour of the compiler and the architecture at both ends of your
> link. Making it work by mere observation of such behaviour is a
> dangerous practice. Adding 'padding' may not align the members in the
> way you expect or in the same way on two different architectures, or
> even with different compiler options or optimizations, or different
> compiler versions.

I think you are making assumptions too. Namely that this behavior makes
sense.  I don't think it does.

And to repeat for the upteenth time, the members are not being aligned.
IF you can show me one instance of one computer in the history of
mankind that aligned chars, I would be very surprised.  One has to
assume the compiler is working as intended other than where it's buggy,
or undocumented.

>

Author: Clifford Slocombe (clifford)
Posted on:

Rate this post
0 useful
not useful
I think what I am about to suggest is ill-advised because regardless of
how universal it may be, it is still relying on implementation defined
behaviour; but if you are prepared to rely on your observations standing
in all relevant cases, compilers, targets and configurations then you
could add a dummy char member at the end of your structure and use it to
determine the 'useful size' of the structure at run-time. Something
like:

struct Char5
{
   char char1;
   char char2;
   char char3;
   char char4;
   char char5;
   char end ;
};

...

struct Char5 dummy ;
size_t sizeof_Char5 = (size_t)(&dummy.end - (char*)&dummy) ;


This would allow you to squirt only the 'useful' part of the structure
on to your communications link.


steve17 wrote:
> Clifford Slocombe wrote:
>> Steve Bliss wrote:
>
> How do you think I determined that.  Maybe I looked?
>
It seemed a reasonable question, perhaps you took it the wrong way. I
was merely trying to ascertain whether your investigation method was
sound. You seem sure. But you are the one asking for solutions, I don't
need to help. I was asking so that I don't have to take the trouble to
verify your observations. You don't have to make it that easy for me,
but I am not in a position to reproduce your results at present.

>> If that is what is happening, it is
>> likely that it is done to achieve efficient array access. So that
>> adjacent elements are aligned optimally.
>
> But no alighment is taking place.
That is not what I suggested. I was suggesting that the 'end-padding'
was to force alignment of adjacent structures in an array. I meant
"adjacent array elements", but I thought that would be obvious in
context.

>
> If there were multibyte objects in the struct, then aligning the structs
> in an array would make sense.  But as there are no multibyte members in
> these structs, there is no alignment.  Just wasted space and a sizeof
> that is useless to me.
You are right, but the structure itself is a multibyte object, and
alignment within an array will have an advantage. The structure cannot
change size depending on how it is used so it is padded to optimise
array access even if no array access actually takes place.

>
> My guess is that to make the compiler simpler, all structs are given a
> size that is a multiple of 4 bytes so if there is an array of them, they
> will all be suitably aligned.  But in the case of structs with only
> chars, there is no benefit from this.  Only wasted space.
There is a potential benefit because the structure may be accessed as a
single object - for example parameter passing-by-copy; a misalignment
would make the copy less efficient.

>
> I think you are making assumptions too. Namely that this behavior makes
> sense.  I don't think it does.
>
Not at all; implementation defined behaviour is just that - make no
assumptions. It is not a matter of whether it makes sense; if it is
legal within the ISO standard, it is unwise to assume it won't be done
just because you think it makes no sense. Remember it is not the
compiler you are relying on being sane or smart, it is the compiler
writer - a human. He may be smart in ways you don't understand, or he
may have written the code after an argument with his wife!

You are "tilting at windmills" to be arguing "this code should work even
though the compiler can legally break it". Code that makes assumptions
about legal implementation defined behaviour is just poor code
regardless of whether it would make sense for those assumptions to be
true.

> And to repeat for the upteenth time, the members are not being aligned.
I think I got that - and my whole argument after querying your test
method for the reasons I explained, was based on the assumption that
your observation was correct.


> IF you can show me one instance of one computer in the history of
> mankind that aligned chars, I would be very surprised.  One has to
> assume the compiler is working as intended other than where it's buggy,
> or undocumented.
>
You may only access the char members, but the compiler may generate code
or use library calls that access structures stored in an array of
contiguous elements, each one of which must be aligned for efficient and
deterministic access.

In the end the path of least resistance is to use the packed attribute
and perhaps some conditional compilation to cope with different
compilers' individual solutions to packing. The path of greatest
portability is one of the solutions I proposed previously or something
similar. And the path of disaster is the one you are arguing for despite
the fact that that code will break on probably the most widely used
compiler in the world! By the way, if you use the approach I suggested
at the beginning of this post - don't tell anyone I suggested it - I
want nothing to do with it ;-)


Clifford

Reply

Entering an e-mail address is optional. If you want to receive reply notifications by e-mail, please log in.

Rules — please read before posting

  • Post long source code as attachment, not in the text
  • Posting advertisements is forbidden.

Formatting options

  • [c]C code[/c]
  • [avrasm]AVR assembler code[/avrasm]
  • [code]code in other languages, ASCII drawings[/code]
  • [math]formula (LaTeX syntax)[/math]




Bild automatisch verkleinern, falls nötig
Note: the original post is older than 6 months. Please don't ask any new questions in this thread, but start a new one.