This question already has answers here:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?
(13 answers)
Closed 3 years ago.
I am trying to understand how exactly structures work in C. More precisely, the size of a structure.
When I print the size of the following structure:
struct Test {
char tab[14];
};
I have the output 14.
But the size of
struct Test2{
uint16_t a;
uint16_t b;
uint16_t c;
uint64_t d;
};
is equals to 16.
I read in a previous post that there is a "padding added to satisfy alignment constraints". Then why this padding isn't apply to the first example ?
Padding is not allowed to be applied in the middle of an array (else pointer arithmetic would break). So all the elements of tab are contiguous.
Furthermore the address of tab[0] needs to the be the same as the address of the corresponding instance of Test; i.e. padding is not allowed at the beginning of a struct.
There could, however, be padding at the end. In your case though, there isn't.
Padding has to do with the alignment at which an object must be arranged. Your first structure only has a char member, and so it can be positioned at any address. Thus there is no padding necessary, since the next element in an array of such beasts may follow directly after.
For other structures that contain members with different alignment constraints, things become more complicated. They must be padded internally to accomodate different alignment requirements, and there can be padding at the end, if the overall alignment is at odds with the size.
Related
As my known, the size of the structure depends upon what compiler is used and the compiler may add padding for alignment requirements.
On a 64-bit system. I have test for 2 examples:
Example 1:
struct
{
uint8 a;
uint32 b;
uint8 c;
}ABC;
size of(uint8 a) == 1 bytes + 3 bytes padding
size of(uint32 b) == 4 bytes + 0 padding
size of(uint8 c) == 1 bytes + 3 padding
==> So, size of(ABC) = 12 bytes.
Example 2:
struct
{
uint8 a;
uint16 b;
uint8 c;
}ABC;
size of(uint8 a) == 1 bytes + 1 bytes padding
size of(uint16 b) == 2 bytes + 0 padding
size of(uint8 c) == 1 bytes + 3 padding
==> So, I assumed size of(ABC) = 8 bytes.
However, the compiler return size of(ABC) = 6 bytes.
Why does the size of(ABC) = 6 bytes in Example 2 instead of 8 bytes as my understanding?
The compiler tries to align objects of structure types such a way that the data member with strictest alignment would be appropriately aligned.
In this structure declaration
struct
{
uint8 a;
uint16 b;
uint8 c;
}ABC;
the data member with the strictest alignment is the data member b. Its address should be aligned by two bytes. So the data member a is padded by one byte. To make the address of an object of the structure to be aligned by 2 bytes the data member c is also padded with one byte.
The compiler may add padding for alignment requirements. Note that this applies not only to padding between the fields of a struct, but also may apply to the end of the struct (so that arrays of the structure type will have each element properly aligned).
For example:
struct foo_t {
int x;
char c;
};
Even though the c field doesn't need padding, the struct will generally have a sizeof(struct foo_t) == 8 (on a 32-bit system - rather a system with a 32-bit int type) because there will need to be 3 bytes of padding after the c field.
Note that the padding might not be required by the system (like x86 or Cortex M3) but compilers might still add it for performance reasons.
size of(uint8 c) == 1 bytes + 3 padding`
==> So, I assumed size of(ABC) = 8 bytes.
There is no reason to add three bytes of padding. Because the alignment requirement of uint8 (presumably really uint8_t or equivalent) is one byte and the alignment requirement of uint16 (presumably really uint16_t or equivalent) is two bytes, the alignment requirement of the full structure is the maximum of those, two bytes. Having used one byte for uint8 a, a byte for padding to make uint16 b aligned, two bytes for uint16 b, and a byte for uint8 c, the size of the structure up to that point is five bytes. Then only one more byte is needed to make it a multiple of its alignment requirement, so the total is six bytes.
The rules typically used to layout a structure are:
Each member in the structure has some size s and some alignment requirement a.
The compiler starts with a size S set to zero and an alignment requirement A set to one (byte).
The compiler processes each member in the structure in order:
Consider the member’s alignment requirement a. If S is not currently a multiple of a, then add just enough bytes to S so that it is a multiple of a. This determines where the member will go; it will go at offset S from the beginning of the structure (for the current value of S).
Set A to the least common multiple1 of A and a.
Add s to S, to set aside space for the member.
When the above process is done for each member, consider the structure’s alignment requirement A. If S is not currently a multiple of A, then add just enough to S so that it is a multiple of A.
The size of the structure is the value of S when the above is done.
Additionally:
If any member is an array, its size is the number of elements multiplied by the size of each element, and its alignment requirement is the alignment requirement of an element.
If any member is a structure, its size and alignment requirement are calculated as above.
If any member is a union, its size is the size of its largest member plus just enough to make it a multiple of the least common multiple1 of the alignments of all the members.
For elementary types (int, double, et cetera), the alignment requirements are implementation-defined and are usually largely determined by the hardware. On many processors, it is faster to load and store data when it has a certain alignment (usually when its address in memory is a multiple of its size). Beyond this, the rules above follow largely from logic; they put each member where it must be to satisfy alignment requirements without using more space than necessary.
Footnote
1 I have worded this for a general case as using the least common multiple of alignment requirements. However, since alignment requirements are always powers of two, the least common multiple of any set of alignment requirements is the largest of them.
#include <stdio.h>
struct Obj {
char a;
uint32_t b;
uint8_t c;
uint64_t d[0];
};
struct Obj1 {
uint64_t d[0];
};
int main() {
uint64_t a[0];
printf("%d\n", sizeof(Obj)); // 16
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(Obj1)); // 16
//cout << sizeof(Obj) << endl; // 16
//cout << sizeof(a) << endl; // 0
//cout << sizeof(Obj1) << endl; // 0
}
As shown above, why the uint64_t variable within the struct will not be stacked right back after the uint8_t, and more peculiar is that the empty array has zero sizes outside the structure.
It's actually an interview question. The explanation goes like this, though still can't understand.
If there is no fourth field, it should be 4+4+4=12, plus the fourth
field is 16, the fourth field does not occupy space, but it will tell
the compiler to align by 8 bytes
This usage is often used in the kernel, for example, the following can
be directly accessed by subscript
Obj o1; uint64_t array[1024]; // In memory, array immediately follows
o1 o1.d[123]; // can access the elements of array
As noted by comments, this may be only applied in C instead of C++. SO I changed the code to C version.
First, your code is undefined behavior. From Arrays
p1 emphasis mine:
In a declaration T D where D has the form
D1 [ constant-expressionopt ] attribute-specifier-seqopt
and the type of the contained
declarator-id in the declaration T D1 is “derived-declarator-type-list
T”, the type of the declarator-id in D is
“derived-declarator-type-list array of N T”. The constant-expression
shall be a converted constant expression of type std::size_t
([expr.const]). Its value N specifies the array bound, i.e., the
number of elements in the array; N shall be greater than zero.
The size of an array has to be greater then 0.
As for gcc compiler extension that allows for zero sized arrays in C code and happens to be also supported in C++ code, the gcc documentation states:
Although the size of a zero-length array is zero, an array member of this kind may increase the size of the enclosing type as a result of tail padding.
Which seems to be happening in your code.
This interview question probes a candidates knowledge of alignment and certain semantics in both the C standard and specific implementations.
The char a member has size one (byte) and alignment requirement one (byte).
The uint32_t b member has size four and commonly has an alignment requirement of four bytes. In order to place it on an address that is a multiple of four bytes, the compiler has to include three unused bytes after a and before b, which are calling padding bytes. Up to this point, the structure requires 1+3+4 = 8 bytes.
The uint8_t c member has size one and alignment requirement one. Up to this point, the structure requires 9 bytes.
With uint64_t d[0], the behavior is not defined by the C standard. However, unless the interviewer has specified this is a question about strictly conforming standard C, answering that the behavior is undefined is inadequate, as there is more to C than just the standard. There is also conforming (but not strictly conforming) C and non-standard variants of C. GCC supports a well-known extension in which the last member of a structure may be declared as an array with zero elements, and the interviewer expects the questioner to be aware of this.
When such a structure is used, the program must allocate sufficient space for whatever array elements it wishes to use, by adding such space to the request made with malloc or a similar memory-allocation routine. For example, to allocate space for the base structure plus 13 elements, one might use malloc(sizeof(struct Obj) + 13 * sizeof(uint64_t)).
Commonly, a uint64_t has an alignment requirement of eight bytes. Whatever its alignment requirement is, the compiler will add sufficient unused bytes between members c and d to ensure d has the proper alignment. If it does require eight-byte alignment, then seven bytes must be inserted after c, so the structure size up to the start of d will be 1+3+4+1+7 = 16 bytes.
This question already has answers here:
Size of struct containing double field
(5 answers)
Why padding are added, if char comes after int?
(4 answers)
Closed 4 years ago.
sizeof(x) returns 2 for the structure below
struct s {
short c;
} x;
but for the structure
struct s {
short c;
char a;
} x;
sizeof(x) returns 4, Why?
The second one gets one padding byte (assuming short is 2 bytes long and char 1 byte long). Shouldn't the first structure have 2 padding bytes then (and thus be 4 bytes long)?
The predominant use of padding is to align structure members as required by the hardware (or other aspects of the C implementation). An algorithm for laying out data in a struct is in this answer.
To answer the question in your title, when do structures not have padding: A structure does not require padding for alignment if each member’s alignment requirement is a divisor of the total size of all preceding members and of the total size of all members. (A C implementation may still add padding for reasons other than alignment, but that is a bit unusual.)
For your examples, let’s suppose, in a C implementation, short is two bytes in size and requires two-byte alignment. By definition, char is one byte and requires one-byte alignment.
Then, in struct s {short c;}:
c is put at the beginning of the struct. There is never any padding at the beginning.
If we make an array of these struct, the next struct s will begin two bytes beyond the first, and its member c will still be at a multiple of two bytes, so it is aligned correctly.
Therefore, we do not need any padding to make this work.
In contrast, in struct s {short c; char a;}:
c is put at the beginning.
a is put two bytes after c. This is fine, since a only requires one-byte alignment.
If we do not add any padding, the size of the struct is three bytes. Then, if we make an array of these struct, the next struct s will begin three bytes from the start.
In that second struct s, the c member will be at an offset of three bytes. That violates the alignment requirement for short.
Therefore, to make this struct work, we must add one byte of padding. This makes the total size four bytes. Then, in an array of these struct, all the members will be at boundaries required by their alignment.
Even if you declare just a single object of a structure, as in struct s {short c; char a;} x;, a structure is always laid out so it can be used in an array.
The first structure has one element of size 2 (assuming short has size 2 on your system). It is as good as directly having an array of short directly.
The second structure is a special thing: access to short variables is best done on even addresses. If we hadn't padding, we had the following:
struct s arr[5]; // an array
void * a = arr; // needed to reference it
Then,
arr[0].c is at a.
arr[0].a is at a + 2 bytes.
arr[1].c is at a + 3 bytes (!).
arr[1].a is at a + 5 bytes (!).
As it is preferrable to have arr[1].c at an even address, we add padding. Then,
arr[1].c is at a + 4 bytes.
arr[1].a is at a + 6 bytes.
According to Wikipedia:
the last member is padded with the number of bytes required so that the total size of the structure should be a multiple of the largest alignment of any structure member
In my understanding, it means that in the following:
struct A {
char *p; // 8 bytes
char c; // 1 byte
};
struct B {
struct A a; // 16 bytes
char d; // 1 bytes
};
Struct A will have a size of 16 bytes, and struct B will have a size of 24 bytes.
The common explanation is that arrays of A should have their elements accessible at the address of the array plus the index times the size of A.
But I fail to see why that is the case. Why could we not say A has size 9 and B has size 10 (both with 8 bytes alignment), and use a special "array-storage" size when indexing into an array?
Of course, we'd still store those types in arrays in a way compatible with their alignment (using 16 bytes to store each B element). Then, we'd simply compute element addresses by taking into account their alignment, instead of considering their size alone (the compiler can do that statically).
For example, we could store 64 objects in a 1Kb bytes array of B's, instead of only 42.
In each translation unit of C, sizeof(T) is the same, regardless of the context of T. Your proposal would introduce at least two values for sizeof(T): one for arrays of T and a different one for individual objects of T. This basically introduces context-dependence into the sizeof operator. It is incompatible with how C handles pointers, arrays, and addresses of objects.
Consider the following:
void zero_A(struct A *a) { memset(a,0,sizeof(*a)); }
/* ... */
struct A single;
struct A several[3];
struct B b;
b.d = 3;
zero_A(&b.a);
zero_A(&single);
zero_A(several+1);
Under your proposal, zero_A would have to know whether the pointer it was passed pointed to struct A in an array context (where sizeof(*a) == 16) or struct A outside of an array context (where sizeof(*a) == 9). Standard C doesn't support this. If the compiler guessed wrong, or the information was lost (eg: in a round-trip through a volatile struct A *), then zero_A(&single) would invoke undefined behavior (by writing past the bounds of single), and zero_A(&b.a) would overwrite b.d and also invoke undefined behavior.
Tightly packing structs into an array is a relatively uncommon requirement, and adding context-dependence to sizeof would introduce a lot of complications to the language, its libraries, and ABIs. There are times you need to do this, and C gives you the tools you need: memcpy and unions.
typedef struct {
/*has 15 pointers*/ // ==> 15 x 4 = 60 bytes ;
uint16_t; // ==> 2 bytes
uint16_t array[];
} dummy_struct;
CASE-A) sizeof(dummy_struct) returns 64 bytes
CASE-B) while if I try to print,
((int) &(((dummy_struct *)(0))->array[0])) this prints 62 bytes.
This prints 62 bytes as well: ((int) &(((dummy_struct *)(0))->array))
I don't understand why there is a change in value? Shouldn't sizeof() return 62 as well?
If there's padding of those 2 extra bytes, shouldn't it happen before the flexible-length member in the struct? If that's the case, shouldn't CASE-B print 64 too instead of 62?
EDIT:
typedef struct {
uint32_t temp;
uint8_t array[][6];
} dummy2;
printf("%d %d\n", sizeof(dummy2), offsetof(dummy2, array)); // PRINTS 4 4
printf("%d \n",((int) &(((dummy2 *)(0))->array[0]))); // PRINTS 4
How come this same effect is not happening with the previous example? Padding doesn't seem to happen here. So, the possible reason for the previous example is padding happening after the flexible-size member?
The sizeof returns the size of the entire structure, while your other two expressions return the offset of the array member, which happens to be two bytes before the end of the struct. You can get the same result with the offsetof, like this:
offsetof(dummy_struct, array) // This is 62
Demo on ideone.
The padding probably does not come before the dummy array, because the dummy array doesn't need it, since the type is uint16_t, which presumably only needs 2-byte alignment, not 4.
That said, I bet this is highly implementation- and target-dependent.
First, this is not a VLA, it's a flexible array member. A VLA can only be a automatic variable (i.e a regular variable on the stack), not the member of a struct. This is because lots of things the compiler does depend on knowing the size of the elements it manipulates.
Based on your comment, it looks like you're on a 32-bit platform which has 4-byte alignment requirement for pointers and 2-byte alignment for 16-bit integers. That's why you get a sizeof of 64 bytes. For sizeof purpose, it is like there is no flexible array member in your struct, so we can ignore it for now. The flexible array member is a "fake" member, it does not take any space.
The compiler adds 2 bytes of padding after the uint16_t to guarantee that in an array of dummy_struct the pointers have 32-bit alignment. Imagine an array of 2 dummy structs, if there is no padding, the first pointer would start right after the uint16_t and it would not be aligned on a 32-bit boundary.
Note that you can usually force the compiler not to pad, but there is no portable way for doing this. With gcc for example, you can use attribute ((packed)) on the struct. If you do that, sizeof(dummy_struct) will return 62 bytes.
For B, you're basically printing the offset of the flexible array member. The C99 standard says:
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.
So when you use ->, the FAM behaves like it's a regular array with the biggest size it could have without changing the size reported by sizeof (64 bytes). In this case, you can only fit a single 16-bit integer without changing the size. In this imaginary struct, there would be no padding and the offset of the imaginary array of one element would be 62 (right after the uint16_t). So that's why you get 62.
If your FAM was a int32_t, you could fit 0 element without changing the size (you'd need the 2 bytes of padding to do the alignment). The standard says that this behaves like an array of size 1. So your FAM will be at offset 64 like what sizeof return.
Let me re-iterate: if you simply change int16_t array[] into int array[], A) will still return 64 but B) will now return 64, not 62.
This is also what happens in your second example. Your FAM is now an array of pointers. You're on a 32-bit platform and your struct is 4. You can fit 0 element in the FAM without changing the size. So its offset is 4.
In the absence of the flexibly array member, assuming the system requires 32-bit alignment for 32-bit integers, the size of the struct would be 64 bytes, including two bytes of padding at the end. If one were to add a fixed-size array holding an even number of 16-bit values, the size of the struct would grow by the size of the added items.
While one could make the argument that code which uses a struct with a flexible array member will know if it needs to adjust the computed size of an item to ensure that consecutively-stored items have proper alignment, and thus sizeof() should report the size of the portion of the structure prior to the flexible member, having the addition of an array reduce the reported size of a structure would be "surprising" behavior. Arguably, the proper way for the language to handle the situation would be to have sizeof require the specification of a second parameter giving the number of items in the array in which case sizeof(dummy_struct,1) would yield 64 but sizeof(dummy_struct,2) would yield 68.