Custom data types: structures, enumerations, unions

We have already learned about structures before, and this article will learn more about them, and then learn other custom data types, enumerations and unions

Table of contents

1. Structure

1.1 Declaration of structure type

1.2 Self-reference of structure

1.3 Definition and initialization of structure variables

1.4 Structure memory alignment

1.5 Structure parameter passing

1.6 Structs implement bit fields (filling and portability of bit fields)

2. Enumeration

2.1 Definition of enumeration type

2.2 Advantages and uses of enumerations

3. United

3.1 Definition of union type

3.2 Characteristics of the union

3.3 Calculation of joint size


1. Structure

1.1 Declaration of structure type

The basics of structs:

Built-in data types include char short int long long float double etc., and C language also allows us to create our own types.

An array is a collection of elements of the same type, while a structure can be a collection of members of different types.

A structure is a collection of values, these are called member variables, and each member of a structure can be a variable of a different type

declaration of structure

struct tag//tag为结构体名称,可以自己设置
{
	member-list;//成员列表
}variable-list;//变量列表

For example, declare a student structure

struct Student//结构体名称是Student, 可以自己起不同的名称
{
	//成员变量
	char name[20];//姓名
	int age;//名字
	float weight;//体重
};//最后要加分号

Special Declaration: Anonymous Struct Types

//匿名结构体类型
struct
{
	char c;
	int a;
	double d;
}s1,s2;//只能在这里创建变量,后面不能再使用,相当于一次性

struct
{
	char c;
	int a;
	double d;
}*ps;//结构体指针

int main()
{
	ps = &s1;
	//虽然*ps的类型与s1的类型内容相同
	//但他们都是匿名结构体变量,编译器会认为它们不同
	//会有警告
	return 0;
}

Warning: The compiler will consider two anonymous structs to be two completely different types.

1.2 Self-reference of structure

Is it okay to have a member in a struct that is of type the struct itself?

错误的示范
struct Node1
{
	int data;
	struct Node1 n;
};
结构体内部有自己,那它的大小是什么,自己大小+data大小,自己大小会无限嵌套

correct self-reference method

struct Node
{
	int data;//4
	struct Node* next;//指针 4/8
};

int main()
{
	struct Node n1;
	struct Node n2;
	n1.next = &n2;//n1的下一个数据是n2
	return 0;
}

Through this structure pointer, we can find the next structure unit and link the data.

typedef type renaming

typedef struct
{
	int data;
	char c;
}S;//有typedef这里S是类型,不是结构体变量

typedef struct
{
	int data;
	Node* next;//这样是错误的,还没有定义Node 报错
}Node;

解决方法:
typedef struct Node
{
    int data;
    struct Node* next;
}Node;

1.3 Definition and initialization of structure variables

Definition of structure variables:

With the structure type, let's define the structure variable

struct S
{
	int a;
	char c;
}s1;//1.声明时创建,是全局变量

struct S s2;//2.通过类型创建,全局变量

int main()
{
	struct S s3;//3.通过类型创建,是局部变量
	return 0;
}

Initialization of structure variables

#include<stdio.h>
struct S
{
	int a;
	char c;
}s1;

struct B
{
	float f;
	struct S s;
};

int main()
{
	//创建变量时初始化
	struct S s2 = { 100,'a'};//1.按顺序初始化
	struct S s3 = { .c = 'r', .a = 100 };//2.自己指定顺序初始化

	struct B sb = { 3.14f,{200,'w'} };

	printf("%f %d %c\n", sb.f, sb.s.a, sb.s.c);

	return 0;
}

1.4 Structure memory alignment

This is the more important content in structure knowledge

We have mastered the basic use of structures, now we discuss a problem in depth, how to calculate the size of structures .

#include<stdio.h>
struct S1
{
	int a;
	char c;
};
struct S2
{
	char c1;
	int a;
	char c2;
};

struct S3
{
	char c1;
	int a;
	char c2;
	char c3;
};


int main()
{
	printf("%d\n", sizeof(struct S1));//8
	printf("%d\n", sizeof(struct S2));//12
	printf("%d\n", sizeof(struct S3));//12

	return 0;
}

Seeing this result, everyone will definitely have doubts about how the size of the structure is calculated.

If you calculate the size, you must first know the structure alignment rules

1. The first member of the structure is always placed at offset 0

2. Starting from the second member, each subsequent member must be aligned to an integer multiple of an alignment number.

  When this alignment number: the smaller value of the member's own size and the default alignment number

  Remarks: In the VS environment, the default pair number is 8

        There is no default alignment number in the gcc environment, no default alignment number is the size of the member itself

3. When all members are stored in,

  The total size of the structure must be: an integer multiple of the maximum alignment number of all members If it is not enough, space is wasted for alignment

4. If a structure is nested, the nested structure must be aligned to an integer multiple of the maximum alignment of its own members. The size of the entire structure must be an integer multiple of the maximum alignment. The maximum alignment includes the nested structure Alignment in body member

Let's take this structure as an example to calculate

struct S3
{
	char c1;
	int a;
	char c2;
	char c3;
};

 C1 is the first member placed at offset 0, and the size of a is 4 bytes. Compared with the default alignment, its alignment is 4, and it is placed at a multiple of 4, that is, at offset 4 , the sizes of c2 and c3 are both 1, and the offsets are all multiples of 1, so they are directly stored later.

The size of this structure is an integer multiple of the maximum alignment number 4, 12

can do some more practice

#include<stdio.h>

struct S2
{
	char c1;
	char c2;
	int a;
};
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;//16
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S2));//8
	printf("%d\n", sizeof(struct S3));//16
	printf("%d\n", sizeof(struct S4));//32
	return 0; 
}

Why memory alignment exists

Most of the references say this

1. Platform reason (transplant reason): 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 will be 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.

In general︰

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

When designing the structure, we must satisfy the alignment and save space. How to do it:

It is to let the members who occupy a small space gather together as much as possible.

 Modify the default parameters #pragma pack preprocessing directive

#include<stdio.h>
#pragma pack(1)//把默认对齐数改为1
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)
struct S
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S));//6
	printf("%d\n", sizeof(struct S1));//12

	return 0;
}

1.5 Structure parameter passing

There are two ways to pass parameters to a structure

#include<stdio.h>
struct S
{
	int data[1000];
	int num;
};

struct S s1 = { {1,2},100 };

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

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

int main()
{
	//传结构体变量
	print1(s1);

	//传结构体指针
	print2(&s1);
}

Which of the above print1 and print2 functions is better?

The answer is: the print2 function is preferred. reason︰

When a function passes parameters, the parameters need to be pushed onto the stack, which will cause system overhead in time and space. Just copy the data again.
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.
 

1.6 Structs implement bit fields (filling and portability of bit fields)

what is bit segment

The declaration of the bit field is similar to the structure, with two differences

1. The members of the bit segment are int, unsigned int, signed int or other plasticizer group char...
2. The members of the bit segment are followed by a colon and a number

#include<stdio.h>

//位段   二进制位

struct A
{
	//_只是成员变量的名称中的下划线,没有什么特殊意义
	int _a : 2;//存放两个二进制位就能表示的数字  00 或 01 或 10 或 11
	int _b : 5;
	int _c : 10;
	int _d : 30;
};//47个bit位

//一个字节8个bit位

int main()
{
	struct A sa = { 0 };
	printf("%d\n", sizeof(sa));//8 个字节,相比16个字节已经节省空间了
	printf
	return 0;
}

In the above code, A is a bit field type, and its size is sizeof(struct A) is 8 bytes.

Why is it 8 bytes, here we learn about the memory allocation of the bit segment

  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. Bit segments involve many uncertain factors, bit segments are not cross-platform, and programs that focus on portability should avoid using bit segments.

 On the VS platform, the memory allocation method of segment data is as follows:

1. The bits in the allocated memory are used from right to left, from high address to low address
2. When the remaining bits of the allocated memory are not enough to use, waste them and open up new space

 let's verify

#include<stdio.h>

struct S
{
	char _a : 3;
	char _b : 4;
	char _c : 5;
	char _d : 4;
};

int main()
{
	struct S s = { 0 };
	s._a = 10;//1010  截断 010
	s._b = 12;//1100
	s._c = 3;//00011
	s._d = 4;//0100
	return 0;
}

 Space development process:

 Debug verification:

 Cross-platform issues with 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. (16-bit machines have a maximum of 16, 32-bit machines have a maximum of 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, whether to discard the remaining bits or use them is uncertain, and the standard has not yet been established. Defined.

Summarize︰

Compared with the structure, the bit field can achieve the same effect, but it can save space very well , but there are cross-platform problems.

2. Enumeration

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

List all possible values.

For example in our real life:

A Monday to Sunday is limited to 7 days that can be - enumerated.

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

There are 12 months in the month, and it is also possible to list

Enumerations can be used here.

2.1 Definition of enumeration type

enum Sex
{
	//枚举的可能取值,默认是从0开始,递增1的
	MALE,
	FEMALE=5,
	SECRET
};

2.2 Advantages and uses of enumerations

 Advantages of Enums

Why use enums?

We can use #define to define constants, why do we have to use enumerations? 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. Prevent naming pollution (encapsulation)
4. Easy to debug
5. Easy to use, multiple constants can be defined at a time

 Enumeration usage:

enum Sex
{
	//枚举的可能取值,默认是从0开始,递增1的
	MALE,
	FEMALE=5,
	SECRET
};

int main()
{
	enum Sex s = MALE;//把可能的确实赋给这样的变量 
	printf("%d\n", MALE);//0
	printf("%d\n", FEMALE);//5
	printf("%d\n", SECRET);//6
	return 0;
}

 You can also directly assign corresponding numbers to enumeration variables, but this method is not available in C++ .

like:

enum Sex s = 0;//MALE

 Enum types and sizes of variables:

Enumeration type members are all int type, the size is 4 bytes

#include<stdio.h>

enum Sex
{
	MALE,
	FEMALE=5,
	SECRET
};

int main()
{
	enum Sex s = MALE;

	printf("%d\n",sizeof(enum Sex));//4
	printf("%d\n", sizeof(MALE));//4
	printf("%d\n", sizeof(s));//4
	return 0;
}

3. United

3.1 Definition of union type

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 Un
{
	char c;
	int i;
};

int main()
{
	union Un u;
	printf("%d\n", sizeof(u));//4

	//下面三个都相等
	printf("%p\n", &u);
	printf("%p\n", &u.i);
	printf("%p\n", &u.c);
	return 0; 
}

3.2 Characteristics of the union

The members of the union share the same memory space, and 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).

 You can use this to judge whether it is big-endian storage or little-endian storage.

#include<stdio.h>

union Un
{
	char c;
	int i;
};

int main()
 {
	union Un u;
	u.i = 1;
	if (u.c == 1)
	{
		printf("小端存储\n");
	}
	else
	{
		printf("大端存储\n");
	}
}

3.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.
#include<stdio.h>

union Un
{
	char arr[5];
	int n;
};

int main()
{
	printf("%d\n", sizeof(union Un));//8
	return 0;
}

The output here is 8 because there is also an alignment in the union. The alignment of the array is calculated by type, the maximum alignment is 4, and the integer multiple of the maximum alignment is 8.

end of article

Guess you like

Origin blog.csdn.net/qq_72916130/article/details/131362055