C language - custom type (structure, enumeration, union)

structure

A structure is a collection of values ​​called member variables. Each member of the structure can be a variable of a different type.

declaration of the structure

struct tag
{
    
    
 	member-list;
}variable-list;

struct——关键字
tag——结构体的标签名
struct tag——结构体类型名
member-list——成员列表(成员变量的列表)
variable-list——变量列表

Example 1 (describing a book):

struct Book
{
    
    
	char name[20];
	int price;
	char id[12];
}b4,b5,b6;//b4,b5,b6是全局的


int main()
{
    
    
	//b1,b2,b3是局部变量
	struct Book b1;
	struct Book b2;
	struct Book b3;


	return 0;
}

special statement

When declaring a structure, it can be incomplete.

Example one:

//匿名结构体类型
//两个结构在声明的时候省略掉了结构体标签(tag)
struct
{
    
    
 	int a;
 	char b;
 	float c;
}x;
struct
{
    
    
 	int a;
 	char b;
 	float c;
}a[20], *p;

int main()
{
    
    
	p=&x;//会报警告
	//注:在编译器看来虽然结构体中的成员是一模一样的,但编译器仍然认为这两个结构体是不同的类型(在编译器看来是不合理的)
	return 0;
}

Note: An anonymous structure type can only be used once after it is created (no label can form a structure type)

self-referencing structure

Example one:


struct Node
{
    
    
 	int data;
 	struct Node next;
};
//不可以这样定义(这样会死递归的)

Note: It is not allowed to contain a member of the type of the structure itself in the structure

Structs are self-referencing:

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

Note:

  • A data structure is a structure in which data is stored in memory
  • Linear data structure: sequential list, linked list; tree data structure: binary tree
  • The nodes in the linked list are divided into two parts: the data field (which stores data) and the pointer field (which stores the address of the next node)

Summary: Structure self-references do not contain structure variables of the same type but pointers to structures of the same type

Example two:

typedef struct
{
    
    
 	int data;
 	Node* next;
}Node;
//这样写代码,是错误的
//先有Node*定义出的成员才能对类型进行重命名产生Node(陷入先有蛋还是先有鸡的问题)
	
//解决方案:
typedef struct Node
{
    
    
 	int data;
 	struct Node* next;  //重命名的Node不能在成员里用
}Node;

Definition and initialization of structure variables

Example one:

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};//结构体嵌套初始化

Example two:

struct S
{
    
    
	char c;
	int i;
}s1, s2;

struct B
{
    
    
	double d;
	struct S s;
	char c;
};
int main()
{
    
    
	struct S s3 = {
    
    'x', 20};
	struct B sb = {
    
     3.14, {
    
    'w', 100},'q' };
	//. 
	//->
	printf("%lf %c %d %c\n", sb.d, sb.s.c, sb.s.i, sb.c);

	return 0;
}

Note:

  • When the structure is not fully initialized, the default value of the remaining uninitialized is 0
  • Structures are directly assignable

Structure memory alignment

Example one:

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

insert image description here
analyze
insert image description here

Alignment rules for structures:

  • The first member is at offset 0 from the structure variable.
  • Other member variables should be aligned to an address that is an integer multiple of a certain number (alignment number).
  • The total size of the structure is an integer multiple of the maximum alignment (each member variable has an alignment).
  • If a structure is nested, the nested structure is aligned to an integer multiple of its own maximum alignment , and the overall size of the structure is an integer multiple of all maximum alignments (including the alignment of the nested structure).

Note:

  • The offset is the difference between the address of the member in the structure variable and the address of the structure variable (the offset is the offset relative to the starting position of the structure).
  • The default alignment number of VS is 8, and linux has no alignment number (that is, the byte size of this member is the alignment number)
  • Alignment = Compiler's default alignment and the smaller value of the member size.
  • For the GCC compiler in Linux, the alignment number is the size of this member

Example two:

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

Parse:
insert image description here

Why does memory alignment exist?

Most of the references say something like this:

  • Platform reasons (reasons for porting): 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.
  • 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 structures is a practice of exchanging space for time.

When designing a structure, it is necessary to satisfy alignment and save space. Members who occupy a small space can be gathered together as much as possible by **. ** So as to achieve the purpose of saving space

Example three:

insert image description here

insert image description here

It can be seen that the members of the two structures are the same, but the order is different, so the size of the memory occupied by the structures is also different

Modify the default alignment

#pragmg pack(N) //设置默认对齐数为N

Example one:

#include<stdio.h>
//默认对齐数是8
//把默认对齐数改为2
#pragma pack(2)
struct S
{
    
    
	char c1;        //结构体S按照默认对齐数为2进行内存对齐
	int i;
	char c2;
};
#pragma pack()  //取消设置的默认对齐数,还原为默认
int main()
{
    
    
	struct S s = {
    
     0 };
	printf("%d\n", sizeof(s));
	return 0;
}

insert image description here
analyze
insert image description here

Note:

  • When setting the default alignment number, it is generally set to 2^n, and it is almost never set to an odd number
  • #pragma pack(1) sets the default alignment to 1, and there is no memory alignment of the structure
  • The meaning of modifying the default alignment is that when the alignment of the structure is not suitable, the programmer can change the default alignment by himself

offsetofmacro

offsetof is a macro in the C language itself, which is used to calculate the offset of a member in the structure compared to the starting position of the structure variable.
insert image description here

Use of offsetof macro

insert image description here

Struct parameter passing

Example one:

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

Note: The printf1 function has the same function as the printf2 function, but the printf2 function has better performance than the printf1 function (lower time and space consumption). The reason is that when a function passes parameters, the parameters need to be pushed onto the stack, which will cause 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 on the stack is relatively large, which will lead to a decrease in performance. Therefore, when the structure is passed as a parameter, the address of the structure must be passed .

bit segment

The bit field is attached to the structure

Bit field definition (declaration)

Bit field declarations and structures are similar, with two differences:

  1. Members of bit fields must be int, unsigned int or signed int or char.
  2. A colon and a number follow the member name of a bit field

Bit segment definition:

struct A   //A就是一个位段类型
{
    
    
 	int _a:2;
 	int _b:5;
 	int _c:10;
 	int _d:30;
};

bit segment memory allocation

Example one:

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

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

insert image description here
Note: After the colon + the number is how many bits are occupied.

analyze

struct A
{
    
    
	//4个字节-32bit
	int _a : 2;//成员占2个bit位
	int _b : 5;//成员占5个bit位
	int _c : 10;//成员占10个bit位
	//4个字节-32bit
	int _d : 30;//成员占30个bit位  
	 
	//int _d : 30;中的_d位段成员在内存中开辟30个比特位是用之前开辟完剩余的比特位和新开辟的32位比特位还是只用新开辟的32位比特位,这是不确定的。因为C语言没有明确规定到底用不用前面的空间。
};
int main()
{
    
    
	printf("%d\n", sizeof(struct A));//4+4=8
	return 0;
}

bit segment memory allocation

  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 (implementation methods are different on different platforms), and programs that focus on portability should avoid using bit segments.

Example two:

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

Analysis (the analysis is only available for VS)
insert image description here

insert image description here

Note:

  • The data inside the next byte (four bytes) of the VS compiler uses the low-bit data first and then the high-bit data (used from right to left)
  • When the content of the remaining space in a space is not enough for the next member, this space will be wasted

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 machine int is up to 16, 32-bit machine int is up to 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 the remaining bits or use them.

Replenish:

  • The integer type occupies 2 bytes on a 16-bit machine; the integer type occupies 4 bytes on a 32-bit machine

Summary: 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 bits

Format of IP datagram

insert image description here

enumerate

An enumeration is a list of possible values.
Enumeration is to create a new type, the value of this type is very limited (clear several values), then you can use enumeration to create enumeration type

Definition and use of enumerated types

Example one:

//声明枚举类型

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

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

Example two:

enum Color  //enum枚举关键字  Color枚举的类型
{
    
    
	RED,  
	GREEN,    //枚举类型的可能取值   这些都是枚举常量
	BLUE
};
int main()
{
    
    
	enum Color c = BLUE;   //在C++中可以这样写enum Color c = Color::BLUE; ::(限定符),BLUE来自于Color
	return 0;
}

Note:

  • The enumeration type is a descriptive enumeration used to describe the limited types of values ​​that can be determined one by one, while the structure is used to describe complex objects.
  • An enumeration type is a type
  • enumeration of values ​​whose possible values ​​are integers

Main points:

  • The content in {} is the possible value of the enumeration type, also called enumeration constant.
  • These possible values ​​are all valid, starting from 0 by default and increasing by 1 at a time. Of course, the initial value can also be assigned when defining.

Example three:

insert image description here
insert image description here

Note:

  • Cannot be written as enum Color c=2; the syntax is not supported in the C++ compiler (int type cannot be converted to enum Color type) type mismatch
  • Enumeration constants cannot be modified, but initial values ​​can be assigned

Example four:

//声明枚举类型
enum Color
{
    
    
	RED=5,//5
	GREEN=8,//8
	BLUE//9
};

int main()
{
    
    

	enum Color c = BLUE;
	printf("%d\n", sizeof(c));  //4
	//计算枚举类型的大小时它只有一种可能取值,所以它的大小就是整形大小
	return 0;
}

Note: The size of the memory occupied by the enumeration type is always 4

Advantages of Enums

The difference between enumeration and #define definition constant:

  1. Increases code readability (numbers are replaced by their corresponding symbol names, which are readable) and maintainability
  2. Compared with the identifier defined by #define, the 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

Note: Debugging code can only be debugged when running the code after the executable program is generated

Union (community)

union type definition

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 unions are also called unions).

Declaration and definition of union type:

//联合类型的声明
union Un 
{
    
     
 	char c; 
 	int i; 
}; 

//联合变量的定义
union Un un; 

//计算联合变量的大小
printf("%d\n", sizeof(un));

Example one:

union Un
{
    
    
	char c;//1
	int i;//4
};
int main()
{
    
    
	union Un u = {
    
    10};
	//union Un u = { 10,1000 };  //err  联合体多个成员共用一块空间初始化时不可能对多个成员进行初始化,其实只初始化一个成员
	u.i = 1000;
	u.c = 100;

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

	printf("%d\n", sizeof(u));  //4

	return 0;
}

insert image description here

Note: The union can only use one of its members at a time

Example two:

insert image description here

insert image description here

Note: The first member of the union will be initialized when the union is initialized

joint features

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 (because the union must at least have the ability to save the largest member).

Example one:

insert image description here

Classic interview:

Determine the size and endian storage of the current computer

//判断当前计算机的大小端存储


int check_sys1()
{
    
    
	int a = 1;
	if ((*(char*)&a) == 1)
	{
    
    
		return 1;//小端
	}
	else
	{
    
    
		return 0;//大端
	}
}

int check_sys2()
{
    
    
	union U
	{
    
    
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
	//返回1 就是小端
	//返回0 就是大端
}

int main()
{
    
    
	int ret = check_sys2();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

Calculation of joint size

Union size calculation rules:

  • 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.

Example one:

union Un
{
    
    
	char a[5];//对齐数1(相当于放了5个字符类型的变量)    
	int i;//对齐数4
	
};

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

	return 0;
}


Example two:

union Un
{
    
    	
	short s[5];//对齐数2 
	int a;//对齐数4
};

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

	return 0;
}

Guess you like

Origin blog.csdn.net/AI_ELF/article/details/130644334