C language custom type

In C language, in addition to several basic data types we commonly use, there is also a type called custom type. For example: We want to describe a student. This student has name, gender, age, height, etc. Basic data types alone cannot be fully described. At this time, we will use our custom type to describe it. Custom types are structs, enumerations and unions.

1. Structure

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

1. Statement

struct tag//tag here represents a tag, not a variable name

{

        member-list;//member variable

}variable-list ; //Variable list, used to define variables. Note the semicolon here

For example, to describe a person:

struct person
{
	char name[20];
	char sex[5];
	int age;
	char nation[20];
};

The variable list here is optional. Variables defined in the variable list are global variables.

2, define and initialize

To define, it can be defined directly in the variable list or in the main function.

struct person
{
	char name[20];
	char sex[5];
	int age;
	char nation[20];
}person1;//结构体变量person1

struct person person2;//结构体变量person2

int main(void)
{
	struct person person3;//结构体变量person3
    struct person person4[2];//结构体数组变量person4,有两个元素,都是结构体类型
    return 0;
}

It is also very simple to initialize.

#include<stdio.h>

struct person
{
	char name[20];
	int age;
}person1 = {"zhangsan", 18};//结构体变量person1

struct person person2 = {"lisi", 18};//结构体变量person2

int main(void)
{
	struct person person3 = {"wangwu", 18};//结构体变量person3
	struct person person4[2] = 
	{"abc", 18, {"def", 18}};//加不加{}都可以

	return 0;
}

Sometimes, the structure will also be nested:

struct person
{
	char name[20];
	int age;
}person1 = {"zhangsan", 18};//结构体变量person1

struct people
{
	struct person person;
	char nation[20];
};

 Initialize it:

struct people people = { {"zhangsan",18}, "XXX" };//Initialization of nested structures

When declaring the structure, we can also not fully declare it, and omit the label at this time.

struct
{
 int a;
 char b;
 float c;
}a;

There is one case where the tag cannot be omitted, that is, when the typedef keyword is used. The typedef keyword can define a new name for a data type. If omitted, an error will be reported.

typedef struct S
{
	int data[1000];
	int num;
}s;
	s s1 = { {1,2,3,4}, 1000 };

3, access to structure members

The access to the members of the structure needs to be accessed through the dot (.) operator. Now let's print these initialized variables.

#include<stdio.h>

struct person
{
	char name[20];
	int age;
}person1 = {"zhangsan", 18};//结构体变量person1

struct people
{
	struct person person;
	char nation[20];
};

struct person person2 = {"lisi", 18};//结构体变量person2

int main(void)
{
	struct person person3 = { "wangwu", 18 };//结构体变量person3
	struct person person4[2] = 
	{"abc", 18, {"def", 18}};//加不加{}都可以

	struct people people = { {"zhangsan",18}, "XXX" };//嵌套结构体的初始化

	printf("%s\n", person1.name);
	printf("%d\n", person1.age);
	printf("--------------\n");
	printf("%s\n", person2.name);
	printf("%d\n", person2.age);
	printf("--------------\n");
	printf("%s\n", person3.name);
	printf("%d\n", person3.age);
	printf("--------------\n");
	printf("%s\n", person3.name);
	printf("%d\n", person3.age);
	printf("--------------\n");
	printf("%s\n", person4[1].name);
	printf("%d\n", person4[1].age);
	printf("--------------\n");
	printf("%s\n", people.person.name);
	printf("%s\n", people.nation);

	return 0;
}

 Now, let's take a look at the members of the variable pointed to by the struct pointer access. There is the following code:

struct Stu
{
	char name[20];
	int age;
};

void print(struct Stu* ps)
{
    printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
    //使用结构体指针访问指向对象的成员,为了简化,使用->操作符来替代(*).操作。
    printf("name = %s   age = %d\n", ps->name, ps->age);
}
int main()
{
    struct Stu s = { "zhangsan", 20 };
    print(&s);//结构体地址传参
    return 0;
}

#include<stdio.h>

struct person
{
	char name[20];
	int age;
};

struct people
{
	struct person* person;
	char nation[20];
};

int main(void)
{
	struct person person = { "zhangsan", 18 };
	struct people people = { &person ,"XXX"};

	printf("%s\n", people.person->name);
	printf("%d\n", people.person->age);
	printf("%s\n", people.nation);

	return 0;
}

 4. Structure parameters

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

When passing parameters, the parameters need to be pushed onto the stack. When passing a structure object, because the structure is too large, the system overhead when the parameters are pushed to the stack is relatively large, resulting in performance degradation. In addition, when calculating the size of the structure, if it is the value of the passed structure, it will cause the size of the structure to increase infinitely. Therefore, the structure needs to pass the address when passing parameters.

5, structure memory alignment

Now, let's discuss the memory size of structs.

5.1, look at the following code, calculate its size (*):

struct S1
{
	char c1;
	int i;
	char c2;
};

int main(void)
{
	printf("%d\n", sizeof(struct S1));
	return 0;
}

 We found that the size of this structure is not 6 bytes as we thought, but 12 bytes.

The structure memory is calculated as follows:

1, the first member is at the address at offset 0 from the structure variable.

2. Other member variables should be aligned to addresses that are integer multiples of the alignment tree. Alignment = the smaller of the compiler's default alignment and the size of the member. The default value in VS is 8, and some default to 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 of the structure is an integer of all maximum alignment numbers (including the alignment number of nested structures). times.

To put it simply, it is: in the memory, the first variable is placed at the starting address of 0, and where the other addresses are to be placed depends on the alignment number. The alignment is the minimum of the default alignment and member size. After being placed according to the alignment number, the size of this structure is an integer multiple of the maximum alignment number.

Calculate the size of the following nested structure: 

struct S1
{
	char c1;//1
	int i;//4
};//大小为8

struct S2
{
	char c1;//1
	struct S1 s1;//8
	int d;//4
};


int main(void)
{
	printf("%d\n", sizeof(struct S2));
	return 0;
}

 When calculating, it is not possible to directly add up the size of the member variables, at the multiple of the maximum value of the alignment number. For example, at the first calculation (*): the sum is 6, and when taking an integer multiple of the alignment number (4), the result is 8, which is obviously inconsistent with the calculation result.

Let's look at the last one:

struct S
{
	short s1;//2
	char c1;//1
	int s2;//4
};


int main(void)
{
	printf("%d\n", sizeof(struct S));
	return 0;
}

 If you do it proficiently, you don't need to draw a picture, you can just do the calculation directly. Sometimes short and char are encountered together. At this time, it can be directly regarded as 4 sizes.

5.2, the reasons for the existence of memory alignment:

1. Platform reasons (transplant reasons): 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 in order to access unaligned memory, the processor needs to make two memory accesses; aligned memory accesses require only one access.

In general: The memory alignment of structures is the practice of exchanging space for time. This practice is very common.

When we design the structure, we must not only meet the alignment, but also save space, so let the members that occupy the small space be concentrated as much as possible. If we change the order at (*), for example, put two char types together, the size of this structure will change.

5.3, modify the default alignment number

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c3;
	int i2;
	char c4;
};
int main()
{
	printf("%d\n", sizeof(struct S2));//6
	return 0;
}

Revert to default:

#pragma pack()//Unset the default alignment number, restore to the default 

Second, the segment

1. Definitions and Statements

Structs can implement bit fields. Bit fields are declared similarly to structs. The bit field defines the space occupied by the member variables in the structure (or union) in units of bits. The use of the bit-segment structure not only saves space, but also facilitates operation. For example, bit segment A:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};

The member name of the bit field is followed by a colon and a number. Subsequent numbers are in bits. The members of the bit field must be of the integer family.

2, the memory allocation of the bit segment

The space of the bit field is opened up by 4 bytes ( int ) or 1 byte ( char ) as needed. The previous bit segment A, _a is of type int, which is opened up with 4 bytes. So what is the size of A (32 bits)?

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct A));
	return 0;
}

 How is this calculated?

Here, _a opens up a size of 4 bytes, but _a only needs two bits. Under 32 bits, there are 30 more bits. _b needs 5 bits, there are 25 bits left, _c needs 10 bits, so there are 15 bits left, _d needs 30 bits, the remaining 15 bits are not enough, just open up 4 bytes of space to store _b. The figure here is assumed to be placed from the left.

Let's look at this question (starting from the right on VS):

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

int main(void)
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;//在内存中是什么样的
    return 0;
}

3. The cross-platform problem of bit segment

1. It is uncertain whether an int bit field is treated as a signed or unsigned number.

2. The maximum number of bits in the bit segment cannot be determined. (16-bit machines have a maximum of 16, 32-bit machines have a maximum of 32, and write 27, which will cause problems on 16-bit machines.

3. Whether the members of the bit field are allocated in memory from left to right or from right to left is undefined.

4. When a structure contains two bit segments, and the members of the second bit segment are too large to accommodate the remaining bits of the first bit segment, it is uncertain whether to discard the remaining bits or use them.

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.

3. Enumeration

To enumerate is to enumerate. List the possible values.

1. Definition:

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

int main(void)
{
	printf("%d\n", Mon);
	printf("%d\n", Tues);
	printf("%d\n", Wed);
	printf("%d\n", Thur);
	printf("%d\n", Fri);
	printf("%d\n", Sat);
	printf("%d\n", Sun);

	return 0;
}

 The enum Day defined above is an enumeration type. The contents of {} are the possible values ​​of the enumeration type, also called enumeration constants. These possible values ​​all have values. The default starts from 0 and increments by 1 at a time. Of course, an initial value can also be assigned when defining.

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

2. Advantages

1. Increase the readability and maintainability of the code

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

3. Prevents naming pollution (encapsulation)

4. Easy to debug

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

3. Use

In the switch statement, if we have too many branches and the label after the case is a number, at this time, to know what the number represents, we need to find it. But if we use enum, we can replace the numbers with constant names that make people know at a glance, which greatly improves the readability of the code.

enum Game
{
	EXIT,
	GAME_BEGIN,
	GAME_DISCONTINUE
};

int main(void)
{
	switch (1)
	{
	case GAME_BEGIN:
		printf("你开始了游戏!\n");
	case GAME_DISCONTINUE:
		printf("你中止游戏!\n");
	case EXIT:
		printf("你退出了游戏!\n");
	}

	return 0;
}

 4. Consortium (Commonwealth)

1. Definition:

Unions are also a special custom type that defines variables that contain a series of members. These members share a common space, so it is also called a common body. The definition of union is similar to that of structure.

//联合类型的声明
union Un
{
 char c;
 int i;
};
//联合变量的定义
union Un un;

2. Features:

The union shares a space, so that the size of the union variable, at least the size of the largest member, can store the largest member.

#include<stdio.h>

union Un
{
	int i;
	char c;
};
union Un un;

int main(void)
{
	// 下面输出的结果是一样的吗?
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));

    //下面输出的结果是什么?
    un.i = 0x44;
    un.c = 0x55;
    printf("%x\n", un.i);

	return 0;
}

 

From this, we can see the characteristics of the consortium: sharing a space. Because of this, when printing un.i, you will find that the result is 0x55. Here 0x44 is overwritten. According to the characteristics of the union, it can be used to judge the storage size of the current computer.

union Un
{
	int i;
	char ch;
};

int main(void)
{
	union Un un;
	un.i = 1;
	printf("%d\n", un.ch);//1,是小端
	
	return 0;
}

3. Calculation of the size of the union

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 number, it must be aligned to an integer multiple of the maximum alignment number.

union Un1
{
	char c[5];//共5个元素,每个占1个字节,总的大小为5
	int i;//4
};

int main(void)
{
	printf("%d\n", sizeof(union Un1));//8,是最大对齐数4的倍数。注意不是5,这里的5是数组总的大小
	return 0;
}
union Un2
{
	short c[7];//2*7
	int i;//4
};

int main(void)
{
	printf("%d\n", sizeof(union Un2));//16
	return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324134948&siteId=291194637