Conquer custom types of C language: structure, enumeration, union

Posted by CtrlAltDel on Tue, 08 Feb 2022 06:25:51 +0100

catalogue

Key points of this chapter

1. Structure

1.1 basic knowledge of structure

1.2 declaration of structure

1.3 special declaration

1.4 self reference of structure

1.5 definition and initialization of structure variables

1.6 structure memory alignment

1.7 modify the default alignment number

1.8 structural transmission parameters

2. Bit segment

2.1 what is a bit segment

2.2 memory allocation of bit segment

2.3 cross platform problem of bit segment

2.4 application of bit segment

3. Enumeration

3.1 definition of enumeration type

3.2 advantages of enumeration

3.3 use of enumeration

4. Consortium

4.2 characteristics of joint

4.3 calculation of joint size

5. Practice

Address book - static version

Key points of this chapter

structural morphology
  • Declaration of structure type
  • Self reference of structure
  • Definition and initialization of structure variables
  • Structure memory alignment
  • Structural transmission parameters
  • Struct implementation bit segment (filling & portability of bit segment)
enumeration
  • Definition of enumeration type
  • Advantages of enumeration
  • Use of enumerations
union
  • Definition of union type
  • Characteristics of joint
  • Calculation of joint size

1. Structure

1.1 basic knowledge of structure

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

1.2 declaration of structure

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

For example, describe a student:
struct Stu
{
        char name [ 20 ]; // name
        int age ; // Age
        char sex [ 5 ]; // Gender
        char id [ 20 ]; // Student number
}; // Semicolons cannot be lost

1.3 special declaration

For example:

// Anonymous structure type
struct
{
        int a ;
        char b ;
        float c ;
} x ;
struct
{
        int a ;
        char b ;
        float c ;
} a [ 20 ], * p ;
The above two structures omit the structure label when declaring( tag ).
So here comes the question...?
// Based on the above code, is the following code legal?
p = & x ;
Warning:
The compiler will treat the above two declarations as two completely different types.
So it's illegal.

1.4 self reference of structure

Can a member whose type is the structure itself be included in the structure?
// code 1
struct Node
{
        int data ;
        struct Node next ;
};
// Is it feasible?
If you can, then sizeof ( struct Node ) How much is it?
  • A structure cannot nest itself

Correct self reference method:

// code 2
struct Node
{
        int data ;
        struct Node * next ;
};

be careful

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef struct
{
	int data;
	Node* next;
}Node;

int main()
{
	Node n;

	return 0;
}

  • There is a problem with the above writing, and the compiler will still report an error,

Correct writing

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
typedef struct Node
{
	int data;
	struct Node* next;
}Node;

int main()
{
	Node n;

	return 0;
}

1.5 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 ; // Defining variables while declaring types p1
struct Point p2 ; // Define structure variables p2
// Initialization: define variables and assign initial values at the same time.
struct Point p3 = { x , y };
struct Stu         // Type declaration
{
        char name [ 15 ]; // name
        int age ;       // Age
};
struct Stu s = { "zhangsan" , 20 }; // initialization
struct Node
{
        int data ;
        struct Point p ;
        struct Node * next ;
} n1 = { 10 , { 4 , 5 }, NULL }; // Structure nesting initialization
struct Node n2 = { 20 , { 5 , 6 }, NULL }; // Structure nesting initialization

By the way, access to structures:

  • For direct access Operator
  • Pointer reuse for indirect access - > operator

1.6 structure memory alignment

We have mastered the basic use of structure.
Now let's discuss a problem in depth: calculating the size of the structure.
This is also a particularly popular test site: Structure memory alignment
Test site: Structure memory alignment
How to calculate ?
First, you must master the alignment rules of the structure:
1. The first member is offset from the structure variable by 0 At your address.
2. Other member variables should be aligned to the address of an integer multiple of a number (alignment number).
Alignment number = The default alignment number of the compiler is the same as the size of the member Smaller value .
The default value in VS is 8
3. The total size of the structure is an integer multiple of the maximum number of alignments (each member variable has an alignment number).
4. If a structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment number, and the integer of the structure is zero
The volume size is an integer multiple of the maximum number of alignments (including the number of alignments of nested structures).
Code one
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};

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

Code two

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S2
{
	char c1;
	char c2;
	int i;
};

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

Code three

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S3
{
	double d;
	char c;
	int i;
};

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

Code four

#define _CRT_SECURE_NO_WARNINGS 1
#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;
}

Why is there memory alignment ?
Most references say this:
1. Platform reason ( Reasons for transplantation ) :
Not all hardware platforms can access any data on any address; Some hardware platforms can only take some special data at some addresses
Set the type of data, or throw a hardware exception.
2. Performance reasons :
data structure ( Especially stack ) It should be aligned on natural boundaries as much as possible.
The reason is that in order to access the misaligned memory, the processor needs to make two memory accesses; Aligned memory access requires only one access
ask
for instance:
  • Memory alignment exists, and all bytes of i can be obtained at one time
  • If there is no memory alignment and i is taken, you need to access it twice
on the whole:
The memory alignment of the structure is space In exchange for time Practice.

When designing the structure, we should not only meet the alignment, but also save space. How to do this:

Let the members who occupy less space gather together as much as possible.   

for example

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};

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

 

S1 is as like as two peas of S2 type, but the size of S1 and S2 occupies some difference.

1.7 modify the default alignment number

We met before #pragma This preprocessing instruction, which we use again here, can change our default alignment number.
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#pragma pack(2)
struct S
{
	char c1;
	int i;//
	char c2;//
};

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

Conclusion:
When the alignment of the structure is inappropriate, we can change the default alignment number by ourselves.
Baidu essay:
Write a macro to calculate the offset of a variable in the structure relative to the first address, and give a description
investigate: offsetof Macro implementation
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stddef.h>
#pragma pack(2)
struct S
{
	char c1;
	int i;
	char c2;
};

int main()
{
	printf("%d\n", offsetof(struct S, c1));
	printf("%d\n", offsetof(struct S, i));
	printf("%d\n", offsetof(struct S, c2));

	return 0;
}

  • What is printed here is the offset of the members C1, I and C2 of struct S just now

Note: the macro has not been learned here. It can be implemented after the macro is explained.

Here's another word: gcc in Linux has no alignment number

1.8 structural transmission parameters

Direct code:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S 
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//Structural transmission parameters
void print1(struct S s) 
{
	printf("%d\n", s.num);
}
//Structure address transmission parameter
void print2(struct S* ps) 
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);//Transmission structure
	print2(&s); //Transmission address
	return 0;
}

  • Although the result is the same, the print2 function is preferred.
  • When transferring parameters to a function, the parameters need to be pressed on the stack, which will have system overhead in time and space.
  • If the structure is too large when passing a structure object, the system overhead of parameter stack pressing is relatively large, which will lead to performance degradation.
  • I'll elaborate later. Now it's a little troublesome to explain

Conclusion:
When the structure passes parameters, the address of the structure should be passed.

2. Bit segment

After the structure is finished, we have to talk about the ability of the structure to realize the bit segment.

2.1 what is a bit segment

The declaration and structure of bit segments are similar, with two differences:
1. The member of the bit segment must be int , unsigned int or signed int .
2. The member name of the bit field is followed by a colon and a number.
  • In fact, char is OK
For example:
struct A
{
        int _a : 2 ;
        int _b : 5 ;
        int _c : 10 ;
        int _d : 30 ;
};
A It's A bit segment type, segment A What is the size of the?
I won't print here. If you are interested, you can try:

printf("%d\n", sizeof(struct A));

2.2 memory allocation of bit segment

1. The members of the bit segment can be int unsigned int signed int Or char (belonging to plastic family)
2. The space of bit segments is as needed 4 Bytes( int )Or 1 Bytes( char )The way to open up.
3. Bit segment involves many uncertain factors. Bit segment is not cross platform. Pay attention to portable programs and avoid using bit segment.

Look at the code

#define _CRT_SECURE_NO_WARNINGS 1
#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;
}

 

 

  • The size end is considered only when the byte order is used. It is not considered here
  • As mentioned above: bit segments involve many uncertain factors, and bit segments are not cross platform
  • Here I tested it in vs2019 for reference only

2.3 cross platform problem of bit segment

1. It is uncertain whether the int bit field is regarded as a signed number or an unsigned number.
2. The number of the largest bits in the bit segment cannot be determined. (16 bit machine max. 16, 32-bit machine max. 32, written as 27, on 16 bit machine)
There will be problems with the filter.
3. Whether the members in the bit segment are allocated from left to right or from right to left in memory has not been defined.
4. When a structure contains two bit segments, and the second bit segment has a large member and cannot accommodate the remaining bits of the first bit segment, yes
It is uncertain whether to discard the remaining bits or use them.

Summary:

Compared with the structure, bit segment can achieve the same effect, but it can save space, but there are cross platform problems.  

2.4 application of bit segment

Let me give you a simpler example,

There are four combinations of two bits: 00, 01, 10, 11,

  • Gender: male, female, confidential. Two bits are enough. Char a: 2 is enough, not int a
  • Sometimes using bit segments saves more space than defining variables and structures

3. Enumeration

Enumeration, as the name suggests, is to enumerate one by one.
List the possible values one by one.
For example, in our real life:
Monday to Sunday is a limited week 7 Days, you can list them one by one.
Gender: male, female and confidential, which can also be listed one by one.
Month has 12 Months, you can also list them one by one

You can use enumeration here.

3.1 definition of enumeration type

enum Day // week
{
        Mon ,
        Tues ,
        Wed ,
        Thur ,
        Fri ,
        Sat ,
        Sun
};
enum Sex // Gender
{
        MALE ,
        FEMALE ,
        SECRET
} ;
enum Color // colour
{
        RED ,
        GREEN ,
        BLUE
};
As defined above enum Day , enum Sex , enum Color Are enumeration types.
{} The content in is the possible value of enumeration type, also known as enumeration constant .
These possible values all have values. The default value is from 0 Start, one increment 1 Of course, initial values can also be assigned when defining.
for example
:
enum Color // colour
{
        RED = 1 ,
        GREEN = 2 ,
        BLUE = 4
};

3.2 advantages of enumeration

Why use enumeration?
We can use #define When defining constants, why do you have to use enumerations?
Advantages of enumeration:
  1. Increase the readability and maintainability of the code
  2. Compared with #define defined identifiers, enumeration has type checking, which is more rigorous.
  3. Prevents naming contamination (encapsulation)
  4. Easy to debug
  5. Easy to use, you can define multiple constants at a time

By the way, the macros and characters defined by define are replaced in preprocessing

3.3 use of enumeration

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void menu()
{
	printf("*****************************\n");
	printf("****  1. add    2. sub  *****\n");
	printf("****  3. mul    4. div  *****\n");
	printf("****  0. exit          *****\n");
	printf("*****************************\n");
}

enum Option
{
	EXIT,//0
	ADD,//1
	SUB,//2
	MUL,//3
	DIV,//4
};

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("Please select:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD://case 1:
			break;
		case SUB://case 2:
			break;
		case MUL://case 3:
			break;
		case DIV://case 4:
			break;
		case EXIT://case 5:
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}
  • Enumeration is used to improve the readability of the code,
  • Using enumeration is simpler than defining characters directly,

4. Consortium

4.1 Definition of union type
Federation is also a special custom type
Variables defined by this type also contain a series of members, which are characterized by the fact that these members share the same space (so the union is also called a common body).
For example:
//Declaration of union type
union Un
{
        char c ;
        int i ;
};
//Definition of joint variables
union Un un ;
//Calculate the size of multiple variables
printf ( "%d\n" , sizeof ( un ));

4.2 characteristics of joint

The members of the union share the same memory space. The size of such a union variable is at least the size of the largest member (because the union must be able to save the largest member at least).
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
union Un
{
	char c;//1
	int i;//4
};

int main()
{
	union Un u = {10};
	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));//

	return 0;
}

 

Face to face questions:

Determine the storage size of the current computer

Code one

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int check_sys()
{
	int a = 1;
	if ((*(char*)&a) == 1)
	{
		return 1;//Small end
	}
	else
	{
		return 0;//Big end
	}
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("Small end\n");
	else
		printf("Big end\n");

	return 0;
}
  • In the previous chapters, I also said to judge the size of the computer. I used (char *)void *, and then judged whether the first byte was 1,

Code two

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int check_sys()
{
	union U
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
	//Return 1 is the small end
	//Return 0 is the big end
}

int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("Small end\n");
	else
		printf("Big end\n");

	return 0;
}

 

  • The above is implemented by using the consortium method, mainly because the address of the consortium is shared, and the addresses of char c and int i are the same

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

For example:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
union Un
{
	char a[5];//1    5
	int i;//4
	char c;//1
};

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

	return 0;
}

  • The maximum member size is 5 and the maximum number of alignments is 4, so the result is 8

For example:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
union Un
{
	short s[5];//2 10
	int a;//4
};

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

	return 0;
}

  • The maximum member size is 10 and the maximum number of alignments is 4, so the result is 12

5. Practice

Address book - static version

  1. The address book can store the information of 1000 people. Each person's information: Name + age + gender + phone + address
  2. Add human information
  3. Delete the information of the designated person
  4. Modify the information of the designated person
  5. Find information about the specified person
  6. Sort address book information

Address book - static version

  • Here's a warning. I'll use dynamic memory to solve it later

---------------------------------------------------------------------------------------------------------------------------------

End of this chapter

Topics: C Back-end