A deep understanding of custom types (structs, enumerations, unions) in C

Catalog

1. Structures

Concepts of 1.1 Structure

1.2 Declarations of Structural Types

1.3 Special Structural Declaration

Self-reference of 1.4 structure

1.5 Definition and initialization of structure variables

1.6 Memory Alignment in Structures

1.7 Memory Alignment in Structures

1.8 Modify the default number of them

1.9 Structural Transfer

2. Bit Segments

Meaning of the 2.1-bit segment

Memory and allocation of 2.2-bit segments

Cross-platform issues with 2.3-bit segments

3. Enumeration

Meaning of 3.1 Enumeration

Advantages of 3.2 Enumeration

4. Union (Community)

4.1 Declaration of the type of consortium:

4.2 Consortium Characteristics

Calculation of 4.3 Consortium Size

1. Structures

Concepts of 1.1 Structure

Structures are collections of values called member variables. Each member of a structure can be a different type of variable.

1.2 Declarations of Structural Types

Say nothing, code it up

#include <stdio.h>
//Let's first describe a student's basic information
struct student//struct is the structure keyword and student is the structure tag
{
	char name[10];//Name
	int age;//Name These four variables are struct member variables
	char sex[20];//Age
	char id[20];//School Number
}; s1, s2;//Create a global structure variable

struct student s3;//Create global structure variables
int main()
{
	struct sudent s4;
	struct sudent s5;//Creating local variables
	return 0;
}

1.3 Special Structural Declaration

When declaring a structure, you can make an incomplete declaration, you can have no structure signature, only a list of variables;

#include <stdio.h>
struct
{
	int a; 
	char b; 
	float c;
}x;
struct
{ 
	int a; 
	char b; 
	float c; 
}a[20], *p;//Here p is an anonymous structure pointer

int main()
{
  p=&x;//But the types on both sides are incompatible at this time
   
   return 0;
}

The compiler treats the two preceding declarations as two distinct types and is therefore illegal, meaning that the anonymous structure type can only be used once at this time and can no longer be used later.

Self-reference of 1.4 structure

That is, an object of its own type stores the address of another object and finds the next one, so that it repeatedly searches down and finds all the structures.

#include <stdio.h>
struct Node
{
	int date;//Data Domain
	struct Node* next;//Node* now stores the address of the next object, which is the pointer field
}Node;
typedef struct Node//A structure's self-reference cannot omit its label name
{ 
	int data; 
	struct Node* next; 
}Node;

1.5 Definition and initialization of structure variables

struct Point
{
	int x;
	int y;
}p1; //Define variable p1struct Point p2 while declaring type; //Define the structure variable p2
struct Point p2;
int main()
{
	struct Point p3;
	return 0;
}

You can also assign values to variables while creating them

struct Point
{
	int x;
	int y;
}p1 = { 1, 2 }; 
struct Point p2 = { 3, 4 };
int main()
{
	struct Point p3={ 5, 6 };
	return 0;
}

Another is nested initialization of structs

struct Node
{ 
	int data; 
	struct Point p; 
	struct Node* next; 
}n1 = { 10, { 4, 5 }, NULL }; //Structures Nested Initialization
struct Node n2 = {20, {5, 6}, NULL};

1.6 Memory Alignment in Structures

struct Point
    {
        int x;
        int y;
    }p3 = { 5,6 }, p4 = {7,8};
    struct Point p2 = {1,2};
    struct S
    {
        double d;
        struct Point p;//Belongs to the nesting of structures, structure S contains Point
        char name[20];
        int data[20];
    };
   int main()
    {
        struct Point p1 = {3,4};
        struct S s = { 3.14, {1,5} , "zhangsan", {1,2,3} };
        printf("%lf\n", s.d);//Print Structures
        printf("%d %d\n", s.p.x, s.p.y);
        printf("%s\n", s.name);
        int i = 0;
        for (i = 0; i < 20; i++)
         {
             printf("%d ", s.data[i]);
         }
  return 0;
}

1.7 Memory Alignment in Structures

First, we need to understand the rules for memory alignment within structures:

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

2.Other member variables are aligned to addresses that are multiple of a number (alignment). Alignment = A compiler default alignment is smaller than the size of the member. (The default value in VS is 8, and Linux does not have a default alignment)

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 nested structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment, and the overall size of the structure is an integer multiple of all the maximum alignments (alignments with nested structures).

Top Example

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

 

Example 2

struct S3
{ 
double d; 
char c; 
int i;
};

Example 3

Let's look at a structure nesting problem

struct S3
{ 
   double d; 
   char c; 
   int i;
};
struct S4
{ 
   char c1; 
   struct S3 s3; 
   double d;
};

 

The size of this nested structure is therefore 32

So why is there memory alignment?

1.Platform Reason (Port Reason): Not all hardware platforms can access any data at any address; some hardware platforms can only fetch certain types of data at certain addresses or throw hardware exceptions.

2. Data structures (especially stacks) should align as closely as possible on natural boundaries.

The reason is that in order to access the misaligned memory, the processor needs to make two memory accesses; aligned memory access only requires one.

In general, memory alignment of structures is the exchange of space for time.

So in order to satisfy alignment and save space, try to bring together the smaller members.

1.8 Modify the default number of them

#include <stdio.h>
int main()
{
   #pragma pack(1)//Set default alignment to 1
   struct S2
{ 
    char c1; 
    int i; 
    char c2;
};
   #pragma pack()//Unset default alignment to default
   return 0;
}

Here's a little bit of knowledge:

The offsetof macro calculates the offset of a variable from the first address in the structure

1.9 Structural Transfer

There are two ways to transfer structure parameters, one is to use a value operation and the other is to use an address operation.

  

struct S
{ 
	int data[1000]; 
	int num; 
}; 
struct S s = { { 1, 2, 3, 4 }, 1000 };
void print1(struct S tmp)//Pass-by operation, where tmp is a temporary structure variable that receives the passed-in structure s
{ 
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", tmp.data[i]);
	}
	printf("\nnum=%d\n", s.num);
}
void print2(const struct S* ps)//Address operation

{
	int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", ps->data[i]);
    }
    printf("\nnum = %d\n", ps->num);
}
int main()
{
    struct S s = { {1,2,3,4,5,6,7,8,9,10}, 100 };
    print1(s);
    print2(&s);
    return 0;
}

Although there are two ways to pass struct parameters above, we prefer the second address operation. This is because when a function passes parameters, the parameters need to be stacked, which incurs time and space overhead. When passing a struct object, the structure is too large and the parameter stack incurs more overhead, which can lead to performance degradation.

2. Bit Segments

Meaning of the 2.1-bit segment

The declaration and structure of a bit segment are similar, except that they differ in two ways

1. The members of the bit segment must be int, unsigned int, or signed int.

2. The member name of the bit segment is followed by a colon and a number.

struct A
{ 
	int _a : 2; //Means 2 bits
	int _b : 5; //5 Bits
	int _c : 10; //10 bits
	int _d : 30;//30 bits
};

So this segment has 47 bits and 8 bytes.

Memory and allocation of 2.2-bit segments

1.The members of a bit segment can be int, unsigned int, signed int, or char (belonging to the reshaping family) types

2.Bits are spatially exploited as needed in either 4 bytes (int) or 1 byte (char).

3.Bit segments involve many uncertainties. Bit segments are not cross-platform. Programs that focus on portability should avoid using bit segments.

For instance

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;

So how does this segment open up space?

Cross-platform issues with 2.3-bit segments

Many questions about bit segments C language is undefined because

1.Whether an int segment is treated as a signed number or an unsigned number is uncertain.

2.The maximum number of bits in the segment cannot be determined. (16-bit machines Max 16, 32-bit machines Max 32, written as 27, will cause problems on 16-bit machines.

3. Whether members in a segment are allocated in memory from left to right or from right to left is undefined.

4. Whether the remaining bits will be discarded or utilized when a structure contains two bits and the members of the second bits are too large to accommodate the remaining bits of the first bits is uncertain.

Summary: Bit segments can achieve the same effect as structures, but can save space well, but there are cross-platform issues.

3. Enumeration

Meaning of 3.1 Enumeration

Enumeration means an enumeration

enum Color//colour
{ 
	RED, 
	GREEN, 
	BLUE
};
int main()
{
	printf("%d\n", RED);//Printed out as 0
	printf("%d\n", GREEN);//1
	printf("%d\n", BLUE);//2
	enum Color c = RED;//Only a constant of an enumeration type can be assigned to a variable of an enumeration type, and no type differences will occur
	if (c == RED)
	{
		printf("green\n");
	}
	return 0;
}

The enum Color defined above is an enumeration type. The content in {} is a possible value for the enumeration type, also known as an enumeration constant. These possible values are all valid. By default, they start at 0 and increase by 1 at a time, although you can also assign an initial value when you define them. For example:

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

Advantages of 3.2 Enumeration

1.Increase code readability and maintainability

2.Enumerations with type checks are more rigorous than identifiers defined by #define.

3.Naming pollution prevention (encapsulation)

4.Easy to debug

5.Easy to use, multiple constants can be defined at once

4. Union (Community)

Union is also a special custom type. Variables defined by this type also contain a series of members, which share the same space (hence union is also called union).

4.1 Declaration of the type of consortium:

union Un
{
	char c;
	int i;
};
int main()
{
	union Un u = { 0 };
	printf("%d\n", sizeof(u));//4
	printf("%p\n", &u);
	printf("%p\n", &(u.c));  //The last three printed addresses are the same, indicating a common space
	printf("%p\n", &(u.i));
	return 0;
}

4.2 Consortium Characteristics

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

Determine the size of the current computer's end storage

Method 1:

int main()
{
	int n = 1;
	char*p = (char*)&n;//Small-end storage: Low-byte content of a data is stored in a low address, high-byte content in a high address
	if (*p == 1)     //Memory space: Low ----------------------------- High
		            //01 00 00 00 small-end storage
					//00 00 00 01 large-end storage
		         //Large-end storage: Low-byte content of a data is stored in a high address, and high-byte content is stored in a low address
	{
		printf("Small-end storage\n");
	}
	else
		printf("Large End Storage\n");
	return 0;
}

Method 2:

int main()
{
	union u //Consortium
	{
		char c;
		int i;
	}u;
	u.i = 1;
	if (u.c == 1)
	{
		printf("Small end\n");
	}
	else
	{
		printf("Big end\n");
	}
	return 0;
}

Method 3: Use the function in union:

int check_sys()
{
	union U
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}
int main()
{
	int ret=check_sys();
	if (ret == 1)
	{
		printf("Small end\n");
	}
	else
	{
		printf("Big end\n");
	}
	return 0;
}

Calculation of 4.3 Consortium 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, align to an integer multiple of the maximum alignment.

union Un1
{ 
	char c[5]; //5 bytes, 1 for number
	int i; //4 bytes, 4 for its number, so consortium size = 8
}; 
union Un2
{ 
	short c[7];//2*7 = 14 bytes, short = 2 for number
	int i; };//For its number = 4, so the consortium size = 16
int main()
{
	printf("%d\n", sizeof(union Un1));//8
	printf("%d\n", sizeof(union Un2));//16
	return 0;
}

Okay, that's the end of this chapter, and I'd like to see your brother compliment you!

Tags: C

Posted on Mon, 20 Sep 2021 16:16:25 -0400 by Pete