Structure Consortium

Keywords: C Deep Learning

Make a little progress every day!  

What is a structure? A structure is a data set composed of the same or different data

Before that, learn some little knowledge

2.1 only structure variables allocate addresses, and the definition of structure does not allocate space.
2.2 the definition of each member in the structure is the same as the previous variable definition, but no space is allocated during definition.
2.3 the declaration of structure variable needs to be declared above or in the main function. If it is under the main function, an error will be reported
Structures in 2.4c language cannot be directly cast, only structure pointers can be cast
2.5 members of the same type can be defined under the same type
 

1. Declaration and definition of structure

1. Structure is a user-defined data type

struct structure name{

Member variable or array

};

It should be noted that the semicolon at the end must not be less, indicating the end of the structure design definition

A structure is a collection that contains multiple variables or arrays. The types of variables can be the same or different. Each such variable or array is called a structure member. Let's take a look at an example:

include<stdio.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
};

stedent is the name of the structure. It has four members: age, name, score and sex. The definition of the structure is very similar to the array we learned earlier, except that the array can be initialized, but not here

2. First define the structure type, and then define the structure variable

#include<stdio.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
};
struct student s1;//s1 is a global variable
int main()
{
	struct student s2;//s2 is a local variable

}

3. When defining structure type, structure variables are also defined

#include<stdio.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
}s1, s2;

int main()
{
	return 0;

}

4. Define anonymous structure

#include<stdio.h>
struct
{
	int age;
	char name[10];
	int score;
	char sex[10];
}s1;

int main()
{
	return 0;

}

However, this method is not very good. This structure has a one-time feeling. It is not recommended to define the structure in this way

Summary:

1. Use the struct keyword to indicate that it is followed by a structure

2. Next is a structure type name, which can be selected by yourself

3. Curly braces enclose the list of structure members. They are described in a declarative way, and; is used to end the description

4. The semicolon after the closing curly bracket indicates the end of the structure design definition

5. Way of definition

1) Declare the structure type before defining the variable name
For example: struct (type name)   Student (structure)   Student1 (variable name), student2 (variable name);
Defines student1 and student2 as struct   Variables of student type, that is, they have struct   Structure of student type

(2) Define variables while declaring types. The general form of this form of definition is: struct   Structure name{
Member list
}Variable name;

be careful:

The members in the structure can be basic data types or user-defined data types

5. Position and function of structure declaration

2. Initialization of structure variables

1. Structural variables, like other variables, can be defined with initial values, and structural variables are initialized with braces

Example:

#include<stdio.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
};

int main()
{
	struct student s1={20,"zhangsan",60,"male" };//Initialize s1;
	printf("%d  %s  %d  %s", s1.age, s1.name, s1.score, s1.sex);//Print members in s1
	return 0;

}

Operation results:

The initialization of structure variables is initialized with braces

How about this one down there?

#include<stdio.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
};

int main()
{
	struct student s1;
	s1={20, "zhangsan", 60, "male" };
	printf("%d  %s  %d  %s", s1.age, s1.name, s1.score, s1.sex);//Print members in s1
	return 0;

}

This does not work. s1 has been defined before. Now we are giving it a value instead of initializing it

We can see

  At this time, the compiler reports an error. After the structure variable is defined, if we still want to assign values to the member variables, we can only assign values one by one

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
};

int main()
{
	struct student s1;
	strcpy(s1.name, "zhangsan");
	s1.age = 40;
	s1.score = 60;
	strcpy(s1.sex, "male");
	printf("%d  %s  %d  %s", s1.age, s1.name, s1.score, s1.sex);//Print members in s1
	return 0;

}

Operation results:

  2. Initialization of nested structures in structures

For the initialization of nested structures in structures, we still use braces for initialization

#include<stdio.h>
struct techer
{
	int age;
	char name[10];
};
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
	struct techer s2;
};

int main()
{
	struct student s1 = { 20,"zhangsan",70,"male",{20,"teacher"} };//Initialize s1;
	printf("%d  %s  %d  %s %s", s1.age, s1.name, s1.score, s1.sex,s1.s2.name);//Print members in s1
	return 0;

}

3. Access structure members

There are two main ways to access structure members

1. Use member access operator. Structure variable. Member variable

2. Use - > operator. Structure pointer - > member variable name

for instance:

#include<stdio.h>
struct techer
{
	int age;
	char name[10];
};
struct student
{
	int age;
	char name[10];
	int score;
	char sex[10];
	struct techer s2;
};

int main()
{
	struct student s1 = { 20,"zhangsan",70,"male",{20,"teacher"} };//Initialize s1;
	printf("%d  %s  %d  %s %s", s1.age, s1.name, s1.score, s1.sex,s1.s2.name);//Print members in s1
	return 0;

}

2. Structure pointer accessing member variable

#include<stdio.h>
struct t
{
	int age;
	char name[12];
	char c;
};
int main()
{
	struct t s1 = { 20,"zhansan",'c' };//Define a structure variable s1 and initialize it to the corresponding value
	struct t* p1 = &s1;
	printf("%s %d", p1->name, p1->age);
	return 0;
}

Operation results:

#include<stdio.h>
#include<string.h>
struct t
{
	int age;
	char name[12];
	char c;
};
int main()
{
	
	struct t s1 = { 20,"zhansan",'c' };//Define a structure variable s1 and initialize it to the corresponding value
	struct t* p1 = &s1;
	printf("%s %d", p1->name, p1->age);
	return 0;
}

Summary:

1. The overall assignment of the structure is only limited to defining variables and initializing them. In the process of use, it can only be assigned one by one, which is similar to the array.

2. Structure is a user-defined type. It is a template for creating variables. It does not occupy space. Structure variables contain real data and need space to store

4. Structural transmission parameters

1. The structure can be used as a function parameter, and the type of parameter passed is similar to other variables or pointers

In the first parameter transmission mode, the transmission structure is received by the structure

#include<stdio.h>
#include<string.h>
struct t
{
	int age;
	char name[12];
	char c;
};
void print(struct t s2)
{
	printf("%d %s", s2.age, s2.name);
}
int main()
{
	
	struct t s1 = { 20,"zhansan",'c' };//Define a structure variable s1 and initialize it to the corresponding value
	print(s1);
	return 0;
}

Operation results:

  Method 2:

Address of passed structure variable:

#include<stdio.h>
#include<string.h>
struct t
{
	int age;
	char name[12];
	char c;
};
void print(const struct t* s2)
{
	printf("%d %s", s2->age,s2->name);
}
int main()
{
	
	struct t s1 = { 20,"zhansan",'c' };//Define a structure variable s1 and initialize it to the corresponding value
	print(&s1);
	return 0;
}

Operation results:

Both methods can achieve the goal. Which method is better   The answer is the second. The reason is that the first is value transfer. A temporary variable will be created to copy the value of the argument to the formal parameter. If the structure is too large, the transfer efficiency will be very low and the space will be wasted. However, the second method of transfer is address transfer, which only needs to copy 4 or 8 bytes. If you want to transfer parameters, try to choose the second method

5. Structure array

The so-called structure array refers to that every element in the structure is a structure. In practice, it is often used to represent groups with the same data structure, such as students in a class

Defining a structure array is similar to defining a structure variable. It can be initialized during definition

Example:

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[12];
	char c;
};

int main()
{
	
	struct student arr[3] = { {20,"zhangsan",'c'},{30,"lisi",'d'},{40,"wangwu",'p'} };
	printf("%d %d %d", arr[0].age, arr[1].age, arr[2].age);
}

Operation results:

  6. Structure pointer

As the name suggests, it is a pointer to the structure, which is similar to those defining integer pointers,

Definition format: struct structure name * + structure pointer name

Example: struct student *struct _pointer;

struct_pointer=&s1;

Use the - > operator when accessing members

Struct_pointer - > + member name

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[12];
	char c;
};

int main()
{
	
	struct student s1 = { 22,"zhangli",'c' };
	struct student* struct_pointer = &s1;
	printf("%d", struct_pointer->age);
}

Operation results:

  be careful:

Structure and structure variable are two different concepts: structure is a data type and a template for creating variables. The compiler will not allocate memory space for it, just like   int,float,char   These keywords themselves do not occupy memory; only structural variables contain real data and need memory for storage. The following writing is wrong. It is impossible to get the address of a structural name or assign it to other variables:

The following is wrong

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[12];
	char c;
};

int main()
{
	
	struct student* p = &student;
	struct student* s = student;
}


7. Get structure members

Structure members can be obtained through the structure pointer. The general form is:

(*pointer).memberName

Or:

pointer->memberName

In the first way, the priority of. Is higher than *, and the parentheses on both sides of (* pointer) cannot be less. If the parentheses are removed and written as * pointer.memberName, it is equivalent to * (pointer.memberName), which is wrong.

In the second writing method, -> is a new operator, which is used to be called "arrow". With it, you can directly obtain structure members through structure pointers, which is also the only use of - > in C language.

The above two writing methods are equivalent. We usually use the second writing method, which is more intuitive.
 


Example:

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[12];
	char c;
};

int main()
{
	
	struct student s1 = { 2,"zhansan",'e' };
	struct student* pointer = &s1;
	printf("%d\n", pointer->age);
	printf("%d", (*pointer).age);
}

Operation results:

  7. Reference of structure variables (input and output)

The input scanf and output printf of structure variables operate the same as other variables

1. It should be noted that. Has a high priority

2. If the member of the structure itself is a structure, you need to continue to use the. Operator until the member of the lowest level.

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char name[12];
	char c;
	struct Date
	{
		int year;
		int month;
		int day;
	}brirthday;
}stu1;

int main()
{
	printf("%d", stu1.brirthday);//This is wrong because brightday is a structure
	scanf("%d", &stu1.brirthday.month);//correct;

	
}

8. Structure memory alignment (key)

  Memory alignment in a structure is a memory operation that trades space for time.

     1, Rules for structure alignment

     1,  Data member Alignment rule: for data members of a structure (or union), the first data member is placed where the offset is 0, and then each data member is aligned according to the value specified by #pragma pack and the smaller one of the data member's own length.

      2. Overall alignment rule of structure (or union): after the data members are aligned, the structure (or union) itself should also be aligned according to the smaller of the value specified in #pragma pack and the maximum data member length of structure (or union).

      3. Combined with 1 and 2, it can be inferred that when the n value of #pragma pack is equal to or exceeds all   Data member Length, the size of this n value will have no effect.

     1)  structural morphology The first address of a variable is an integer multiple of its longest basic type member;

      Note: when opening up space for a structure, the compiler first finds the widest basic data type in the structure, and then looks for it   Memory address The position that can be an integral multiple of the basic data type is used as the first address of the structure. The size of the widest basic data type is used as the alignment modulus described above.

      2) The address of each member of the structure relative to the first address of the structure   Offset (offset) is an integer multiple of the member size. If necessary, the compiler will add internal adding between members;

Note: before opening up space for a member of a structure, the compiler first checks whether the offset of the first address of the pre opened space relative to the first address of the structure is an integer multiple of the member. If so, the member is stored. On the contrary, a certain byte is filled between the member and the previous member to meet the requirement of integer multiple, That is, move the first address of the pre opened space back a few bytes.

     3)  structural morphology The total size of is an integer multiple of the size of the widest base type member of the structure, if necessary,   compiler trailing padding is added after the last member.

Note: the total size of the structure includes padding bytes. In addition to the above two items, the last member must also meet the third item. Otherwise, several bytes must be filled at the end to meet the requirements of this item.

      4) Continuous elements of the same type in the structure will be in a continuous space, and   array Same.

      5) If there are elements with a length greater than the number of processor bits in the structure, the alignment unit is the multiple of the processor; Otherwise, if the length of the elements in the structure is less than the multiple of the processor, the longest element in the structure will be used   data elements Is the alignment unit.

2. Why set structure memory alignment?

1, Hardware reason: speed up CPU access
When most of us don't know how the CPU reads data, we basically think that the CPU reads data byte by byte, but in fact, it reads data according to blocks. The size of blocks can be 2, 4, 8 and 16. The size of the block is also known as memory read granularity.
Assuming that the CPU has no memory alignment and wants to read a 4-byte data into a register (assuming that the reading granularity is 4), two situations will occur
1. The data starts at 0 bytes read by the CPU, which you can read just once
2. The beginning of the data is not at byte 0. Suppose it is at byte 1. The CPU should first read out bytes 0 ~ 3 and read the contents of bytes 4 ~ 7. Then discard 0 bytes in 0 ~ 3 bytes and 5, 6 and 7 bytes in 4 ~ 7 bytes. Then combine the data of 1,2,3,4.
It can be seen that the efficiency of CPU reading is not very high, which can be said to be cumbersome.
But if there is memory alignment:
Since each data is aligned, the CPU can read the data at one time. Although there will be some memory fragments, it is nothing in terms of the size of the whole memory. It can be said that it is a practice of exchanging space for time.
2, Platform reason:
Not all hardware platforms can access any data at any address. Some hardware platforms can only get some types of data at some addresses, otherwise hardware exceptions will be thrown.

3. Calculation of structure size

Calculate the size of the structure according to the above rules:

Let me explain:

The first member has an offset of 0 in the structure variable   At your address.

Other member variables should be aligned to the address of an integer multiple of a number (alignment number). Alignment number  =  The compiler defaults to an alignment number with the smaller value of the member size. The default value in vs is 8   The default value for Linux is 4

The total size of the structure is an integer multiple of the maximum number of alignments. (each member variable has its own alignment number)

If a 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 an integer multiple of all the maximum alignment numbers (including the alignment number of nested structures).

Now let's practice it. It's just saying but not practicing

#include<stdio.h>
#include<string.h>
struct student
{
	int age;
	char a;
}stu1;

int main()
{
	printf("%d", sizeof(stu1));
	return 0;
}


What is the size of the structure above?

First, according to the above rules, the first element is placed at the offset of 0. The type of the first element is integer and the size is 4 bytes, while the type of a of the second element is char and the size is 1 byte. The default maximum alignment number of the compiler is 8. If the minimum value of the two is 1, a will be placed under the multiple of 1. At this time, a and age occupy a total of 5 bytes, but the maximum alignment number in all members is 4, that is, age. 5 is not a multiple of 4, so the compiler will waste 3 bytes of space for alignment, so the size of this structure is 8

Operation results:

  Now let's look at another problem

#include<stdio.h>
#include<string.h>
struct student
{
	char c1;
	char c2;
	int i;
}stu1;

int main()
{
	printf("%d", sizeof(stu1));
	return 0;
}

Operation results:

The result is 8. Let's analyze why it is   8??
C1 is a char type, occupying one byte. The first member is   c1   In the structure, the variable offset is 0   At your address.
c2 is a char type, accounting for one byte. It should be aligned to the position of an integer multiple of the alignment number. Alignment number  =  The compiler's default alignment number is the smaller value in the member size. The default value in vs is 8, and the smaller value is 1. The alignment number of char type is 1, so it is aligned to 1   An integer multiple of, which is the address space starting with offset 1.
i is of type int, occupying four bytes. It should be aligned to an integer multiple of the alignment number. The alignment number of type int is   4, so align to 4   An integer multiple of.
Let's take a look at the memory distribution diagram:

Memory distribution diagram:

So what is the result?

#include<stdio.h>
#include<string.h>
struct student
{
	char c1;
	
	int i;
	char c2;
}stu1;

int main()
{
	printf("%d", sizeof(stu1));
	return 0;
}

Operation results:

The result is 12. Let's take a look at the process?
c1 is a char type, accounting for one byte, and the offset corresponding to the structure variable is 0   At your address.
i is int type, accounting for four bytes. The alignment number is 4. It is aligned to an integer multiple of 4, that is, the address space with an offset of 4.
c2 is char type, accounting for one byte, aligned to 1   That's the next address space, aligned to the address space at offset 8.
The total size of the structure is an integer multiple of the maximum alignment number, so it is an integer multiple of the alignment number 4. Now 9 bytes of space have been used, so the total size is 12 bytes of space. So the output is 12.
 

Memory map:

Next, let's look at an example of how to calculate the size of nested structures

Example:

#include<stdio.h>
struct s3
{
	double d;
	char c;
	int i;
};
struct s4
{
	char c1;
	struct s3 s;
	double d;
};
int main()
{
	struct s4 t;
	printf("%d ", sizeof(t));
}

Operation results:

  The result is 32. Let's take a look at the analysis:
Easy to get struct   S3 occupies 16 bytes. Please analyze by yourself. If not, you can ask me.
Let's take a look at struct   Size of S4, struct   There are three member variables in S4. The first one is of char type, accounting for one byte, and is aligned to the address with offset 0. The second member is used by structure nesting. The structure S3 variable S3 has just been obtained, accounting for 16 bytes, so the alignment number of the second member is 16. Because the alignment number is the smaller value between the compiler default number and the member alignment number, vs the default alignment number is 8, and the smaller value is 8, so it is aligned to the address space with an offset of 8. Office. The third member is double, accounting for 8 bytes, corresponding to an integer multiple of 8, that is, the address of offset 24.
The total size of the structure is an integer multiple of the maximum alignment number 8, so it is 32.
Let's take a look at the memory distribution diagram:

 

#pragma pack() modifies the default number of alignments

Simple understanding #pragma
As one of the more complex preprocessing instructions, it is used to change the compilation state of the compiler and provide specific compilation instructions for specific compilers. These instructions are specific to a certain (or some) compiler. Other compilers may not know the meaning of the instruction or have different understanding of the instruction, that is to say, #The implementation of pragma is related to the specific platform. It can be simply understood that the preprocessing instruction is a tool for developers to interact with the compiler.

#pragma   pack instruction description
Since the reading time of the memory is far less than the storage speed of the CPU, the idea of setting the alignment coefficient of the data structure, that is, sacrificing space for time, is used to improve the storage efficiency of the CPU.

Let's start with the alignment configuration of the compiler. Taking VC6 as an example, the compilation options in vc6 are  / Zp[1|2|4|8|16]  ,/ Zp1 indicates alignment with 1-byte boundary, and accordingly, / Zpn indicates alignment with n-byte boundary. N-byte boundary alignment means that the address of a member must be arranged on an integer multiple of the size of the member or an integer multiple of N, whichever is the smallest. That is:
min ( sizeof ( member ), n)
In fact, 1-byte boundary alignment means that there are no holes between structure members.
/The Zpn option is applied to the whole project and affects all structures involved in compilation.

example:

#include<stdio.h>
#pragma pack(1)
struct s4
{
	char c1;
	double d;
};
int main()
{
	struct s4 t;
	printf("%d ", sizeof(t));
	
}

Operation results:

 

The answer is 9. By #pragma modifying the default alignment number to 1, the size of all members and 1 take the minimum value, then the alignment number of all members is 1, that is, 9 next to each other, so the size is 9

offfsetof and its implementation

offsetof is a library function in < stddef. H >, which is used to calculate the offset of member variables in the structure·

Function prototype:

  How to use:

#include<stdio.h>

#include<stddef.h>
struct s4
{
	char c1;
	double d;
};

int main()
{
	struct s4 t;
	printf("%d ", offsetof(struct s4, d));
	
}

Operation results:

The principle has been mentioned above, but I won't repeat it here

To implement offsetof, it is actually a macro  

Code implementation:

#include<stdio.h>
#include<stddef.h>
#define OFFSETOFF(struct_type ,member)(int)&((struct_type*)0)->member
struct s4
{
	char c1;
	int a;
	double d;
};

int main()
{
	printf("%d ", OFFSETOFF(struct s4, a));
	
}

The first parameter is the name of the structure. The second parameter is the name of the structure member. It returns the offset of the member in the structure

The ingenious implementation of this macro is to force the address of the starting object to 0,   That is (struct_type*)0. Here, the returned (struct_type*)0 - > member is the offset of the member


Note: this structure does not apply for memory space, but needs to access its members. According to common sense, there will be no error, because it accesses the unsolicited memory space

However, here we do not actually access the memory space of the structure member, but only return its address value. We use the address operator, and the value here is calculated by the compiler in the compilation stage. hs

Consortium

What is a consortium? In C language, the definition of variables is the process of allocating storage space. Generally, each variable has its own unique storage space, so can different data types (not colleagues) be stored in the same memory space?

The answer is yes. This can be achieved by using a consortium. A consortium is also called a community. The keyword defining a consortium in C language is union.
 

1. The format of defining a consortium is similar to the structure

union consortium name{

Member list

};

There are several members in the member list. The member form is generally type specifier member name.

Like structs and enumerations, a union is also a construction type

  2. Method of consortium definition

Method 1: create a template first and then define variables

#include<stdio.h>
union student{
	int a;
	char c;
};
int main()
{
	union student s1;
	return 0;
}

Method 2: create templates and variables

#include<stdio.h>
union student
{
	int a;
	char c;
}s1;
int main()
{
	
	return 0;
}

Method 3: omit the consortium name, that is, anonymous consortium

#include<stdio.h>
union 
{
	int a;
	char c;
}s1;
int main()
{
	
	return 0;
}

The initialization of the consortium is basically the same as that of the structure, but I will not repeat it here

2. Calculation of consortium size

 

When the specified value byte alignment of #pragma pack() is not defined, its calculation rule is the size of memory occupied by the largest member in the consortium, and must be a multiple of bytes occupied by the largest type

Let's take an example:

 

#include<stdio.h>
union 
{
	char s[10];
	int a;
}s1;
int main()
{
	printf("%d", sizeof(s1));
	return 0;
}

Operation results:

  The size of the largest member in this consortium is 10, that is, the array, but the size of the bytes occupied by the largest type is 4. It is not an integer multiple of 4, so the compiler will waste 2 bytes, so the size is 12 bytes

3. Use the consortium to judge the size of the end:

Concept and characteristics of the consortium: the union maintains enough space to store "one" of multiple data members instead of configuring space for each data member. In the union, all members share the same space and only one data member is stored at the same time. The biggest feature is that all data members have the same starting address, that is, the base address of the consortium.

2) There are mainly two types of byte storage in the computer: big_endian mode and little_endian mode. It can be understood from the English name that the big end mode starts from the low address and ends at the high address (that is, the high address stores the status and the low address stores the high position); the small end mode starts from the high address and ends at the low address (opposite to the big end).

3) Taking advantage of the fact that all data members in the union have the same starting address, the data storage mode can be cleverly obtained by storing 1 through an int member and then reading through a char member. If it is read through a char member (i.e. the first byte on the reading starting position), if the value is 1, it indicates that it is a small end mode.

 

The code is as follows:

#include<stdio.h>
union stu
{
	int a;
	char c;
};
int main()
{
	union stu s;
	s.a = 1;
	if (s.c == 1)
	{
		printf("Small end");
	}
	else
	{
		printf("Big end");
	}
	return 0;
}

  Bloggers have limited strength. If you have any mistakes, please leave a message in the comment area. If you think it's good, you can praise it. Thank you!

Posted by Neomech on Sat, 25 Sep 2021 00:41:24 -0700