Custom types – structures, enumerations, unions

Table of Contents

1. Structure

1.1 Declaration of structure type

1.2 Self-reference of structure

1.3 Definition and initialization of structure variables

1.4 Structure memory alignment

Modify the default alignment number

1.5 Structure parameter passing

2. Structure implements bit segments (filling of bit segments & portability)

2.1 What is a bit segment (segmented by binary bits)

2.2 Memory allocation of bit segments

2.3 Cross-platform issues in bit segments

2.4 Application of bit segments

3. Enumeration

3.1 Definition of enumeration types

3.2 Advantages of enumeration

3.3 Use of enumerations

4.Union

4.1 Definition of union types

4.2 Characteristics of union

4.3 Calculation of union size


1. Structure

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

1.1 Declaration of structure type

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
}; //The semicolon cannot be lost

Special statement:

//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 tag when they are declared.

//Based on the above code, is the following code legal?
p = &x;

Warning: The compiler will treat the two declarations above as completely different types. So it’s illegal.

1.2 Self-reference of structure

Is it okay to include a member in a structure that is of type the structure itself?

//Code 1
struct Node
{
 int data;
 struct Node next;
};
//Is it possible? No, report an error
If so, what is sizeof(struct Node)?

//Correct self-referencing method:
//Code 2
struct Node
{
 int data;
 struct Node* next;
};
//Code 3
typedef struct
{
 int data;
 Node* next;
}Node;
//Is it possible to write code like this?
//solution:
typedef struct Node
{
 int data;
 struct Node* next;
}Node;

1.3 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; //Declare the type and define variable p1 at the same time
struct Point p2; //Define structure variable 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}; // Nested initialization of structure
struct Node n2 = {20, {5, 6}, NULL};//Initial structure nesting

1.4 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 point: structure memory alignment
//Exercise 1
struct S1
{
 char c1;
 int i;
 char c2;
};
printf("%d\\
", sizeof(struct S1));
//Exercise 2
struct S2
{
 char c1;
 char c2;
 int i;
};
printf("%d\\
", sizeof(struct S2));
//Exercise 3
struct S3
{
 double d;
 char c;
 int i;
};
printf("%d\\
", sizeof(struct S3));
//Exercise 4-structure nesting problem
struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\\
", sizeof(struct S4))

How are test points calculated?

First, you must master the alignment rules of structures:

1. The first member is at the address whose offset is 0 from the structure variable.

2. Other member variables should be aligned to an address that is an integer multiple of a certain 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

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 and the nested structure is aligned to an integer multiple of its own maximum alignment number, the overall size of the structure is an integer of all maximum alignment numbers (including the alignment number of nested structures) times

Why does memory alignment exist? Most reference materials say this:

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

Answer

Modify the default alignment number

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

#include <stdio.h>
#pragma pack(8)//Set the default alignment number to 8
struct S1
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//Cancel the set default alignment number and restore it to the default
#pragma pack(1)//Set the default alignment number to 1
struct S2
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//Cancel the set default alignment number and restore it to the default
int main()
{
    //What is the output result?
    printf("%d\\
", sizeof(struct S1));
    printf("%d\\
", sizeof(struct S2));
    return 0;
}

Conclusion: When the alignment of the structure is inappropriate, can we change the default alignment number 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 Note: I haven’t learned about macros here yet. You can implement them after the explanation of macros.

1.5 Structure Parameter Passing

struct S
{
 int data[1000];
 int num;
};
struct S s = {<!-- -->{1,2,3,4}, 1000};
//Structure passing parameters
void print1(struct S s)
{
 printf("%d\\
", s.num);
}
//Structure address passing parameters
void print2(struct S* ps)
{
 printf("%d\\
", ps->num);
}
int main()
{
 print1(s); //Pass structure
 print2( & amp;s); // Pass address
 return 0;
}

Which of the above print1 or print2 functions is better?

The answer is: the print2 function is preferred.

reason:

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. The structure implements bit segments (filling of bit segments & portability)

After talking about structures, we have to talk about the ability of structures to realize bit segments.

2.1 What is a bit segment (segmented by binary bits)

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

1. Members of the bit field must be int, unsigned int or signed int. Char also belongs to the plastic surgery family

2. There is a colon and a number after the member name of the bit field.

for example:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};
printf("%d\\
", sizeof(struct A));

A is a bit segment type. What is the size of segment A?

  • 1. 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 allocated in 4 bytes (int) or 1 byte (char) according to the needs. But assigned according to the number after the colon.
  • 3. Bit segments involve many uncertain factors. Bit segments are not cross-platform. Programs that focus on portability should avoid using bit segments.

Memory allocation of 2.2-bit segment

  • 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 allocated in 4 bytes (int) or 1 byte (char) as needed.
  • 3. Bit segments involve many uncertain factors. Bit segments are not cross-platform. Programs that focus on portability should avoid using bit segments.
//An example
struct S
{
 char a:3;//Allocate 3 bits
 char b:4;//Allocate 4 bits
 char c:5;//Allocate 5 bits
 char d:4;//Allocate 4 bits
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//How is space opened up? 

Cross-platform issues with the 2.3 bit range

1. It is uncertain whether the int bit field is regarded as a signed number or an unsigned number.

2. The maximum number of bits in the bit field cannot be determined. (The maximum number for a 16-bit machine is 16, and the maximum number for a 32-bit machine is 32. If written as 27, problems will occur on a 16-bit machine.

3. Whether members in a bit segment are allocated from left to right or right to left in memory has not yet been defined.

4. When a structure contains two bit fields, and the members of the second bit field are larger 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 structure, bit segments can achieve the same effect, but can save space very well, but there are cross-platform problems.

Application of 2.4-bit segment

Example, the design of message headers in the tcp/ip protocol in computer networks

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 are a limited number of 7 days in a week, and they can be listed one by one.

Gender includes: male, female, confidential, or you can list them one by one.

There are 12 months in the month, and they can also be listed 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//color
{
 RED,
 GREEN,
 BLUE
};

The enum Day , enum Sex , and enum Color defined above are all enumeration types. The contents in {} are possible values of the enumeration type, also called enumeration constants. These possible values are all valuable, starting from 0 by default and incrementing by 1 at a time. Of course, an initial value can also be assigned when defining. For example:

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

3.2 Advantages of Enumeration

We can use #define to define constants, why do we have to use enumerations?

Advantages of enumerations:

  • 1. Increase code readability and maintainability
  • 2. Compared with identifiers defined by #define, enumerations have type checking, which is more rigorous.
  • 3. Prevents naming pollution (encapsulation)
  • 4. Easy to debug
  • 5. Easy to use, multiple constants can be defined at one time

3.3 Use of enumeration

enum Color//color
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//Only enumeration constants can be used to assign values to enumeration variables, so that there will be no type differences.
clr = 5; //ok

4.Union

4.1 Definition of Union Type

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

for example:

//Declaration of union type
union Un
{
 char c;
 int i;
};
//Definition of joint variables
union Un un;
//Calculate the size of two variables
printf("%d\\
", sizeof(un));

4.2 Characteristics of Union

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

Interview questions:

Determine the big and small endian storage of the current computer

union Un
{
 int i;
 char c;
};
union Un un;
//Are the results output below the same?
printf("%d\\
", & amp;(un.i));
printf("%d\\
", & amp;(un.c));
//What is the result output below?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\\
", un.i);

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.
union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];//2*7=14 Compared with the default 8, 8 is smaller, so 8 is an aligned number
 int i;//4 Compared with the default 8, 4 is smaller, so 4 is the alignment number,
      //The maximum alignment number is, because 8>4, so it is 8, so the memory size is an integer multiple of the maximum alignment number
};
//What is the result output below?
printf("%d\\
", sizeof(union Un1));//
printf("%d\\
", sizeof(union Un2));//
syntaxbug.com © 2021 All Rights Reserved.