Detailed explanation of custom types (structure, enumeration, union)

Table of contents

1. Structure

 1.1 Understanding of structure:

1.2 Declaration of structure

1. Declare the structure type first, and then define the variables of this type

2. Define while declaring the type

1.3 Special declarations for structures 

1.4 Self-reference of structure

1.5 Initialization and access of structures

1.6 Structure memory alignment 

 1.7 Modify the default alignment number

 1.8 Structure parameter passing

Second, bit segment 

2.1 What is a bit segment

2.2 Memory allocation for bit segments

 2.3 Cross-platform problems of bit segment

Three, enumeration

3.1 Definition of enumeration type

 3.2 Use of enumeration

3.3 Advantages of enumeration 

  4. Union (community)

4.1 Definition of Consortium

4.2 Characteristics of the consortium

4.3 Calculation of joint size


Preface: C language provides some data types that have been defined by the system, such as: int, float, char, etc., users can use them to define variables in the program to solve general problems, but the problems we have to deal with are often more complicated. Only the types provided by the system cannot meet the needs of applications. C language allows users to create some data types according to themselves and use them to define variables.

1. Structure

 1.1 Understanding 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.

The general form of declaring a struct type is:

struct structure name

        {

        //Member variables are used to describe the relevant properties of the structure object

        member list

        };

Note: The name of the structure type is a combination of a keyword struct and the structure name (for example: struct Student). The structure name is specified by the user, also known as "struct mark", to distinguish it from other structure types. The Student in the above structure is the structure name.

           Inside the curly braces are the subitems contained in the structure, called structure members. In the above example, num, name, age, sex, etc. are all members.

1.2 Declaration of structure

1. Declare the structure type first, and then define the variables of this type

struct Student
{
	int num;			
	char name[20];		
	char sex[5];		
	int age;			
};


int main()
{
    struct Student s1;
    struct Student s2;
    return 0;
}

2. Define while declaring the type

struct Student
{
	int num;			
	char name[20];		
	char sex[5];		
	int age;			
}s1,s2;

The general form of this method definition is

struct structure name

{

        member list;

} variable name list;

 Note: Declare the type and define the variable together, and you can directly see the structure of the structure, which is more intuitive. It is more convenient to use this method when writing a small program, but when writing a large program, it is often required to declare the type and to Variable definitions are placed in different places to make the program structure clear and easy to maintain.

Next, we use the diagram to strengthen the understanding of structure types and structure variables

1.3 Special declarations for structures 

When declaring a structure, it can be incomplete. Anonymous struct types, for example:

//匿名结构体类型
struct
{
     int a;
     char b;
     float c;
}x;
struct
{
     int a;
     char b;
     float c;
}*p;

can only be used once 

 The above two structures omit the structure tag (tag) when declaring it.

        p = &x.

Warning: The compiler will treat the above two declarations as two completely different types. So it's illegal.

1.4 Self-reference of structure

struct Node
{
     int data;             //数据域
     struct Node* next;    //指针域
};

        struct Node next;

It is wrong to write like this, the size of sizeof(struuct Node) is unknown.  

1.5 Initialization and access of structures

struct Student
{
	int num;			
	char name[20];		
	char sex[5];		
	int age;	
};						


int main()
{
	struct Student s1 = { 114202,"张三","男",18 };
	printf("%d %s %s %d\n", s1.num, s1.name, s1.sex, s1.age);
	return 0;
}



//结构体嵌套初始化
struct Node
{
     int data;
     struct Point p;
     struct Node* next; 
}n1 = {10, {4,5}, NULL};

When defining a structure variable, its members can be initialized. The initialization list is some constants enclosed in curly braces, and these constants are assigned to each member of the structure variable in turn. 

Note: The structure variable is initialized, not the structure type.

structure access 

p points to a structure variable stu

  1. stu. member name
  2. (*p).member name
  3. p->member name

1.6 Structure memory alignment 

Structure memory alignment is mostly used to calculate the size of the structure. Next, let me observe the following code. What are the sizes of the two structures?

According to our current knowledge, char is one byte and int is four bytes, so the size of these two structures is 6 bytes. But the result is different from what we imagined, why? Let us enter the following study with questions.

 We first observe the offset of each member variable in the structure from the starting position through a macro (offsetof)

 The function of offsetof: calculate the offset of the member variable of the structure compared to the starting position of the structure

 First, you must master the alignment rules of the structure:

  1. The first member is at offset 0 from the structure variable.
  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. There is no default alignment number in Linux, and the alignment number is the size of the member itself
  3. The total size of the structure is an integer multiple of the maximum alignment (each member variable has an alignment).
  4.  If a structure is nested, the nested structure is aligned to an integer multiple of its own maximum alignment, and the overall size of the structure is an integer multiple of all maximum alignments (including the alignment of the nested structure).

 For example:

 Why does memory alignment exist?

1. Platform reasons (reasons for porting): Not all hardware platforms can access any 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 to access unaligned memory, the processor needs to make two memory accesses; while aligned memory accesses require only one access.  

 

accessing twice when reading i

The first time read 4 bytes from the alignment boundary, but i only covered 3 bytes, and read again to get the complete i

 Summary:
The memory alignment of the structure is the practice of exchanging space for time. We will try to gather members who take up less space together to save space.

 1.7 Modify the default alignment number

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
     char c1;
     int i;
     char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

 1.8 Structure parameter passing

struct S
{
     int data[1000];
     int num;
};

struct S s = {
   
   {1,2,3,4}, 1000};


//结构体传参
void print1(struct S s)
{
     printf("%d\n", s.num);
}


//结构体地址传参
void print2(struct S* ps)
{
     printf("%d\n", ps->num);
}


int main()
{
     print1(s);  //传结构体
     print2(&s); //传地址
     return 0;
}

print2 function is better

Reason: When a function passes parameters, the parameters need to be pushed onto the stack.

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.

Conclusion: When the structure is passed as a parameter, the address of the structure must be passed. 

Second, bit segment 

2.1 What is a 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 segment.

 For example:

struct A
{
 int a:2;
 int b:5;
 int c:10;
 int d:30;
};

 A is a bit segment type. What is the size of segment A

4 integers should be 16 bytes, why is it 8 now?

Some data in memory is very small, for example, only two binary bits are needed to store 1, 2, and 3, and 30 bits can be saved by using bit segments. 

The bit segment is considered from the perspective of saving space

2.2 Memory allocation for bit segments

Notice: 

1. The members of the bit segment 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. Bit segments involve many uncertain factors. Bit segments are not cross-platform. Programs that focus on portability should avoid using bit segments.

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

 2.3 Cross-platform problems of bit segment

1. Whether an int bit field is treated as a signed number or an unsigned number is undefined.

2. The maximum number of bits in a bit field cannot be determined. (16-bit machines can be up to 16, 32-bit machines can be up to 32, written as 27, there will be problems on 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.

Summary: Compared with the structure, the bit segment can achieve the same effect, and can save space very well, but there are cross-platform problems.

Three, enumeration

Enumeration, as the name suggests, is to enumerate one by one.

List all possible values.

For example in our real life:

There are only 7 days in a week from Monday to Sunday, which can be listed one by one.

Gender: male, female, confidential, and can also be listed one by one.

The three primary colors can also be listed one by one.

3.1 Definition of enumeration type

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};

enum Color//颜色
{
	RED,
	GREEN,
	BLUE
};

The enum Day , enum Sex , and enum Color defined above are all enumeration types.

The content in {} is the possible value of the enumeration type, also called enumeration constant.

The value of the enumeration constant starts from 0 by default and increments by 1

 You can also assign an initial value when declaring an enumeration type

 3.2 Use of enumeration

enum Color
{
     RED=1,
     GREEN=2,
     BLUE=4
};


enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

enum Color clr = 3; 

This code is wrong, 3 is of type int, and the clr variable is of type Color, which cannot be assigned successfully. Clarifies that an enum is a type

3.3 Advantages of enumeration 

We can use #define to define constants, why use enums?

Advantages of enums:

1. Increase the readability and maintainability of the code

2. Compared with the identifier defined by #define, the enumeration has type checking, which is more rigorous.

3. Easy to debug

4. Easy to use, you can define multiple constants at a time

  4. Union (community)

4.1 Definition of Consortium

Unions are also a special custom type

Variables defined by this type also contain a series of members, characterized by the fact that these members share the same space (so unions are also called unions).

For example:

union Un
{
	int i;
	char a;
};

int main()
{
	union Un un = { 0 };
	un.i = 0x11223344;
	un.a = 0x55;

	return 0;
}

4.2 Characteristics of the consortium

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 the union must at least have the ability to save the largest member).

union Un
{
	int i;
	char a;
};

int main()
{
	union Un un = { 0 };
	printf("%p\n", &un);
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.a));
	return 0;
}

 Example: Determine the big and small endian storage of the current computer

union 
{
	int i;
	char a;
}un;

int check()

{
	union
	{
		int i;
		char a;
	}un = { .i = 1 };
	return un.a;
}

int main()
{
	
	int ret = check();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

4.3 Calculation of joint size

The size of the union is at least the size of the largest member.

When the maximum member size is not an integer multiple of the maximum alignment, it must be aligned to an integer multiple of the maximum alignment.

The content of this time is over here. I hope you can gain something after reading it, and thank you readers for your support. If you have any questions about the article, you can leave a message in the comment area. The blogger must carefully revise it and write better articles in the future. 

おすすめ

転載: blog.csdn.net/2301_76207836/article/details/131715918