Detailed explanation of structure, enumeration, and union error-prone points in C language supplement

Table of contents

I. Introduction

2. Text

2.1 Structure

2.11 Basic knowledge of structure

2.12 Structure memory alignment (a popular test point)

Structure memory alignment rules:

2.13 Why do we need structure memory alignment?

2.14 Modify the default alignment number

2.15 Function parameter passing

2.16 bit segments

2.17 Cross-platform problems in bit segments

2.2 Enumeration

2. 21 Advantages of Enumeration

2.3 Consortia

3. Conclusion


I. Introduction

    In this section, we will share the knowledge of structures, enumerations, and associations with our friends, hoping to bring benefits to our friends.

2. Text

2.1 Structure

2.11 Basic knowledge of structure

      A structure is a collection of values ​​called member variables. Each member of the structure can be a variable of a different type.
declaration of structure
struct tag
{
     member - list ;
} variable - list ;
For example to describe a student:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢

special statement (anonymous statement)
//匿名结构体类型
struct
{
     int a;
     char b;
     float c;
}x;
struct
{
    int a;
   char b;
   float c;
}a[20], *p;

 It is worth noting that the anonymous structure is only valid for the variables at the time of creation, such as: x, a[20], *p are anonymous, and the anonymous structure cannot be used to create a new structure variable later.

2.12 Structure memory alignment (a popular test point)

      Next, let's take a closer look: structure memory size 

//计算出结构体内存大小
struct S1
{
   char c1;  //1字节
   int i;        // 4字节
   char c2;  //  1字节
};
// 结果: 12个字节

Why is this?

Let's find out here

Structure memory alignment rules:

  • 1. The first member is at the address whose offset from the structure variable is 0 .
  • 2. Other member variables should be aligned to an address that is an integer multiple of a certain number (alignment number) .
Alignment = Compiler's default alignment and the smaller value of the member size .
The default value in VS is 8, and the default alignment number of linux is itself, such as: int alignment number is 4
  • 3. The total size of the structure is an integer multiple of the maximum alignment number (each member variable has an alignment number).
  • 4. If a structure is nested, the nested structure is aligned to an integer multiple of its own maximum alignment number , and the overall size is equal to the maximum alignment number (including the alignment number of the nested structure) Integer multiples.

 Here we analyze the previous example:

 How to detect it?

Here I will introduce a macro-offsetof,    whose function is to return the offset of the memory where the structure member is located .

Header file: #include<stddef.h>

 use:

#include<stddef.h>

#include<stdio.h>

struct S3
{
    double d;// 对齐数 8
    char c; //  对齐数1
    int i; //   对齐数4
}s3;

int  main()

{

   printf("%u\n", offsetof(struct S4, c1));    //  0
    printf("%u\n", offsetof(struct S4, s3));   //   8
    printf("%u\n", offsetof(struct S4, d));     //    12
return 0;

}

The above example only practices the first three principles. Let's take another example of nested structures:

//练习4-结构体嵌套问题
struct S3
{
  double d;  // 对齐数 8
  char c;      //  对齐数1
  int i;          //   对齐数4
};
//  S3 的大小: 16个字节
struct S4
{
  char c1;    
  struct S3 s3;  // 对齐数 8 , 16字节
  double d;       //  对齐数8
};
printf("%d\n", sizeof(struct S4));
//  结果:  32

 

As shown in the picture:

 2.13 Why do we need structure memory alignment?

Much of it is explained this way:
  • 1. Platform reason ( transplant reason ) :
Not all hardware platforms can access arbitrary data at any address; some hardware platforms can only fetch certain types of data at certain addresses, otherwise a hardware exception is thrown.
  • 2. Performance reasons :
Data structures ( especially stacks ) should be aligned on natural boundaries as much as possible. The reason is that in order to access unaligned
memory, the processor requires two memory accesses; aligned memory accesses require only one access.

In general:
The memory alignment of the structure is the practice of exchanging space for time .

 So how do we reduce space usage?

Let members who occupy a small space gather together as much as possible.

2.14 Modify the default alignment number

       We have seen the #pragma preprocessing directive before, and here we use it again to change our default alignment.

like: 


#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char c1;  //  1   8 -> 1
    int i;        //   4  8->  4
    char c2;  //  1   8->  1
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)  //设置默认对齐数为8
struct S2
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
   //输出的结果是什么?
   printf("%d\n", sizeof(struct S1));
   printf("%d\n", sizeof(struct S2));
   return 0;
}

 

 It is worth noting that the default alignment number modified by #pragma pack() is generally 2 ^ n, and there are rarely odd numbers.

2.15 Function parameter passing

     When a function passes parameters, the parameters need to be pushed onto the stack, which will cause system overhead in time and space. If a structure object is passed, the structure is too large , and the system overhead of pushing the parameters on the stack is relatively large, which will lead to a decrease in performance
in conclusion:
When a structure is passed as a parameter, the address of the structure must be passed .

2.16 bit segments

      After the structure is finished, we have to talk about the ability of the structure to realize the bit segment.
what is bit segment
      Bit field declarations and structures are similar, with two differences:
1. The members of the bit field must be int , unsigned int or signed int .
2. There is a colon and a number after the member name of the bit field, indicating the occupied bit.
for example:
struct A
{
   int _a:2;   // 2 bit
   int _b:5;   // 5bit
   int _c:10;
   int _d:30;
};  // 问那  struct A的大小是多少?结果是  8个字节

 

bit segment memory allocation
1. The members of the bit field can be int, unsigned int, signed int or char (belonging to the integer family) type
2. The space of the bit field is opened up in the form of 4 bytes ( int ) or 1 byte ( char ) according to the need.
3. The bit segment involves many uncertain factors , the bit segment is not cross-platform, and programs that focus on portability should avoid using the bit segment .
So how is it allocated in memory?
//一个例子
struct S
{
   char a:3;
   char b:4;
   char c:5;
   char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//空间是如何开辟的?

 Parse:

Idea: First, when a byte is full or not enough, create another byte space.

 Here we need to distinguish from the previous big and small endian byte order. This time, the moving unit is bit, and the latter unit is byte.

2.17 Cross-platform problems in bit segments

1. It is uncertain whether the int bit field is regarded as a signed number or an unsigned number .
2. The maximum number of bits in a bit field cannot be determined. ( Maximum 16 for 16 -bit machines , maximum 32 for 32 -bit machines , written as 27 , there will be problems in 16 -bit machines.
3. Whether the members in the bit segment are allocated from left to right in memory or from right to left has not yet been defined.
4. When a structure contains two bit segments, and the second bit segment is too large to accommodate the remaining bits of the first bit segment, it is uncertain whether to discard or utilize the remaining bits.
Specifically used in the network protocol, interested students can learn about it.
Summarize:
     Compared with the structure, the bit segment can achieve the same effect, but it can save space very well, but there are cross-platform problems.

2.2 Enumeration

     It is to list the possible results of a certain situation together, and when using it, directly use the data that is correct for the situation.

like:

12 months can be one, one list

Gender can also be listed one by one

Use as follows:

enum Color//颜色
{
   RED=1,
   GREEN=2,
   BLUE=4
};
The content in {} is the possible value of the enumeration type, also called enumeration constant .
    If it is not initialized, it starts from 0 by default and increments by 1 at a time. Of course, it can also be assigned an initial value when it is defined. (Note: Like #define modified constants, the data is placed in the constant area, and once defined, it is not allowed to be modified .)
like:
enum  a
{
   c,   // 默认值为 0
   b,   //                1
   k = 200, //赋值为200
   z,        //  由前+ 1,为 201
   t          //  同理        202
};

2. 21 Advantages of Enumeration

  • Why use enums?
    We can use #define to define constants, why use enums?
Advantages of enums:
  • 1. Increase the readability and maintainability of the code (less #define)
  • 2. Compared with the identifier defined by #define , the enumeration has type checking , which is more rigorous.
(In c++, when enumeration constants are assigned, the data on the right side will be enumerated to determine whether it is an enumeration member)
  • 3. Prevent naming pollution (the scope can be limited by enumeration name to avoid renaming)
  • 4. Easy to debug (you can debug and find the corresponding value, unlike the #define definition, which is replaced during precompilation, and it is not easy to trace the source)
  • 5. Easy to use, multiple constants can be defined at a time

2.3 Consortia

union type definition
    Union is also a special custom type. The variable defined by this type also contains a series of members. The characteristic is that these members share the same space (so union is also called union). for example:
#include<stdio.h>
union p1
{
    char i;
    int z;
}a;
int main()
{
    printf("%p\n", &(a.i));
    printf("%p\n", &(a.z));
    return 0;
}

 

 result:

joint features
    The members of the union share the same memory space, so the size of such a joint variable is at least the size of the largest member (because
Union must be able to save at least the largest member).
  • The memory size of the union should also follow the structure memory alignment principle.
    Interview question: Imagine how to use the characteristics of the joint to judge the big endian and little endian of the machine ?
#include<stdio.h>
union p1
{
	char i;
	int z;
}a;
int main()
{
	a.i = 5;
	a.z = 1;
    a.i == 1 ? printf("小端") : printf("大端");
	return 0;
}

The result is little endian, using char i can only read the first byte of data, it can be judged that the value modified by int z exists in the first byte, so the machine is little endian.

3. Conclusion

    This section ends here, thank you friends for browsing, if you have any suggestions, please comment in the comment area.

 

Guess you like

Origin blog.csdn.net/qq_72112924/article/details/130005582