Custom types: structure, enumeration, union

Keywords: C

1, Structure

Declaration of structure:

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

Special statement:

//Anonymous structure type
struct
{
 int a;
 char b;
 float c; 
 }x;
struct
{
 int a;
 char b;
 float c; 
 }a[20], *p;

Self reference of structure:

//Code one
struct Node
{
 int data;
 struct Node next;
};
//Is it feasible?

The above is not allowed.

Correct self reference method:

//Code two
struct Node
{
 int data;
 struct Node* next;
};

be careful:

//Code 3
typedef struct
{
 int data;
  Node* next;
 }Node;
//Is it feasible to write code like this?

//Solution:
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; //Define the variable p1 while declaring the type
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}; //Structure nesting initialization


struct Node n2 = {20, {5, 6}, NULL};//Structure nesting initialization

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

How to calculate the test site? First, you must master the alignment rules of the structure:

  1. The first member is at an address offset 0 from the structure variable.
  2. Other member variables should be aligned to the address of an integer multiple of a number (alignment number).
    Alignment number = the compiler default alignment number and the smaller value of the member size.
    The default value in VS is 8
  3. The total size of the structure is an integer multiple of the maximum number of alignments (one for each member variable).
  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).

Why is there memory alignment?

  1. Platform reason (migration reason): not all hardware platforms can access any data at any address; Some hardware platforms can only get certain types of data at certain addresses, otherwise hardware exceptions will be thrown.
  2. Performance reason: data structures (especially stacks) should be aligned on natural boundaries as much as possible. The reason is that in order to access misaligned memory, the processor needs to make two memory accesses; Aligned memory access requires only one access.

in general:
  memory alignment of structures is a method of trading space for time.

Modify the default number of alignments

    we have seen #pragma this preprocessing instruction before. 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() / / unset the default alignment number and restore it to the default
#pragma pack(1) / / set the default alignment number to 8
struct S2
{
 char c1;
 int i;
 char c2;
};
#pragma pack() / / unset the default alignment number and restore it to the default
int main()
{
    //What is the output?
    printf("%d\n", sizeof(struct S1));
    printf("%d\n", sizeof(struct S2));
    return 0;
}

Structural transmission parameters

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 transfer parameter
void print2(struct S* ps) {
 printf("%d\n", ps->num);
}
int main()
{
 print1(s);  //Transmission structure
 print2(&s); //Transmission address
 return 0; 
 }

Which of the print1 and print2 functions above is better?
The answer is: the print2 function is preferred.

reason:

When a function passes parameters, 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.

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

2, Bit segment

What is a bit segment?

1. The member of the bit field must be int, unsigned int or signed int.

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

A is a bit segment type.

Memory allocation for bit segments

  1. The member of the bit segment can be int unsigned int signed int or char (belonging to the shaping family)
  2. The space of bit segments is opened up in the form of 4 bytes (int) or 1 byte (char) as needed.
  3. Bit segments involve many uncertain factors. Bit segments are not cross platform. Pay attention to portable programs and avoid using bit segments.
struct S {
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
struct S s = {0};
s.a = 10; s.b = 12; s.c = 3; s.d = 4;
//How is space opened up?


Cross platform problem of bit segment

  1. It is uncertain whether the int bit field is treated as a signed number or an unsigned number.
  2. The number of the largest bits in the bit segment cannot be determined. (16 bit machines have a maximum of 16 and 32-bit machines have a maximum of 32, which is written as 27. Problems will occur on 16 bit machines.
  3. Whether 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 member of 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.

Summary:

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

Application of bit segment:

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 of a week is a limited seven days, which can be listed one by one.
Gender: male, female and confidential, which can also be listed one by one.
The month has 12 months, which can also be listed one by one

You can use enumeration here

Definition of enumeration type:

These possible values have values. By default, they start from 0 and increase by 1 at a time. Of course, initial values can also be assigned when defining. For example:

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

Advantages of enumeration:
We can use #define to define constants. Why do we have to use enumeration? 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

Use of enumeration:

enum Color//colour
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//You can only assign values to enumeration variables with enumeration constants, so that there will be no type difference.
clr = 5;               //ok??

4, Consortium

Definition of union type:

Union is also a special user-defined 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 community). 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));

Characteristics of the 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).

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

From the results, the value of c follows i.

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.

union Un1
{
 char c[5];
 int i;
};
union Un2
{
 short c[7];
 int i;
};
//What is the output below?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));

The output result is 8 16. Because the total size of the union is an integer multiple of the maximum alignment number type, bytes are filled.

Posted by interrupt on Thu, 04 Nov 2021 19:30:43 -0700