[C language advanced] Custom types: structure, bit segment, enumeration, union

content

structure

declaration of structure

special statement

self-referencing of structs

Definition and initialization of structure variables

Structure memory alignment

 Why does memory alignment exist?

Modify the default alignment

structure parameter

bit segment

Memory allocation for bit fields

Cross-platform issues with bit fields 

Application of Bit Segments

enumerate 

Definition of enumeration types

Advantages of enumeration

use of enumeration

union (community)

Definition of Union Types

joint features

Calculation of joint size


structure

Getting to know the structure

declaration of structure


For example to describe a student:

struct Stu
{
	char name[20]; 
	int age; 
	char sex[5]; 
	char id[20];
}s1, s2, s3; // 分号不能丢

struct Stu s4;//全局变量
int main()
{
	struct Stu s5;//局部变量
	return 0;
}

special statement

When declaring a structure, you can declare it incompletely.   You can only use it once, you can't use it again

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

The above two structures are declared without the structure tag (tag).  

// 在上面代码的基础上,下面的代码合法吗?
p = & x ;

If the members of an anonymous structure are the same, they are also different types of structures in the eyes of the compiler!!!!

So it's illegal

self-referencing of structs

This thing is often used in linked lists in data structures

Is it OK to have a member of type the struct itself in a struct?

struct Node
{
	int data;
	struct Node next;
};
// 可行否?   不可以的,因为会一直循环包括自己

If so, what is sizeof ( struct Node )?
The correct way to self-reference:

struct Node
{
	int data;
	struct Node* next;
};
//typedef 就是类型重命名
typedef struct
{
	int data;
	Node* next;
} Node;
// 这样写代码,可行否?

No, it's the chicken or the egg question

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

Definition and initialization of structure variables

With the structure type, how to define variables is actually very simple.

struct Point
{
	int x;
	int y;
} p1; // 声明类型的同时定义变量 p1

struct Point p2; // 定义结构体变量 p2

// 初始化:定义变量的同时赋初值。
struct Point p3 = { x , y };

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

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

Structure memory alignment

Now let's dive into a problem: calculating the size of a structure.
This is also a particularly popular test site: structure memory alignment

#include <stdio.h>

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

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

struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	printf("%d\n", sizeof(struct s1));
	printf("%d\n", sizeof(struct s2));
	printf("%d\n", sizeof(struct S3));
	printf("%d\n", sizeof(struct S4));
	return 0;
}

Why does this result occur?

How to calculate it?

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

1. The first member is at the address at offset 0 from the structure variable.
2. Other member variables should be aligned to an address that is an integer multiple of a number (alignment number).
Alignment = the smaller of the compiler's default alignment and the size of the member.
The default value in VS is 8. (If there is no default alignment number, then 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.

 

 

offsetof - macro
that calculates the offset of a struct member relative to the starting position

struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

#include <stddef.h>

int main()
{
	printf("%u\n", offsetof(struct S4, c1));
	printf("%u\n", offsetof(struct S4, s3));
	printf("%u\n", offsetof(struct S4, d));
	return 0;
}

 Why does memory alignment exist?

1. Platform reasons (migration 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 .
 

When designing the structure, we must not only satisfy the alignment, but also save space, so that the members that occupy the small space should be concentrated as much as possible.

Modify the default alignment

We have seen the #pragma preprocessor directive before, here we use it again to change our default alignment.

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

 

Conclusion:
When the alignment of the structure is not suitable, we can change the default alignment by ourselves.

Baidu written test questions:
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

structure parameter

Go directly to the code:

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

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 the structure is passed parameters, the address of the structure must be passed.

bit segment

The declaration and structure of bit fields are similar, with two differences:

1. The members of the 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 .

(The bits of the bit segment personally think that they can be understood as bits, that is, to allocate bits)

for example:

#include <stdio.h>

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

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

 

 

Memory allocation for bit fields

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 4 bytes ( int ) or 1 byte ( char ) as needed to open up.
3. The bit segment involves many uncertain factors. The bit segment is not cross-platform. Programs that focus on portability should avoid using the bit segment.

If the number is too large, high-order truncation will occur when the bit segment is truncated.

If the bit field is too large, one bit (8 bytes) cannot be placed, and low-order truncation will occur when storing in memory 

Cross-platform issues with bit fields 

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. (The 16-bit machine has a maximum of 16, and a 32-bit machine has a maximum of 32, which is written as 27, which will cause problems on a 16-bit machine.
3. The members of the bit segment are allocated in memory from left to right, or from right to left. The standard is not yet defined.
4. When a structure contains two bit segments, and the members of the second bit segment are relatively large and cannot 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.

Application of Bit Segments

This is an IP datagram

 

enumerate 

Enumeration, as the name suggests, is an enumeration.
List the possible values.
For example, in our real life:
gender is: male, female, confidential,

Colors: red, green, blue (three primary colors)

It can be listed one by one.

Definition of enumeration types

enum Sex // 性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color // 颜色
{
	RED,
	GREEN,
	BLUE
};


Advantages of enumeration

We can use #define to define constants, why use enums?
The advantages of enumeration:
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. Prevent naming pollution (encapsulation)
4. Easy to debug
5. Easy to use, you can define multiple constants at a time

use of enumeration

#include <stdio.h>

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

int main()
{
	enum Color clr = GREEN; // 只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
	printf("%d\n", clr);
	return 0;
}


 

union (community)

Definition of Union Types

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 the union is also called a union ).
for example:

#include <stdio.h>

union Un
{
	char c;
	int i;
};

int main()
{
	union Un un;
	// 计算连个变量的大小
	printf("%d\n", sizeof(un));
	return 0;
}

Why is it 4?

 

joint features

The members of the union share the same memory space , so the size of a union variable is at least the size of the largest member (because the union must at least be able to hold the largest member ).
    

union Un
{
	int i;
	char c;
};
int main()
{
	union Un un;
	// 下面输出的结果是一样的吗?
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));
	// 下面输出的结果是什么?
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	return 0;
}


 How is this union placed in memory?

 

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

for example:  

#include <stdio.h>

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

 

Guess you like

Origin blog.csdn.net/qq_54880517/article/details/124262283