C++ sizeof (Part 2)

  • 2020-11-03 22:32:02
  • OfStack

sizeof operates on basic data types, and the results are deterministic on a particular platform and in a particular compiler. It is slightly more complicated if sizeof is used to calculate the size of the construct types: structures, unions, and classes.

1.sizeof computational structure

Consider the following code:


struct S1
{
char c;
int i;
};
cout<< " sizeof(S1)= " <<sizeof(S1)<<endl;

sizeof(S1) is 8, not sizeof(char)+sizeof(int)=5. This is because the member variables need to be aligned when the structure or class member variables have different types. As stated in Principles of Computer Composition 1, the purpose of alignment is to reduce access instruction cycles and improve CPU memory speed.

1.1 Memory alignment principles

(1) The first address of the structure variable can be divisible by its widest base member type size;

(2) The offset of each member of the structure relative to the first address of the structure is an integer multiple of the size of the member. If necessary, the compiler will add padding bytes between the members;

(3) The total size of the structure is an integer multiple of the size of the widest base member type of the structure. If necessary, the compiler will add padding bytes after the last member.

With these three principles of memory alignment, you can easily handle memory alignment for nested struct types. As follows:


struct S2
{
char c1;
S1 s;
char c2;
};

When looking for the widest basic data type of S2, including the members of its nested structure, the widest data type of S1 is found to be int, so the widest data type of S2 is int. The alignment of S1 s in the structure S2 also follows the first three criteria, so sizeof(S2)=sizeof(char)+pad(3)+sizeof(S1)+1+pad(3)=1+3+8+1+3=16 bytes, where pad(3) represents padding of 3 bytes.

The offset of a member of the structure relative to the first address of the structure is obtained by the macro offsetof(), which is also defined in stddef.h, as follows:


#define offsetof(s,m) (size_t)&(((s *)0)->m)

For example, the offset in S1 is obtained by

size_t pos = offsetof(S1, i); / / pos is equal to 4

1.2 Modify the alignment

1.2.1#pragma pack

#pragma pack(n) Where n is the byte alignment number, its values are 1, 2, 4, 8, 16, and the default is 8. When the structure is aligned,

(1) The offset of a member is an integer multiple of the size of the member itself and the minimum value of n2;
(2) The final size of the structure is an integer multiple of the member size of the widest basic type in the structure and the minimum value of n2.

Consider the following code:


#pragma pack(push) // The current pack Set the stack save 
#pragma pack(2) // Must be used before the structure is defined 
struct S1
{
char c;
int i;
};
struct S2
{
char c1;
S1 s;
char c2
};
#pragma pack(pop) //  Restore to prior pack Set up the 

// or 
#pragma pack(2)
...
#pragma pack()

Therefore, sizeof(S2)=sizeof(char)+pad(1)+sizeof(S1)+1+pad(1)=1+1+6+1=10 bytes.

Note that #pragma pack cannot specify the storage address of the variable, and the first address of the variable defaults to an integer multiple of the maximum base member type size.

1.2.2__declspec(align(#))

VC++ supports ___ 103EN(align(#)), not supported in GNU C++. The value of # is 1 to 8192, which is a power of 2. Use examples are as follows:


__declspec(align(256)) struct TestSize
{
char a;
int i;
};
cout<<sizeof(TestSize)<<endl; // The output 256

___ (align(#)) requires # to be an integer power of 2, with two main effects:
(1) After the structure or class member presses #pragma pack to determine the memory layout, fill the memory at the end so that the entire object size is at least an integer multiple of #.
(2) When acting on a variable, it is mandatory for the compiler to place the variable at a memory location multiple of #. This is important when calling strictly aligned methods such as native API.

1.3 Empty structure

Data types with length 0 are not allowed in C/C++. The size of an "empty structure" (without data members) is not 0, but 1. The "empty structure" variable also has to be stored so that the compiler can only allocate 1 byte of space to it for placeholding. As follows:


struct S3 { };
sizeof(S3); //  The results for 1

1.4 Bit domain structure

Some information is stored in one or more base 2 bits, rather than taking up a full byte. For example, when storing a switch quantity, there are only 0 and 1 states, which can be represented by 1 bit. To save storage space and make processing easier, C also provides a data structure called a "bit field" or "bit segment". A structure containing a bit-domain variable is called a bit-domain structure. The definition form of bit-domain structure:


struct  Bit-field structure name 
{ 
 Type specifier   A domain name : A domain length ;
...
};

Note that the bit field length should not be greater than the bit length of the data type corresponding to the type descriptor.
The main purpose of using bitfields is to compress storage. The general rules are as follows:

(1) If the adjacent bit field fields are of the same type and the sum of their bit widths is less than the sizeof size of the type, the following field will be stored immediately after the first field until it cannot be contained;
(2) If the adjacent bit field fields are of the same type, but the sum of their bit widths is greater than the sizeof size of the type, the following fields will start from the new storage unit with an offset of an integer multiple of their type size;
(3) If the types of fields in adjacent bit fields are different, the specific implementation of each compiler is different. VC++ adopts uncompressed mode, GNU C++ adopts compressed mode;
(4) If bit-field fields are interspersed with non-bit-field fields, no compression will be carried out;
(5) The total size of the whole structure is an integer multiple of the size of the widest member of the basic type;
(6) The bit field can be a bitless domain name, at this time it is only used for filling or adjusting the position, can not be used. Such as:


struct BitFiledStruct
{ 
int a:1;
int :2; // the 2 Bits unavailable 
int b:3;
int c:2;
};

Regarding the size of sizeof of bit-domain structure, the following code is investigated:


#include <iostream>
using namespace std;

struct BFS1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
struct BFS2
{
char f1 : 3;
int i : 4;
char f2 : 5;
};
struct BFS3
{
char f1 : 3;
char f2;
char f3 : 5;
};

int main()
{
cout<<sizeof(BFS1)<<endl;
cout<<sizeof(BFS2)<<endl;
cout<<sizeof(BFS3)<<endl;
}

Run the above program and the output of VC++ and GNU C++ is as follows:

[

//VC++ Output results
2
12
3

//GNU C++ Output
2
4
3

]

By examining the above codes, we can get:

(1) sizeof(BFS1)==2. When adjacent field types are different, in VC++ sizeof (BFS2)=1 +pad(3)+4+1+pad(3)=12, using uncompressed method, the offset of the field variable i needs to be a multiple of 4, and the total size of the field structure BFS2 must be an integer multiple of sizeof(int). In GNU C++, it is sizeof(BFS2)=4. The adjacent bit-field fields have different types, so compressed storage is adopted. The bit-field variable i is stored immediately after the remaining bits of bit-field variable f1, and the bit-field variable f2 is also stored immediately after the remaining bits of bit-field variable i. So the final result sizeof(BFS2)=1+pad(3)=4.

(2) sizeof(BFS3)==3, when non-bit field fields are intersperse, no compression will be generated. In VC++ and GNU C++, the size is 3. If the storage is compressed, then sizeof(BFS3)==2.

2.sizeof computes common objects

The structure is sequential in memory organization, while the common body is overlapping. Each member shares 1 segment of memory, so the sizeof of the whole common body is the maximum value of sizeof per member. A member of a structure can also be a construct type, where the construct type members are considered as a whole. Therefore, in the following example, assuming that sizeof(s) is greater than sizeof(i) and sizeof(c), sizeof(U) is equal to sizeof(s).


union U
{
int i;
char c;
S1 s;
};

3. The class sizeof calculation

Class is a custom construct type commonly used in C++. It consists of data members and member functions. When sizeof is calculated, it is not very different from the structure. Consider the following code:


struct S2
{
char c1;
S1 s;
char c2;
};
0

Note 1 that there is no essential difference between C++ and structs. Structs can also contain member functions, constructors, destructors, virtual functions, and inheritance. However, C is not used in this way. The only difference between a class and a struct 1 is that the default permission for members of the struct is public, while the class is private.

Based on the above points, the output results from the program are reviewed and the following conclusions are drawn:

(1) Similar to structure 1, data types with length of 0 are not allowed to exist in C++. Although the class does not have any members, the object of the class still occupies 1 byte.
(2) The member functions of a class do not affect the space occupied by the class object, and the size of the class object is determined by its data members.
(3) The same needs to be aligned for class 1 and structure 1. See the memory alignment of structure above for specific alignment rules.
(4) If a class contains virtual functions, the compiler inserts a pointer to a table of virtual functions in the class object to facilitate the dynamic invocation of virtual functions.

Therefore, the size of the object of this class is at least 4 bytes larger than if it did not contain virtual functions. If you think about memory alignment, maybe more. If you use alignment between data members, if the class object contains at least one data member and has virtual functions, then the size of the object is at least 8B, which the reader can deduce.

Above is the detailed content of C++ sizeof (below), more about C++ sizeof information please pay attention to other related articles on this site!


Related articles: