Custom types: structures, enumerations, unions

structure

What is a structure

A structure is a custom data type, which consists of multiple member variables of different types. A structure can pack multiple related data together for easy management and processing.

declaration of structure

Definition form:

struct structure name { type member name 1; type member name 2; // more member variables };



Describe a student as:

struct Stu
{
    
    
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}; //分号不能丢

In this way, struct Stu is the same as the int type, which is a data type;Remember not to forget Stu, this is a type defined by ourselves, so it must have a specific name;

special statement

When declaring a structure, it can be incomplete.

struct
{
    
    
 int a;
 char b;
 float c;
}x;
struct
{
    
    
 int a;
 char b;
 float c;
}a[20], *p;

This type is called an anonymous structure type; the unknown number added in parentheses is equivalent to a variable;

insert image description here
The above code looks like a structure, you can p=&x; but in fact, the == compiler will regard these two as custom types, which are different customizations, and == does not look at the content inside, so cannot be quoted as such;

self-referencing structure

struct Node
{
    
    
 int data;
 struct Node* next;
};

This is a linked list in a data structure, which is also a self-reference of the structure; for a structure, it is equivalent to opening up a certain space in the memory, and each member occupies a certain size of space;

insert image description here

Remember that it cannot be written as a non-pointer structure, which will lead to an infinite loop. For the above writing method, we only need to let the structure pointer point to NULL at the end;

Definition and initialization of structure variables

In the above, we have actually introduced a method of defining structure variables, which is to add definitions after the brackets;
of course, it can also be like this:

struct Point
{
    
    
 int x;
 int y;
}p1;  
struct Point p2;

Both p1 and p2 above are correct definitions, and p1 and p2 are of the same type;

Initialization can look like this:

struct Point p3 = {
    
    2, 5};

Define variables and initialize them at the same time;

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

have to be aware of is,When it is written outside the function as a global variable, the type of n1 must be a global variable, and n2 depends on whether it is placed inside the function;

Structure memory alignment

First look at the following code:

struct S1
{
    
    
 char c1;
 int i;
 char c2;
};
printf("%d\n", sizeof(struct S1));
struct S2
{
    
    
 char c1;
 char c2;
 int i;
};
printf("%d\n", sizeof(struct S2));

Answer:
12
8

Why the answer is different, it is because the memory of the structure is aligned, let me first talk about how the above is calculated;

  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 is an integer multiple of all maximum alignments (including the alignment of the nested structure) .

According to the calculation rules, we can know:
insert image description here

First look at the first member of S1 by default at the place where the variable offset is 0, or regard it as occupying memory byte 1, and for the second member, the alignment number is 4 (VS default alignment number is 8, compared with the number of bytes occupied by its own members, the alignment number takes its minimum value) so it either occupies 4 or 8; and 4 bytes are not enough, so vacate 3 blank positions and directly shift to the offset The amount is 7; then add c2; since the final calculation result needs to be an integer multiple of the maximum alignment number, and the maximum alignment number here is just 4, then you have to get 12; look at S2 again, the first member It is still at the place where the variable offset is 0, and then the alignment number of c2 is 1, so it occupies the place where the offset is 1. When it reaches i, the alignment number is 4, then 2 positions are vacated, and the final occupied word section is 8.

Why there is memory alignment:

Why does memory alignment exist?
Most of the reference materials 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 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
    .
    Generally speaking:
    the memory alignment of the structure is the practice of exchanging space for time;
struct S3
{
    
    
 double d;
 char c;
 int i;
};
printf("%d\n", sizeof(struct S3));

struct S4
{
    
    
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

First calculate that the size of S3 is 16, and the maximum number of alignments is 8; then according to the calculation method, S4 is 32;

insert image description here

Modify the default alignment

We can change the default value of the alignment number through the #pragma preprocessing directive

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

12
6

When the alignment of the structure is inappropriate, we can change the default alignment by ourselves;

Struct 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;
}

For the above printing results are all 1000, for the reference of the structure, you can use the method of structure name + dot + member name, and for the structure pointer, it is the structure name -> member name;

And for the above parameter passing methods, it is recommended to use the address to pass parameters;
when the function passes parameters, the parameters need to be pushed on the stack, and there will be 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 to the stack is relatively large, which will lead to a decrease in performance.

bit segment

The declaration and structure of the bit field are similar, there are two differences:
1. The member 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.

like:

struct A
{
    
    
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};
int main()
{
    
    
printf("%d\n", sizeof(struct A));
return 0;
}

Result:
The bits in the 8-bit segment represent binary bits, so the bit segment can be simply said to be memory-compressed typesetting of binary bits;

insert image description here

For a byte, there are 8 bits. After one variable is arranged, the next variable is arranged. If there is not enough space, it will jump to the next byte for arrangement; because the above is int as the data type; so in the end The result is two ints: 8.

Bit segment memory allocation `struct S

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;
//空间是如何开辟的?

insert image description here

The bit segment will intercept your binary bits, and the excess part is invalid, and then the intercepted part is formatted in one byte in turn. If the next intercepted bit segment exceeds the remaining space (one byte), then go to the next A byte is sorted; then it is converted from binary to hexadecimal in groups of 4 bits, and the corresponding number is the address of a byte;

Cross-platform issues with bit segments

  1. It is undefined whether an int bit field is treated as signed or unsigned.
  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 members of a bit field are allocated from left to right in memory or from right to left is undefined.
  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.

Compared with structures, bit segments can achieve the same effect and can save space very well, but there are cross-platform problems

Enumeration enum

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.
These possible values ​​are all valuable,Default starts from 0, incremented by 1 in turn, of course, the initial value can also be assigned when declaring the enumeration type.

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

Advantages of Enums

  1. Increase code readability and maintainability
  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
enum Color//颜色
{
    
    
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;

Only enumeration constants can be used to assign values ​​to enumeration variables, so that there will be no type differences.

Consortium

A union is also a special type
of custom type. The variables defined by this type also contain a series of members, which are characterized byshare the same space(So ​​unions are also called unions).

//联合类型的声明
union Un
{
    
    
 char c;
 int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un));

Result: 4
Because the union shares a piece of space, that is, some parts of the space are reused for storage;

insert image description here
so,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

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

	printf("%p\n", &un);
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));


	return 0;
}

insert image description here

Indicate that their addresses are all the same;

Use the union to judge the big and small end:

int check_sys()
{
    
    
	union
	{
    
    
		int i;
		char c;
	}un = {
    
    .i = 1};
	return un.c;
}

int main()
{
    
    
	int ret = check_sys();
	

	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

Result: little endian
This is to use the characteristics of the union to assign a value to int i in memory. If it is stored in little endian, it will be: then
insert image description here
for char c, it is exactly 01, and the returned result is 01;

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.

union Un1
{
    
    
 char c[5];
 int i;
};
union Un2
{
    
    
 short c[7];
 int i;
};
//下面输出的结果是什么?
int main()
{
    
    
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}

Result:
8
16

For Un1, the maximum storage is originally 5, but since the maximum alignment is 4, the union size must be 8 (2 4); for Un2, the maximum storage is originally 14, since the maximum alignment is 4, Then the union size is 14 (4 4).

Guess you like

Origin blog.csdn.net/m0_74068921/article/details/131715567