The introduction

C language structure in the body of the layout is a platitude problem, the Internet also read some information, some say more vague, some are wrong. I draw lessons from the predecessors of the article, through practice, summed up some rules, if there is a mistake, hope to correct, very grateful.

The actual environment

  • System environmentMacOS Sierra (10.12.4)
  • IDE Xcode (8.3)

An overview of the

Pragma pack: pragma pack: pragma pack: pragma pack: pragma pack: pragma pack: pragma pack

normal

The details of struct byte alignment depend on the specific compiler implementation, but generally follow three guidelines:

  1. The first address of a structure variable can be determined by the size of its widest primitive type member (sizeof).
  2. The offset of each member of a structure relative to the first address of the structureoffsetAre integer multiples of the size of the member, and the compiler adds padding bytes between the members if necessary.
  3. The total size of the structuresizeofIs an integer multiple of the size of the widest base member of the structure. The compiler adds padding bytes after the last member if necessary.

The following demo will explain the above rules:

code

struct student {
  char name[5];
  double weight;
  int age;
};
Copy the code
struct school {
  short age;
  char name[7];
  struct student lilei;
};
Copy the code
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    // insert code here...
    struct student lilei = {"lilei".112.33.20};
    printf("size of struct student: %lu\n".sizeof(lilei));
    printf("address of student name: %u\n",lilei.name);
    printf("address of student weight: %u\n",&lilei.weight);
    printf("address of student age: %u\n",&lilei.age);
    
    struct school shengli = {70."shengli",lilei};
    printf("size of struct school: %lu\n".sizeof(shengli));
    printf("address of school age: %u\n",&shengli.age);
    printf("address of school name: %u\n",shengli.name);
    printf("address of school student: %u\n",&shengli.lilei);
  }
  return 0;
}
Copy the code

The output

Explain rules

  1. When the compiler makes space for a structure, it first finds the widest base datatype in the structure and then looks for the location of the memory address divisible by the base datatype as the first address of the structure. (In this demostruct schoolcontainsstruct student, so the widest basic data type isdouble.sizeof(double)for8.1606416152/8 = 200802019.1606416112/8 = 200802014).
  2. Before clearing space for each member of a structure, the compiler checks whether the offset of the first address of the pre-created space relative to the first address of the structure is an integer multiple of the size of this member. If so, it places this member. If not, it fills bytes between this member and the previous member to reach the integer multiple. This is to move the first address of the pre-opened space a few bytes back (whystruct student weightThe first address of the member is1606416160Rather than1606416157Char arrays are 1 byte aligned, and the structure members are stored at integers multiple of their own maximum internal size, ** for examplestruct aAre there instruct bMembers,bAre there inChar, int, doubleWait for the members, thatbThe starting location of the storage should be an integer multiple of 8. throughstruct schoolYou can see the member memory distribution,school.nameThe first address of the1606416114Rather than1606416119.school.studentThe first address of the1606416128That can be8Divisible, cannot be divided by24Aliquot).
  3. The total size of the structure consists of padding bytes. The last member of the structure must satisfy the first two, but must also satisfy the third, or must fill a certain number of bytes at the end of the structure to satisfy the requirementstruct studentThe number of occupied bytes is24Rather than20).

Distribution of memory

extension

Careful friends may find that the value of &shengli.lilei(equivalent to shengli.lilei.name) is not equal to lilei.name, In other words, the member of struct student lilei and struct student lilei in struct school shengli do not refer to the same memory space, but to a new memory space created by value copy. That is, structs are value types rather than reference type data structures. Also, the memory address shows that the memory space of the two structure variables is allocated consecutively on the memory stack.

A domain

The main purpose of structs using bit-fields is to compress storage. Bit-field members cannot be sizeof individually. C99 states that int,unsigned int, and bool can be bit-field types, but the compiler almost always extends this to allow other types to exist. Structs that contain bit-field fields must follow the following four rules in addition to the three above:

  1. If the word ends of adjacent bit fields have the same type and the sum of bit widths is less than the typesizeofSize, the latter field will be stored next to the former until it can no longer accommodate it.
  2. If adjacent bit-field fields are of the same type, but the sum of the bit widths is greater than the typesizeofSize, the latter field will start from the new storage unit and its offset will be an integer multiple of its type size.
  3. If the type of the adjacent bit-field field is different, the specific implementation varies from compiler to compiler. VC6 uses uncompressed mode, and dev-C ++ uses compressed mode.
  4. If bit-field fields are interspersed with non-bit-field fields, no compression is performed.

The following demo will explain the above rules:

code

typedef struct A {
  char f1:3;
  char f2:4;
  char f3:5;
  char f4:4;
}a;
Copy the code
typedef struct B {
  char  f1:3;
  short f2:13;
}b;
Copy the code
typedef struct C {
  char f1:3;
  char f2;
  char f3:5;
}c;
Copy the code
typedef struct D {
  char f1:3;
  char :0;
  char :4;
  char f3:5;
}d;
Copy the code
typedef struct E {
  int f1:3;
}e;
Copy the code
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    // insert code here... 
    printf("size of struct A: %lu\n".sizeof(a));
    printf("size of struct B: %lu\n".sizeof(b));
    printf("size of struct C: %lu\n".sizeof(c));
    printf("size of struct D: %lu\n".sizeof(d));
    printf("size of struct E: %lu\n".sizeof(e));
  }
  return 0;
}
Copy the code

The output

Explain rules

  1. struct AAll bit-field member types arechar, the first byte can only holdf1andf2.f3Storage starts from the next byte, the second byte cannot accommodatef4, sof4It’s also stored from the next byte, sosizeof(a)The results for3.
  2. struct BThe median field member type is different and has been compressed, thereforesizeof(b)The results for2(Uncompressed mode is not validated, sorry).
  3. struct CThere are non-bit-field type members between the median field members, which are not compressed, sosizeof(c)So that’s 3.
  4. struct DIs there a named domain member in,char f1:3Account for3abit.char :0Move to the next1The unit of movement depends on the bit-field type,shortMove to the next2Bytes,intMove to the next4Bytes),char :4Account for4abitAnd then can’t accommodatechar f3:5So save it1Bytes, sosizeof(d)The results for3.
  5. Some of you might wonder, whysizeof(e)The results for4It should not just occupy1A byte? Don’t forget the aboveRule 3.

Matters needing attention

  1. The address of the bit-field is not accessible, so the & operator is not allowed for the bit-field. You cannot use Pointers to bitfields or arrays of bitfields (arrays are special Pointers).
  2. Bitfields cannot be returned as a result of a function.
  3. The bitfield is in units of the defined type, and the length of the bitfield cannot exceed the length of the defined type. Such as the definitionint a:33It’s not allowed.
  4. A bitdomain may not specify a bitdomain name, but nameless bitdomains cannot be accessed. Nameless bit fields can only be filled or repositioned, depending on the type. For example,char:0Indicates that the entire bit field is pushed back by one byte, that is, the next bit field after the nameless bit field is stored from the next byte, and so onshort:0andint:0Represents the entire bitfield pushed back by two and four bytes, respectively. When the length of the vacancy field is a specific value N (e.gint:2), this variable is used for N bits only.

Pragma Pack preprocessor macros

The compiler’s #pragma pack directive is also used to adjust the alignment of structures, with slightly different compiler names and usages. Using the #pragma pack(n) directive, the compiler will align n bytes with values 1, 2, 4, 8, 16, default 8, and uncustomize byte alignment using the #pragma pack() directive. If set to #pragma Pack (1), the structure has no padding bytes, enabling “seamless storage” of space, which is friendly and compatible for transferring data across platforms. The structure contains the #pragma Pack preprocessor macro, which follows the following two rules in addition to the three above:

  1. If n is greater than or equal to the number of bytes used by the member type, the offset must meet the default alignment. If n is less than the number of bytes used by the member type, the offset must be a multiple of n and does not meet the default alignment. The offset of a structure member should be the smallest of the two, as follows:

    offsetof(item) = min(n, sizeof(item))
  2. For the total size of the structure, if n is greater than the number of bytes taken up by all member types, then the total size of the structure must be a multiple of the number of bytes taken up by the largest member, otherwise it must be a multiple of N.

usage

#pragma pack(push)  // Set the current alignment
#pragma pack(pop)   // Remove the current alignment when packing stack is removed
#pragma pack(n)     //n=1,2,4,8,16 saves the current alignment and sets the alignment to n bytes
#pragma pack()      // Pack (pop)
#pragma pack(push,n)Pack (push) + pack(n)
Copy the code

code

#pragma pack(4)

typedef struct F {
  int f1;
  double f2;
  char f3;
}f;

#pragma pack()
Copy the code
#pragma pack(16)

typedef struct G {
  int f1;
  double f2;
  char f3;
}g;
Copy the code
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    // insert code here...
    printf("size of struct D: %lu\n".sizeof(f));
    printf("size of struct E: %lu\n".sizeof(g));
  }
  return 0;
}
Copy the code

The output

Explain rules

  1. struct FThe alignment is set to4.min(4, sizeof(int)) = 4.f1Account for4The offset is0.min(4, sizeof(double)) = 4.f2Account for4The offset is4.min(4, sizeof(char)) = 1.f3Account for1The offset is12And finally the whole structure satisfiesRule 3.sizeof(f) = 16.
  2. struct GThe alignment is set to16Is larger than any member type in the structuresizeof(f) = 24.

conclusion

Bitfields and **#pragma pack preprocessor macros have their own rules to follow as long as they follow the three guidelines **. Structure members are arranged in order to save as much space as possible.

Refer to the link

Blog. Sina. Cn/dpool/blog /… C.biancheng.net/cpp/html/46… Hubingforever.blog.163.com/blog/static…