Detailed explanation of user-defined type: structure (memory alignment, bit segment) + enumeration + Union

Keywords: C C++ data structure

Data type:

C language built-in type:
— char short int long float double
Custom type / construction type:
- structure, enumeration, union

Data storage - type details

1, Structure

1. Special declaration

When declaring a structure, you can declare it incompletely

Anonymous structure

struct
{
	int a;
	char c;
	double d;
}s1, s2; // correct

int main()
{
	struct s3; // err
	return 0;
}

Anonymous structure type

struct
{
	int a;
	char c;
	double d;
}s1, s2;

struct
{
	int a;
	char c;
	double d;
}*ps;

int main()
{
	ps = &s1; // '=': incompatible types from '*' to '*'
	// The compiler will treat the above two declarations as two completely different types, which is illegal

	return 0;
}

2. Structure self reference

Data structure: describes the structure of data stored in memory
 Linear data structure: sequential list, linked list

Node of the same type found node of the same type

Incorrect writing:

struct Node
{
	int date;
	struct Node n; // Wireless recursive err
};

Correct writing: save address

struct Node
{
	int date; // Data domain
	struct Node* next; // Pointer field
};

Where is the error of the following code and how to correct it

typedef struct
{
	int date;
	Node* next;
	
}Node;

The anonymous structure is named, but it has not been generated when used
Solution:

typedef struct Node
{
	int date;
	struct Node* next;

}Node;

3. Definition and initialization of structure variables

Create and initialize

struct Point
{
	int x;
	int y;
}p1 = { 5,6 }, p2; // overall situation

struct Point p2 = { 1,2 }; // overall situation

int main()
{
	struct Point p1 = { 3,4 }; // local
	// Program run here to create
	// Global variables are created before the program runs

	return 0;
}

Structure nesting initialization

struct Point
{
	int x;
	int y;
}p1 = { 5,6 }, p2;

struct Point p2 = { 1,2 };

struct S
{
	double d;
	struct Point p;
	char name[20];
};

int main()
{
	struct Point p1 = { 3,4 };

	struct S s = { 3.14, {1, 5}, {"zhangsan"}};

	return 0;
}

4. Print structure

#include <stdio.h>

struct Point
{
	int x;
	int y;
}p1 = { 5,6 }, p2;

struct Point p2 = { 1,2 };

struct S
{
	double d;
	struct Point p;
	char name[20];
	int date[20];
};

int main()
{
	struct Point p1 = { 3,4 };

	struct S s = { 3.14, {1, 5}, {"zhangsan"}, {1,2,3} };

	printf("%lf\n", s.d); // 3.140000
	printf("%d %d\n", s.p.x, s.p.y); // 1 5
	printf("%s\n", s.name); // zhangsan
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d ", s.date[i]); // 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	}

	return 0;
}

2, Structure memory alignment

What is the same as like as two peas of S1 and S2, but how much difference does S1 and S2 occupy?

#include <stdio.h>

struct S1
{
	char c1;
	int a;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int a;
};

int main()
{
	struct S1 s = { 'x', 100, 'y' };

	printf("%d\n", sizeof(struct S1)); // 12
	printf("%d\n", sizeof(struct S2)); // 8
	printf("%d\n", sizeof(s)); // 12

	return 0;
}

1. Memory alignment

Structure memory alignment rules:

  1. The first member of the structure is always placed where the starting position of the structure is offset by 0
  2. Structure members start with the second member and are always placed at an offset of an integer multiple of a pair
    Alignment number = the compiler's default alignment number and the smaller value of the variable's own size
    Linux - no default alignment number (its own size is the alignment number)
    VS - the default number of alignments is 8
  3. The total size of the structure must be an integral multiple of the maximum number of alignments of each member

practice:

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

How to find nested structure

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

Rule 4:
If a structure is nested, the nested structure is aligned to an integer multiple of its maximum alignment number,
The overall size of a structure 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 work at certain addresses
    Get some specific types of data, otherwise throw a hardware exception.
  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
    Two memory accesses are required; Aligned memory access requires only one access

Summary:
Memory alignment of structures is a way to trade space for time

When designing the structure, we should not only meet the alignment, but also save space. How to:
Let the members with small space gather together as much as possible.

2. Modify the default number of alignments

Use the preprocessing instruction #pragma to set the default alignment number and cancel the setting to restore the default alignment number

Example: modify the alignment number of structure S1 to 1

#include <stdio.h>

// Next to each other, there is no space to waste and the efficiency is low
#pragma pack(1)
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()

int main()
{
	struct S1 s[] = { 0 };
	printf("%d\n", sizeof(s)); // 6

	return 0;
}

3. Macro offsetof (type can be passed when parameters are passed)

Calculates the offset of a structure member from the starting position of the structure stored in memory

size_t offsetof( structName, memberName );

#include <stdio.h>
#include <stddef.h>

struct S1
{
	char c1;
	int i;
	char c2;
};

int main()
{
	printf("%d\n", offsetof(struct S1, c1)); // 0
	printf("%d\n", offsetof(struct S1, i)); // 4
	printf("%d\n", offsetof(struct S1, c2)); // 8

	return 0;
}

4. Structural transmission parameters

Value / address transmission

#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};

// Structural transmission parameters
void print1(struct S tmp)
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", tmp.data[i]);
	}
	printf("\nnum = %d\n", tmp.num);
}

// Structure address transfer parameter
void print2(struct S* ps)
{
	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); // Transmission structure
	print2(&s); // Transmission address

	return 0;
}

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.

3, Bit segment

1. The ability of a structure to implement a bit segment

  1. Bit segments can save space
  2. Bit segment - binary bit
#include <stdio.h>

struct A
{
	int _a : 2; // _ a - 2 bits are bits
	int _b : 5; // _ b - 5 bit s
	int _c : 10; 
	int _d : 30;
};

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

2. 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.

An example:

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10; // 1010 - 010
	s.b = 12; // 00001100 - 1100
	s.c = 3; // 00000011 - 00011
	s.d = 4; // 00000100 - 0100
}

How is space opened up?

 Conjecture:
 char-> First open 1 byte 00000000
 a-3bit    010
 b-4bit    1100
 There is not enough space left. Open up another byte
 c-5bit    00011
 There is not enough space left. Open up another byte
 d-4bit    0100
 Left<-Right height
 0(1100)(010)  000(00011)  0000(0100)
 62 03 04

3. 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.

4, Enumeration

enum Color enumeration type. {} is the possible value of enumeration type, also known as enumeration constant

#include <stdio.h>

enum Color
{
	RED = 2, // Initial value assignment
	GREEN,
	BLUE,
};

int main()
{
	enum  Color c = GREEN; // Possible values assigned to enumeration types

	if (c == GREEN)
	{
		printf("green\n");
	}

	return 0;
}

Advantages of enumeration:

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

Example: clear calculator

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

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("Please select:>\n");
		switch (input)
		{
		case ADD:
			break;
		case SUB:
			break;
		case MUL:
			break;
		case DIV:
			break;
		case EXIT:
			break;
		}
	} while (input);
	
	return 0;
}

5, Consortium (Consortium)

1. 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)

#include <stdio.h>

//Declaration of union type
union Un
{
	char c;
	int i;
};

int main()
{
	//Definition of joint variables
	union Un u = { 0 };

	// Same address
	printf("%p\n", &u);
	printf("%p\n", &(u.c));
	printf("%p\n", &(u.i));

	return 0;
}

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)

Determine the storage size of the current computer:

#include <stdio.h>

// 1
int main()
{
	int a = 1;//0x 00 00 00 01
	//Low ------------------- > High
	//01 00 - small end storage
	//00 01 - big end storage

	char* pc = (char*)&a;
	if (*pc == 1)
	{
		printf("Small end\n");
	}
	else
	{
		printf("Big end\n");
	}
}

Using Consortium:

// 2
int check_sys()
{
	union U
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}

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

	return 0;
}

3. Calculation of joint size

  1. The size of the union is at least the size of the largest member.
  2. 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.

Example: calculate the following code results

union Un1
{
	char c[5]; // 5    1
	int i; // 4  8     4 
	// The total size of the union must be a multiple of 4. 3 bytes are wasted after c. The result is 8
};
union Un2
{
	short c[7]; // 14    2
	int i; // 4  8       4
	// 14 is not a multiple of 4. The result is 16
};

int main()
{
	printf("%d\n", sizeof(union Un1)); // 8
	printf("%d\n", sizeof(union Un2)); // 16
}

Address book program

Posted by pgudge on Wed, 22 Sep 2021 09:53:31 -0700