Foreword
This article will introduce three custom types in C language: structure, enumeration and union. These data types are very important in C language, they allow programmers to create their own data types to meet specific needs. By understanding these data types, you can better grasp the characteristics and applications of C language.
1. Structure
Basic knowledge of structures
The structure is
some value
A collection of values called member variables. Each member of the structure can be a variable of different types.
Note: Unlike an array, an array is a collection of elements of the same type, and the structure member types can be different.
1 Structure declaration
struct
tag{
member
–
list
;
//Member name}
variable
–
list
;
//variable name
For example, describe a student:
struct Stu { char name[20];//name int age;//Age char sex[5];//Gender char id[20];//student number }; //The semicolon cannot be lost
1.2 Special Statement
- When declaring a structure, you can declare it incompletely.
//
Anonymous structure typestruct
//The structure omits the structure tag when declaring it{
int
a
;char
b
;float
c
;}
x
;struct
//The structure omits the structure tag when declaring it{
int
a
;char
b
;float
c
;}
a
[
20
],
*
p
;
So here’s the problem?
//
Based on the above code, is the following code legal?p
=&
x
;
Warning:
The compiler will treat the two declarations above as two completely different types.
So it’s illegal.
1.3 Self-reference of structure
-
Is it okay to include a member in a structure that is of type the structure itself?
like:
//
code
1struct
Node{
int
data
;struct
Node next
;};
//
Is it possible?If possible, then
sizeof
(
struct
Node
)
how many?
Warning: This way of writing is not possible, it is similar to the recursive behavior of functions; structures
struct
Node
It will continue to expand and eventually cause stack overflow. so
sizeof(struct Node
) cannot be calculated.
Correct way to self-reference:
//
code
2struct
Node{
int
data
;struct
Node
*
next
;
//The linked list in the data structure records the position of the next structure (node)};
2. Special errors:
//
code
3typedef struct
{
int
data
;Node
*
next
;}
Node
;//
Is it possible to write code like this?
warn:
typedef
Even if the type name redefinition has not been executed, a warning of undefined identifier will be reported.
//
solution:typedef struct
Node{
int
data
;struct
Node
*
next
;}
Node
;
1.4 Definition and initialization of structure variables
With the structure type, how to define variables is actually very simple!
Method 1: Declare the variable in the main function after defining the type without initializing it
struct
Point{
int
x
;int
y
;}
p1
;
//
Declare the type and define the variable at the same time
p1struct
Point p2
;
//
Define structure variables
p2
Method 2: Declare variable initialization in the main function after defining the type
//
Initialization: Define a variable and assign an initial value at the same time.struct
Point p3
=
{
x
,
y
};struct
Stu
//
type declaration{
char
name
[
15
];
//
nameint
age
;
//
age};
struct
Stu s
=
{
“zhangsan”
,
20
};
//
initialization
Method 3: Nested initialization of the structure after defining the type
struct
Node{
int
data
;struct
Point p
;struct
Node
*
next
;}
n1
=
{
10
, {
4
,
5
},
NULL
};
//
Structure nested initializationstruct
Node n2
=
{
20
, {
5
,
6
},
NULL
};
//
Structure nested initialization
1.5 Structure memory alignment
We have mastered the basic use of structures.
Now let’s delve into a problem: calculating the size of a structure.
This is also a particularly popular test site:
Structure memory alignment
1.5.1 How to calculate ?
First, you must master the alignment rules of structures:
1.
The offset of the first member from the structure variable is
0
address.2.
Other member variables should be aligned to addresses that are integer multiples of a certain number (alignment number).Number of alignments
=
The compiler defaults an alignment number equal to the size of the member
Smaller value
.VS
The default value in
8
Different compilers use different default values3.
The total size of the structure is an integer multiple of the maximum alignment number (each member variable has an alignment number).4.
If the structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment number, and the overall size of the structure is all
Maximum number of alignments
(Including the number of alignments of nested structures)
an integer multiple of
.
Now that you have learned how to calculate, let’s try it out in practice.
struct S2 { char c1; char c2; int i; }; printf("%d\ ", sizeof(struct S2)); //The space size is 8 bytes
1.5.2 Why does memory alignment exist?
Most of the reference materials say this:
1.
Platform reasons
(
Reason for transplantation
)
: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 whenever possible.
The reason is that in order to access unaligned memory, the processor needs to make two memory accesses; aligned memory access requires only one access.
In general:
The memory alignment of the structure is
Space
in exchange for
Time
way of doing.
When designing a structure, we must not only satisfy alignment but also save space. How to do this:
//
For example:struct
S1{
char
c1
;int
i
;char
c2
;};
struct
S2{
char
c1
;char
c2
;int
i
;};
//The members of type S1 and S2 are exactly the same, but there are some differences in the size of the space occupied by S1 and S2.
So when designing a structure:
It would be a wise choice to keep members who take up less space together as much as possible! .
1.6 Modify the default alignment number
we’ve met before
#pragma
This preprocessing directive, which we use again here, changes our default alignment number.
#include
#pragma pack(8)
//
Set the default alignment number to
8struct
S1{
char
c1
;int
i
;char
c2
;};
#pragma pack()
//
Cancel the set default alignment number and restore it to default#pragma pack(1)
//
Set the default alignment number to
1struct
S2{
char
c1
;int
i
;char
c2
;};
#pragma pack()
//
Cancel the set default alignment number and restore it to defaultint
main
(){
//
What is the output?printf
(
“%d\
”
,
sizeof
(
struct
S1
));printf
(
“%d\
”
,
sizeof
(
struct
S2
));return
0
;}
Conclusion:
When the alignment of the structure is inappropriate, we can change the default alignment number ourselves.
Exercise:
Write a macro to calculate the offset of a variable in the structure relative to the first address and give an explanation
Inspection:
offsetof
Macro implementation
#define
my_offsetof
(type,name) ((
size_t)
((
char*
)( & amp;(( ( (
struct S2
*)
NULL
)->name))-(char*)
NULL
))
//This method uses a null pointer to convert it into a structure pointer of type type, then accesses the members to obtain its address, and then subtracts it from the starting address of the structure.
1.8 Structure parameter passing
Which of the following print1 or print2 functions is better?
struct
S{
int
data
[
1000
];int
num
;};
struct
S s
=
{{
1
,
2
,
3
,
4
},
1000
};//
Structure parameter passingvoid
print1
(
struct
S s
){
printf
(
“%d\
”
,
s
.
num
);}
//
Structure address passing parametersvoid
print2
(
struct
S
*
PS
){
printf
(
“%d\
”
,
PS
->
num
);}
int
main
(){
print1
(
s
);
//
Pass structureprint2
(
&
s
);
//
Pass addressreturn
0
;}
The answer is: preferred
print2
function.
Because
- 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 and the structure is too large, the system overhead of pushing parameters onto the stack will be relatively large, which will lead to performance degradation.
2. Position
After talking about structures, we have to talk about the ability of structures to realize bit segments.
2.1 What is a bit segment
The declaration and structure of bit fields are similar, with two differences:
- Members of a bit field must be int, unsigned int, or signed int.
- The member name of the bit field is followed by a colon and a number.
for example:
struct
A
//A is a bit segment type
.{
int
_a
:
2
;int
_b
:
5
;int
_c
:
10
;int
_d
:
30
;};
2.2 Memory allocation of bit segments
- Members of the bit field can be int unsigned int signed int or char (belonging to the integer family) type
- The space of the bit field is allocated in 4 bytes (int) or 1 byte (char) as needed.
- Bit segments involve many uncertainties. Bit segments are not cross-platform. Programs that focus on portability should avoid using bit segments.
Demo:
struct S { char a:3; char b:4; char c:5; chard:4; }; struct S s = {0}; s.a = 10; s.b = 12; s.c = 3; s.d = 4;
2.3 Cross-platform issues with bit segments
1. int
It is undefined whether a bit field is treated as a signed or unsigned number.2.
The maximum number of bits in a bit field cannot be determined. (
16
Bit machine maximum
16
,
32
Bit machine maximum
32
, written as
27
,exist
16
bit machineThere will be problems with the machine.
3.
The criteria for whether members of a bit field are allocated from left to right or right to left in memory are undefined.4.
When a structure contains two bit fields, and the members of the second bit field are larger and cannot fit in the remaining bits of the first bit field, it isIt is uncertain whether to discard the remaining bits or to use them.
Summary:
Compared with the structure, the bit segment can achieve the same effect, but can save space very well, but there are cross-platform problems.
3. Enumeration
Enumeration, as the name suggests, means enumerating items one by one.
List the possible values one by one.
For example, in our real life:
Monday to Sunday of the week is limited
7
God, you can list them all.
Enumerations can be used here.
3.1 Definition of enumeration types
enum
ttype-name
//
Enumeration type name{
//Member name
};
//variable name
The enumeration type starts from 0 by default and increments by 1 at a time. Of course, an initial value can also be assigned when defining.
enum
Color
//
color{
RED
=
1
,GREEN
=
2
,BLUE
=
4};
Note: Enumeration type members will continue to be automatically incremented by 1 from the member you assigned.
3.2 Advantages of enumerations
We can use #define to define constants, why do we have to use enumerations?
Advantages of enumerations:
- Increase code readability and maintainability
- Compared with identifiers defined by #define, enumerations have type checking, which is more rigorous.
- Prevented naming pollution (encapsulation)
- Easy to debug
- Easy to use, multiple constants can be defined at one time
IV. Union (community)
4.1 Definition of union types
Union is also a special custom type. The variables defined by this type also contain a series of members. The characteristic is that these members share the same space (so union is also called a union).
for example:
//
union type declarationunion
Un{
char
c
;int
i
;};
//
Definition of joint variablesunion
Un un
;//
Calculate the size of two variablesprintf
(
“%d\
”
,
sizeof
(
un
));
4.2 Union Characteristics
The members of a 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 variable
The union must be able to save at least the largest member).
4.3 Calculation of union 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:
union
Un1{
char
c
[
5
];int
i
;};
union
Un2{
short
c
[
7
];int
i
;};
//
What is the output below?printf
(
“%d\
”
,
sizeof
(
union
Un1
));
//The size is 8 bytesprintf
(
“%d\
”
,
sizeof
(
union
Un2
));
//The size is 16 bytes