[C language advanced study notes] 4. Custom type (1) (structure + bit segment)


structure

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.
An array is a collection of elements of the same type. A struct is also a collection of values, and each member of a struct can be of a different type.


struct declaration

struct tag
{
    
    
	member_list;
}variable_list;

Structure keyword: struct
structure tag: tag
structure type: struct tag
structure member list: member_list
structure variable list: variable_list

For example, describe a student: include the student's name, student ID, age, gender

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

The above structure declaration method is a complete declaration. Of course, there are also incomplete declarations, such as omitting the label of the structure. For
example:

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

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

Thinking: Is this code p = &x; legal? Will it work properly in the compiler?
The compiler will issue a warning: the compiler will treat the above two declarations as two completely different types, so it is illegal.
insert image description here
typedef -- - Type redefinition
Thinking: Can typedef be used to redefine anonymous structure types?
E.g:

typedef struct
{
    
    
	int data;
	Node* next;
}Node;
//这样写代码,可以吗?

This method cannot run under the compiler!

Solution:

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

Thinking: Can struct Node* next; be replaced by Node* next;?

Answer: No, typedef redefines the structure type, provided that the structure type is created first, and then redefines a new type name for its type name. If it is said that the redefinition is used inside the created structure type name? Did you start using it before it was created? This place can't travel through time like this!


Self-referencing of structs

Thinking: Is it okay to include a member whose type is the structure itself in a structure?

//代码1
struct Node
{
    
    
	int data;
	struct Node next;
};
//可行否?

If yes, what is sizeof(struct Node) ?

Understanding: Assuming that the method in Code 1 can be executed, then in the process of creating the structure, the
size of the struct Node next is unknown because the struct Node type of the structure has not been created yet, and whether the struct Node type can be Successful creation in turn depends on
the certainty of the size of the struct Node next type. So the two contradict each other. So the above method does not work!

Correct self-referencing of structs:

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

Why is this approach successful?

Understanding: The self-application method of the structure here is not to directly use the structure to create variables, but to create a pointer to the type of the structure. We know that the size of the pointer has nothing to do with the type it points to, only the platform environment. , 32-bit platform pointer size is 4 bytes, 64-bit platform, the pointer size is 8 bytes. Because of the certainty of the size of the pointer, the overall size of the structure type can also be determined when self-referencing.


Definition and initialization of structure variables

With the structure type, how to define structure variables and initialize variables?
Example 1:

struct Point
{
    
    
	int x;
	int y;
}p1;//声明结构体类型的同时定义变量p1
struct Point p2;//定义结构体变量p2
struct Point p3 = {
    
     1,1 };//初始化:定义变量的同时给变量赋值

Example 2:

struct Stu//类型声明
{
    
    
	char name[20];//姓名
	int age;//年龄
};
struct Stu s = {
    
     "Student_zhang",20 };//初始化

Example 3:

struct Point
{
    
    
	int x;
	int y;
}p1;//声明结构体类型的同时定义变量p1
struct Node
{
    
    
	int data;
	struct Point p;
	struct Node* next;
}n1 = {
    
     10,{
    
    4,5},NULL };//结构体嵌套初始化
struct Node n2 = {
    
     20,{
    
    1,2},NULL };//结构体嵌套初始化

Structure memory alignment

We have mastered the basic use of structures, so let's discuss a deeper problem now;
any data type should have its corresponding memory space size, such as char size is 1 byte, int type is 4 Byte, double type is 8 bytes, etc. Without a certain size, it is impossible to know how much memory space should be allocated to a variable of this type at the time of creation. So, what is the size of the struct? How to calculate it?
This involves a popular test site: structure memory alignment!

Structure memory alignment rules:
1. The first member is at the address whose offset is O from the structure variable.
⒉. Other member variables should be aligned to the address of an integer multiple of a certain number (alignment number).
①Alignment number = The smaller value of the compiler's default alignment number and the size of the member.
②The default value in VS is 8. There is no default value in Linux, and its own size is the alignment number
. 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.

Special attention: In point 4, the nested structure is not aligned to an integer multiple of the overall size of the structure, but an integer multiple of the maximum alignment number of the structure itself.


Exercise one:

#include<stdio.h>
	struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s1));
	return 0;
}

Analysis:
insert image description here
Results show:
insert image description here


Exercise 2:

#include<stdio.h>
struct s2
{
    
    
	char c1;
	char c2;
	int i;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s2));
	return 0;
}

analyze:
insert image description here

Results show:
insert image description here


Exercise 3:

#include<stdio.h>
struct s3
{
    
    
	double d;
	char c;
	int i;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s3));
	return 0;
}

Analysis:
insert image description here
Results show:
insert image description here


Exercise 4:
Structure Nesting Problem

#include<stdio.h>
struct s3
{
    
    
	double d;
	char c;
	int i;
};
struct s4
{
    
    
	char c1;
	struct s3 s3;
	double d;
};
int main()
{
    
    
	printf("%d\n", sizeof(struct s4));
	return 0;
}

analyze:
insert image description here

Results show:
insert image description here


Thinking: Why does memory alignment exist?

Most of the reference materials say:
1. Platform reason (transplantation reason): not all hardware platforms can access any data at any address; some hardware platforms can only fetch certain data at certain addresses Data of a specific type, otherwise a hardware exception is thrown.
⒉ 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: memory alignment of structures is the practice of trading space for time.

When designing the structure, we must not only satisfy the alignment, but also save space. How to do it?

Keep members who take up less space together as much as possible.

For example, change the way in exercise 1 to the way in exercise 2:

	struct s1
{
    
    
	char c1;
	int i;
	char c2;
};
struct s2
{
    
    
	char c1;
	char c2;
	int i;
};

The members of s1 and s2 types are exactly the same, but the size of the space occupied by s1 and s2 is different. The size of the former is 12 bytes, and the size of the latter is 8 bytes. Obviously, the space utilization efficiency of the latter method is higher. .


Modify the default alignment

#pragma set default alignment

Using #pragma, can be used to change our default alignment number.
Example:

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

What is the output of the above code?

Analysis: The size of the s1 structure is 12. This is explained in detail in Exercise 1. The key is what is the size of s2?
According to the rules of structure memory alignment, the default alignment number is 1, then:

insert image description here

Results show:
insert image description here

Conclusion: When the alignment of the structure is not suitable, we can change the default alignment number to meet the needs.


offsetof

This is a macro that calculates the offset of a struct member relative to the start of the struct

A certain degree written test question:
Content: Write a macro to calculate the offset of a variable in the structure relative to the first address, and give an explanation.
Investigation: Implementation of the offsetof macro

#include<stdio.h>
#include<stdlib.h>
struct s2
{
    
    
	char c1;
	int i;
	char c2;
};
int main()
{
    
    
	printf("%d\n", offsetof(struct s2, c1));
	printf("%d\n", offsetof(struct s2, i));
	printf("%d\n", offsetof(struct s2, c2));
	return 0;
}

insert image description here


structure parameter

Example:

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

There are two ways to pass parameters in a structure:
one is to pass a structure object (pass by value), which corresponds to the way of the print1 function, and the
other is to pass the address of the structure (by address), which corresponds to the way of the print2 function.

Thinking: Which of the above print1 and print2 functions is better?

The answer is: the print2 function is preferred.

reason︰

When passing parameters to a function, the parameters need to be pushed onto the stack, which will result in system overhead in terms of time and space.
If a structure object is passed, the structure is too large, and the system overhead of parameter stacking is relatively large, so the performance will be degraded.

Conclusion: When passing parameters in a structure, you need to pass the address of the structure.


bit segment

What is a bit field?
The declaration and structure of a bit field are similar, with two differences:
1. The members of a bit field must be int, unsigned int or signed int.
2. The member name of the bit field is followed by a colon and a number.
Example:

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

struct A is a bit segment type, so what is the size of A?
To answer the above question, we must first understand the meaning of a colon and a number after the member name of the bit segment. In fact, the numbers after the colon represent binary bits. The specific analysis is as follows:
insert image description here

Extension:
①In the actual test, the integer type can be used.
②Only one type appears in the same bit segment. For example, the int
type is different from the unsigned int type and cannot be in the same bit segment at the same time.


Memory allocation for bit fields

1. The members of the bit field can be int unsigned intsigned int or char (belonging to the integer family) type
2. The space of the bit field is 4 bytes ([int) or 1 byte (char) as needed. to open up.
3. Bit segment involves many uncertain factors. Bit segment is not cross-platform. Programs that focus on portability should avoid using bit segment.

These uncertain factors are reflected in:
①Whether the space is going to be wasted?
② Is the space used from left to right or from right to left?
For example, VS2019: ① open up 1/4 bytes first, ② use from right to left, the space will be wasted

Example:

#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;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

Before execution:
insert image description here

After execution:
insert image description here

analyze:
insert image description here


Cross-platform issues with bit fields

1. It is uncertain whether the 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 machine maximum 16, 32-bit machine maximum 32), written as 27, there will be problems on 16-bit machine.
3. Whether members in a bit segment are allocated in memory from left to right or from right to left is undefined.
4. When a structure contains two bit fields, and the members of the second bit field are relatively large and cannot accommodate the remaining bits of the first bit field
, it is uncertain whether to discard the remaining bits or use them.

Summary:
Compared with the structure, the bit segment can achieve the same effect, but it can save space (advantage), but the bit segment has cross-platform problems (disadvantage).


Application Scenarios for Bit Segments

Network transfer protocol package (data transfer protocol package in computer network will be mentioned)
insert image description here

Chinese version:

insert image description here

Guess you like

Origin blog.csdn.net/QIYICat/article/details/118251907