Page 1 of 1

sizeof() bug?

Posted: Fri Nov 14, 2014 8:21 pm
by fredvr
Looks like sizeof() reports the wrong size for the Union below.

Code: Select all

union {
	unsigned char  			byte[0];   
	struct {
		struct {  
			unsigned char	cmd;         // 1 byte
			float			amount;    // 4
			float			speed;       // 4
			float			accel;        // 4
			float			pos;           // 4
			unsigned char   dir;            // 1
		} exec;
		struct { 
			unsigned char 	lastCmd;               // 1 byte
			unsigned char	lastExecStat;        //  1 byte
			float			currPos;               //  4 bytes
			struct {                                   
				unsigned char fwd 	       :1;
				unsigned char moving       :1;
				unsigned char idle 	       :1;
				unsigned char sleep         :1;
				unsigned char pwrSave	:1;				
			} bits;			                     // 1 byte
		} stat;
	} flds;
} I2Cbuf;
Because I need to transfer the content by copying this buffer by looping through byte[0] .. byte[ sizeof(I2Cbuf) -1 ] I wanted to check if the union is setup correctly starting by looking at it's size using the code below.

The results are not what I expected to be and it looks like sizeof() is not reporting consistent values here..

Code: Select all

i = sizeof( I2Cbuf.flds );
// i -> 36bytes  which would be the size of  I2Cbuf......

i= 0;
i += sizeof( I2Cbuf.flds.exec.cmd );
i += sizeof( I2Cbuf.flds.exec.amount  );
i += sizeof( I2Cbuf.flds.exec.speed ); 
i += sizeof( I2Cbuf.flds.exec.accel );
i += sizeof( I2Cbuf.flds.exec.pos );
i += sizeof( I2Cbuf.flds.exec.dir );
// i -> 18   which is correct IMHO and should be similar to I2Cbuf.flds.exec 
		
i = sizeof( I2Cbuf.flds.exec );	
// i-> 24.....!!!!!!

i= 0;
i += sizeof( I2Cbuf.flds.stat.bits );
i += sizeof( I2Cbuf.flds.stat.lastCmd );
i += sizeof( I2Cbuf.flds.stat.lastExecStat );
i += sizeof( I2Cbuf.flds.stat.currPos );
//i -> 7   which is correct IMHO

however:
i = sizeof( I2Cbuf.flds.stat );
//i -> 12  !!!!!!!!
I would think the size of I2Cbuf.flds should be 18+7= 25 and not 36..
I'm missing something here..?

Using v6.1.1

Re: sizeof() bug?

Posted: Fri Nov 14, 2014 8:47 pm
by tankist02
Data alignment?

Re: sizeof() bug?

Posted: Fri Nov 14, 2014 11:57 pm
by fredvr
First of all, I'm pretty unexperienced so I can easily misjudge stuff and my assumptions can be wrong... ;)

But for me I don't have the feeling this difference in reported memory usage is caused by data alignment:
- I tried to fix the array I2Cbuf.byte[0] to I2Cbuf.byte[30]. This field is in the union the counter part of I2Cbuf.flds and share the same memory.
I assume (..) that byte[30] is always one memory block and not spread because of alignement. exact the same result (36 bytes in total)
- I can imagine that some allignment is involved but not causing 24 bytes to actualy use 36bytes which exceeds 1 byte overhead for each field average even when there are several single byte fields involved..

If it really is caused by data allignment then the use of struct/union seems to be very,very, very inefficient in memory use. As far as I know there is not much special in what I´m doing here, I think.

(For me it is important to keep this buffer (I2Cbuf) as compact as possible because it's total is limited to 32bytes max.)

Re: sizeof() bug?

Posted: Sat Nov 15, 2014 3:33 am
by Gibbon1
If you are using gcc what you are looking for is

typedef struct __attribute__((packed))
{
uint8_t something;
uint32_t something_else;
} 5byte_size_t;

Without packed attribute you have no idea how the compiler is going to lay the struct out in memory. Usually the compiler optimizes for speed not space. Which means aligning everything on a word boundary, so your struct will have gaps.

When I switched code from an AVR to an ARM I had to add a bunch of packet attributes.

A gotcha: On some ARM processors volatile and packed may result in seg fault because volatile says, every access must be done in a single read or write but the uP can't do a single read or write to an unaligned word, faced with that the compiler will emit code to do an single unaligned access (and emits a warning).

Re: sizeof() bug?

Posted: Sat Nov 15, 2014 4:47 pm
by fredvr
so it is really an allignment issue... :(

I played a bit around with the __attribute __ and the size shrinked from 36 with 6 bytes to 30 bytes max.
Adding the attribute at the top of the struct was not enough to let it shrink these 6 bytes. It was necessary to add this 'attribute' to every underlaying struct aswell.

Still 5 bytes more than the sum of the individual fields is though. Can't get it more compact using __attribute__
Glad to know about the existance of this attribute now, though. Something to keep in mind :)

Compiled the same union in Arduino IDE (AVR). Here sizeof() reports the 25bytes even without __attribute__
I add it anyway to be sure to get the max out of it.

Re: sizeof() bug?

Posted: Sun Nov 16, 2014 7:25 am
by Gibbon1
I use typedef stucts with the packed attribute a lot for casting buffers to some meaningful data structure (I wish C had little_indian big_indian attributes)

In your case perhaps the structures with bitfields are causing the size to be larger than expected.

I never use bitfields, instead I use macro's to define bit masks.

// Like this
#define CLOCK_EN (1<<7)

if(flags & CLOCK_EN)
do_something();

Anyway not much of a codelite question.

Re: sizeof() bug?

Posted: Sun Nov 16, 2014 5:47 pm
by Alix
The original problem is really a data aligment problem. Here is how the compiler implements your structure (look at the gaps):

Code: Select all

    union {
       unsigned char           byte[0];   
       struct {
          struct { 
             unsigned char   cmd;         // 1 byte
             char          gap1[3];       // 3 bytes
             // float must be aligned on 4-byte boundary
             float         amount;    // 4
             float         speed;       // 4
             float         accel;        // 4
             float         pos;           // 4
             unsigned char   dir;            // 1
          } exec;
         char        gap2[3];        // 3 bytes
         // next structure must be aligned on 4-byte boundary because of the float
          struct {
             unsigned char    lastCmd;               // 1 byte
             unsigned char   lastExecStat;        //  1 byte
             char          gap3[2];               // 2 bytes
             // float must be aligned on 4-byte boundary
             float         currPos;               //  4 bytes
             struct {                                   
                unsigned char fwd           :1;
                unsigned char moving       :1;
                unsigned char idle           :1;
                unsigned char sleep         :1;
                unsigned char pwrSave   :1;            
             } bits;                              // 1 byte
          } stat;
          // the whole structure contains a multiple of its minimal alignment (4 bytes)
          chat gap4[3]                      // 3 bytes
       } flds;
    } I2Cbuf;
If you totalize the size of each field (including the gaps due to alignment requirements), you get the total of 36 bytes.

Re: sizeof() bug?

Posted: Mon Nov 17, 2014 1:51 am
by fredvr
okay, I'm 100% convinced now..!! :)
float must be aligned on 4-byte boundary
This gave me enough to rearrange the struct in such way (all floats clustered at the start of the structs and in combination with __attribute__((packed)) that sizeof() now really reports 25bytes!!!!!

BINGO! Great to have learned about all this. I knew about allignment but never realized that this can have such an impact and you really have to be aware of this behaviour when all bytes count!!

thanks guys!

Re: sizeof() bug?

Posted: Mon Nov 17, 2014 12:27 pm
by Jarod42
Anyway, it is unrelated to Codelite as this question is a code question.