In the following struct, I feel the size should be 20, but it's coming out to be 24.
class X {
unsigned int a;
unsigned int b;
double c;
unsigned int d;
};
Why can't the compiler arrange d from 16-20 bytes ?
This is so that c remains aligned on an 8-byte boundary in an array of X.
X x[2];
X[0]:
0-3 unsigned int a
4-7 unsigned int b
8-15 double c
16-19 unsigned int d
20-23 [PAD]
X[1]:
24-27 unsigned int a
28-31 unsigned int b
32-39 double c
40-43 unsigned int d
44-47 [PAD]
So, you can see that you need 4 bytes of pad between the array elements. This is captured by adding 4-bytes at the end of the object. If the second array element started at position 20, then you find that x[1].c was not 8 byte aligned.
So, finally, d does start at byte 16 as you expect, but the end of d is not the end of the object.
In POD structs like this, the address of the struct is the address of its first element. The size is the distance in memory between successive elements of an array of type X[]. For the double to be aligned in the second element of a hypothetical array, the first int and the entire struct must be aligned to the same strictness as double. This requires padding at the end.
On which platform you are running ? on my ubuntu machine it is giving me output 20 only !!
sizeof unsigned int is 4 and double is 8 and thats why the output is 20 on my machine!!
This varies from hardware to hardware !!
Related
I understand that in C++, if we have a struct like this:
struct x_
{
char a; // 1 byte
int b; // 4 bytes
short c; // 2 bytes
char d; // 1 byte
} MyStruct;
Memory structure will look like this due to compiler padding:
struct x_
{
char a; // 1 byte
char _pad0[3]; // padding to put 'b' on 4-byte boundary
int b; // 4 bytes
short c; // 2 bytes
char d; // 1 byte
char _pad1[1]; // padding to make sizeof(x_) multiple of 4
}
Can somebody please help me understand why sizeof(x_) must be a multiple of 4, and not any other number?
At least, a class must have the same alignment largest alignment of any of its non-static data member. So x_ must be aligned as b. If x_ had a lower alignment requirement, then b could be misaligned. Imagine an object x_ at address 3 then its b member would also be at an address 3+alignof(int). If alignof(int)==4 then b would be at address 7, so it would be misaligned. So following this example, alignof(x_)==4;
Then the size of an object must be a multiple of the alignment because elements of an array are required to be contiguous: a pointer to the end of element n must be a pointer to the element n+1. So in your case x size must be a multiple of 4.
I've been learning about structure data padding since I found out my sizeof() operator wasn't returning what I expected. According to the pattern that I've observed, it aligns structure members with the largest data type. So for example...
struct MyStruct1
{
char a; // 1 byte
char b; // 1 byte
char c; // 1 byte
char d; // 1 byte
char e; // 1 byte
// Total 5 Bytes
//Total size of struct = 5 (no padding)
};
struct MyStruct2
{
char a; // 1 byte
char b; // 1 byte
char c; // 1 byte
char d; // 1 byte
char e; // 1 byte
short f; // 2 bytes
// Total 7 Bytes
//Total size of struct = 8 (1 byte of padding between char e and short f
};
struct MyStruct3
{
char a; // 1 byte
char b; // 1 byte
char c; // 1 byte
char d; // 1 byte
char e; // 1 byte
int f; // 4 bytes
// Total 9 bytes
//Total size of struct = 12 (3 bytes of padding between char e and int f
};
However if make the last member an 8 byte data type, for example a long long, it still only adds 3 bytes of padding, making a four-byte aligned structure. However if I build in 64 bit mode, it does in fact align for 8 bytes (the biggest data type). My first question is, am I wrong in saying it aligns the members with the largest data type? This statement seems correct for a 64 bit build, but only true up to 4 byte data types in a 32 bit build. Has this to do with the native 'word' size of the CPU? Or the program itself?
My second question is, would the following be an entire waste of space and bad programming?
struct MyBadStruct
{
char a; // 1 byte
unsigned int b; // 4 bytes
UINT8 c; // 1 byte
long d; // 4 bytes
UCHAR e; // 1 byte
char* f; // 4 bytes
char g; // 1 byte
// Total of 16 bytes
//Total size of struct = 28 bytes (12 bytes of padding, wasted)
};
How padding is done, is not part of the standard. So it can be done differently on different systems and compilers. It is often done so that variables are aligned at there size, i.e. size=1 -> no alignment, size=2 -> 2 byte alignment, size=4 -> 4 byte alignment and so on. For size=8, it is normally 4 or 8 bytes aligned. The struct it self is normally 4 or 8 bytes aligned. But - just to repeat - it is system/compiler dependent.
In your case it seems to follow the pattern above.
So
char a;
int b;
will give 3 bytes padding to 4 byte align the int.
and
char a1;
int b1;
char a2;
int b2;
char a3;
int b3;
char a4;
int b4;
will end up as 32 byte (again to 4 byte align the int).
But
int b1;
int b2;
int b3;
int b4;
char a1;
char a2;
char a3;
char a4;
will be just 20 as the int is already aligned.
So if memory matters, put the largest members first.
However, if memory doesn't matter (e.g. because the struct isn't used that much), it may be better to keep things in a logical order so that the code is easy to read for humans.
Typically the best way to reduce the amount of padding inserted by the compiler is to sort the data members inside of your struct from largest to smallest:
struct MyNotSOBadStruct
{
long d; // 4 bytes
char* f; // 4 bytes
unsigned int b; // 4 bytes
char a; // 1 byte
UINT8 c; // 1 byte
UCHAR e; // 1 byte
char g; // 1 byte
// Total of 16 bytes
};
the size may vary depending on 32 vs 64 bit os because the size of a pointer will change
live version: http://coliru.stacked-crooked.com/a/aee33c64192f2fe0
i get size = 24
All of the following is implementation dependent. Do not rely on this for the correctness of your programs (but by all means make use of it for debugging or improving performance).
In general each datatype has a preferred alignment. This is never larger than the size of the type, but it can be smaller.
It appears that your compiler is aligning 64-bit integers on a 32-bit boundary when compiling in 32-bit mode, but on a 64-bit boundary in 64-bit mode.
As to your question about MyBadStruct: In general, write your code to be simple and easy to understand; only do anything else if you know (through measurement) that you have a problem. Having said that, if you sort your member variables by size (largest first), you will minimize padding space.
Here is a C++ class for revering bits from LeetCode discuss. https://leetcode.com/discuss/29324/c-solution-9ms-without-loop-without-calculation
For example, given input 43261596 (represented in binary as 00000010100101000001111010011100), return 964176192 (represented in binary as 00111001011110000010100101000000).
Is there anyone can explain it? Thank you so very much!!
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
struct bs
{
unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
unsigned int _08:1; unsigned int _09:1; unsigned int _10:1; unsigned int _11:1;
unsigned int _12:1; unsigned int _13:1; unsigned int _14:1; unsigned int _15:1;
unsigned int _16:1; unsigned int _17:1; unsigned int _18:1; unsigned int _19:1;
unsigned int _20:1; unsigned int _21:1; unsigned int _22:1; unsigned int _23:1;
unsigned int _24:1; unsigned int _25:1; unsigned int _26:1; unsigned int _27:1;
unsigned int _28:1; unsigned int _29:1; unsigned int _30:1; unsigned int _31:1;
} *b = (bs*)&n,
c =
{
b->_31, b->_30, b->_29, b->_28
, b->_27, b->_26, b->_25, b->_24
, b->_23, b->_22, b->_21, b->_20
, b->_19, b->_18, b->_17, b->_16
, b->_15, b->_14, b->_13, b->_12
, b->_11, b->_10, b->_09, b->_08
, b->_07, b->_06, b->_05, b->_04
, b->_03, b->_02, b->_01, b->_00
};
return *(unsigned int *)&c;
}
};
Consider casting as providing a different layout stencil on memory.
Using this stencil picture, the code is a layout of a stencil of 32-bits on an unsigned integer memory location.
So instead of treating the memory as a uint32_t, it is treating the memory as 32 bits.
A pointer to the 32-bit structure is created.
The pointer is assigned to the same memory location as the uint32_t variable.
The pointer will allow different treatment of the memory location.
A temporary variable, of 32-bits (using the structure), is created.
The variable is initialized using an initialization list.
The bit fields in the initialization list are from the original variable, listed in reverse order.
So, in the list:
new bit 0 <-- old bit 31
new bit 1 <-- old bit 30
The foundation of this approach relies on initialization lists.
The author is letting the compiler reverse the bits.
The solution uses brute force to revert the bits.
It declares a bitfield structure (that's when the members are followed by :1) with 32 bit fields of one bit each.
The 32 bit input is then seen as such structure, by casting the address of the input to a pointer to the structure. Then c is declared as a variable of that type which is initialized by reverting the order of the bits.
Finally, the bitfield represented by c is reinterpreted as an integer and you're done.
The assembler is not very interesting, as the gcc explorer shows:
https://goo.gl/KYHDY6
It doesn't convert per see, but it just looks at the same memory address differently. It uses the value of the int n, but gets a pointer to that address, typecasts the pointer, and that way, you can interpret the number as a struct of 32 individual bits. So through this struct b you have access to the individual bits of the number.
Then, of a new struct c, each bit is bluntly set by putting bit 31 of the number in bit 0 of the output struct c, bit 30 in bit 1, etcetera.
After that, the value at the memory location of the struct is returned.
First of all, the posted code has a small bug. The line
return *(unsigned int *)&c;
will not return an accurate number if sizeof(unsigned int) is not equal to sizeof(uint32_t).
That line should be
return *(uint32_t*)&c;
Coming to the question of how it works, I will try to explain it with a smaller type, an uint8_t.
The function
uint8_t reverseBits(uint8_t n) {
struct bs
{
unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
} *b = (bs*)&n,
c =
{
b->_07, b->_06, b->_05, b->_04
, b->_03, b->_02, b->_01, b->_00
};
return *(uint8_t *)&c;
}
uses a local struct. The local struct is defined as:
struct bs
{
unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
};
That struct has eight members. Each member of the struct is a bitfield of width 1. The space required for an object of type bs is 8 bits.
If you separate the definition of the struct and the variables of that type, the function will be:
uint8_t reverseBits(uint8_t n) {
struct bs
{
unsigned int _00:1; unsigned int _01:1; unsigned int _02:1; unsigned int _03:1;
unsigned int _04:1; unsigned int _05:1; unsigned int _06:1; unsigned int _07:1;
};
bs *b = (bs*)&n;
bs c =
{
b->_07, b->_06, b->_05, b->_04
, b->_03, b->_02, b->_01, b->_00
};
return *(uint8_t *)&c;
}
Now, lets' say the input to the function is 0xB7, which is 1011 0111 in binary. The line
bs *b = (bs*)&n;
says:
Take the address of n ( &n )
Treat it like it is a pointer of type bs* ( (bs*)&n )
Assign the pointer to a variable. (bs *b =)
By doing that, we are able to pick each bit of n and get their values by using the members of b. At the end of that line,
The value of b->_00 is 1
The value of b->_01 is 0
The value of b->_02 is 1
The value of b->_03 is 1
The value of b->_04 is 0
The value of b->_05 is 1
The value of b->_06 is 1
The value of b->_07 is 1
The statement
bs c =
{
b->_07, b->_06, b->_05, b->_04
, b->_03, b->_02, b->_01, b->_00
};
simply creates c such that the bits of c are reversed from the bits of *b.
The line
return *(uint8_t *)&c;
says:
Take the address of c., whose value is the bit pattern 1110 1101.
Treat it like it is a pointer of type uint8_t*.
Dereference the pointer and return the resulting uint8_t
That returns an uint8_t whose value is bitwise reversed from the input argument.
This isn't exactly obfuscated but a comment or two would assist the innocent. The key is in the middle of the variable declarations, and the first step is to recognize that there is only one line of 'code' here, everything else is variable declarations and initialization.
Between declaration and initialization we find:
} *b = (bs*)&n,
c =
{
This declares a variable 'b' which is a pointer (*) to a struct "bs" just defined. It then casts the address of function argument 'n', a unit_32_t, to the type pointer-to-bs, and assigns it to 'b', effectively creating a union of uint_32_t and the bit array bs.
A second variable, an actual struct bs, named "c", is then declared, and it is initialized through the pointer 'b'. b->_31 initializes c._00, and so on.
So after "b" and "c" are created, in that order, there's nothing left to do but return the value of "c".
The author of the code, and the compiler, know that after a struct definition ends, variables of that type or related to that type can be created, before ";", and that's why #Thomas Matthews closes with, "The author is letting the compiler reverse the bits."
I know that when we assign a negative value to a unsigned datatype then the two's compliment of it gets stored, that is the maximum value that the datatype can store minus the negative value we have assigned.
To test that, I have written a program which illustrates that, however I am not able to understand the behavior of char datatype.
#include <iostream>
using namespace std;
template<class T>
void compare(T a,T b)
{
cout<<dec<<"a:"<<(int)a<<"\tb:"<<(int)b<<endl; //first line
cout<<hex<<"a:"<<(int)a<<"\tb:"<<(int)b<<endl; //second line
if(a>b)
cout<<"a is greater than b"<<endl;
else
cout<<"b is greater than a"<<endl;
}
int main()
{
unsigned short as=2;
unsigned short bs=-4;
compare(as,bs);
unsigned int al = 2;
unsigned int bl =-4;
compare(al,bl);
char ac=2;
char bc=-4;
compare(ac,bc);
int ai =2;
int bi =-4;
compare(ai,bi);
}
Output is
a:2 b:65532
a:2 b:fffc
b is greater than a
a:2 b:-4
a:2 b:fffffffc
b is greater than a
a:2 b:-4
a:2 b:fffffffc
a is greater than b
a:2 b:-4
a:2 b:fffffffc
a is greater than b
The compare(...) function is called for times with arguments of different datatypes
unsigned short- 2 bytes , therefore -4 gets stored as 65532.
unsigned int - 4 bytes, however as we are trying to typecast it to int while outputting it it is shown -4 in the output, so it is tricking the compiler, however the hex output and the logical comparison result shows that the internal representation is in two's compliment.
char - 1 byte, this is where I am getting confused.
int - 4 bytes, signed datatype, nothing unexpected, normal result.
The question I have to ask is why char is behaving like a signed int?
Even though we are typecasting to int before outputting the first line in the result, why is char showing values similar to int, even when char is 1 byte and int 4 byte. unsigned short showed different value, because its memory requirement was 2 byte.
unsigned int and int is showing same result in the first line of the result, because both are 4 bytes, and the compiler gets tricked successfully, and is acceptable.
But why is char also showing the same value, as if its memory layout was the same as that of int?
And the logical comparison also shows that char does not behave as a unsigned datatype, but a signed one. unsigned datatypes are showing b as greater than one. While char is showing a is greater than b , in terms with signed datatype. Why?
Isn't char 1 byte unsigned datatype?
This is what I learnt when I did a course on C and C++ in by B.Tech degree.
Any explaination would be helpful.
The compiler used is mingw 2.19.1.
Isn't char 1 byte unsigned datatype?
Maybe, maybe not. The signedness of char is implementation-defined.
In your current implementation, it is obviously signed.
And in the output from the compare method, you get four bytes shown, because you cast to int for the output, so the char value -4 gets converted to the int value -4.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?
I was trying to understand the concept of bit fields.
But I am not able to find why the size of the following structure in CASE III is coming out as 8 bytes.
CASE I:
struct B
{
unsigned char c; // +8 bits
} b;
sizeof(b); // Output: 1 (because unsigned char takes 1 byte on my system)
CASE II:
struct B
{
unsigned b: 1;
} b;
sizeof(b); // Output: 4 (because unsigned takes 4 bytes on my system)
CASE III:
struct B
{
unsigned char c; // +8 bits
unsigned b: 1; // +1 bit
} b;
sizeof(b); // Output: 8
I don't understand why the output for case III comes as 8. I was expecting 1(char) + 4(unsigned) = 5.
You can check the layout of the struct by using offsetof, but it will be something along the lines of:
struct B
{
unsigned char c; // +8 bits
unsigned char pad[3]; //padding
unsigned int bint; //your b:1 will be the first byte of this one
} b;
Now, it is obvious that (in a 32-bit arch.) the sizeof(b) will be 8, isn't it?
The question is, why 3 bytes of padding, and not more or less?
The answer is that the offset of a field into a struct has the same alignment requirements as the type of the field itself. In your architecture, integers are 4-byte-aligned, so offsetof(b, bint) must be multiple of 4. It cannot be 0, because there is the c before, so it will be 4. If field bint starts at offset 4 and is 4 bytes long, then the size of the struct is 8.
Another way to look at it is that the alignment requirement of a struct is the biggest of any of its fields, so this B will be 4-byte-aligned (as it is your bit field). But the size of a type must be a multiple of the alignment, 4 is not enough, so it will be 8.
I think you're seeing an alignment effect here.
Many architectures require integers to be stored at addresses in memory that are multiple of the word size.
This is why the char in your third struct is being padded with three more bytes, so that the following unsigned integer starts at an address that is a multiple of the word size.
Char are by definition a byte. ints are 4 bytes on a 32 bit system. And the struct is being padded the extra 4.
See http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86 for some explanation of padding
To keep the accesses to memory aligned the compiler is adding padding if you pack the structure it will no add the padding.
I took another look at this and here's what I found.
From the C book, "Almost everything about fields is implementation-dependant."
On my machine:
struct B {
unsigned c: 8;
unsigned b: 1;
}b;
printf("%lu\n", sizeof(b));
print 4 which is a short;
You were mixing bit fields with regular struct elements.
BTW, a bit fields is defined as: "a set of adjacent bits within a sindle implementation-defined storage unit" So, I'm not even sure that the ':8' does what you want. That would seem to not be in the spirit of bit fields (as it's not a bit any more)
The alignment and total size of the struct are platform and compiler specific. You cannot not expect straightforward and predictable answers here. Compiler can always have some special idea. For example:
struct B
{
unsigned b0: 1; // +1 bit
unsigned char c; // +8 bits
unsigned b1: 1; // +1 bit
};
Compiler can merge fields b0 and b1 into one integer and may not. It is up to compiler. Some compilers have command line keys that control this, some compilers not. Other example:
struct B
{
unsigned short c, d, e;
};
It is up to compiler to pack/not pack the fields of this struct (asuming 32 bit platform). Layout of the struct can differ between DEBUG and RELEASE builds.
I would recommend using only the following pattern:
struct B
{
unsigned b0: 1;
unsigned b1: 7;
unsigned b2: 2;
};
When you have sequence of bit fields that share the same type, compiler will put them into one int. Otherwise various aspects can kick in. Also take into account that in a big project you write piece of code and somebody else will write and rewrite the makefile; move your code from one dll into another. At this point compiler flags will be set and changed. 99% chance that those people will have no idea of alignment requirements for your struct. They will not even open your file ever.