c array and structure

Arrays store composite types of the same type; structures store composite types of different types for custom data structures.

In the computer, there are two ways to store a large amount of data collection. One is to store data in blocks, which is the storage method of an array. A large number of data of the same type are put together in one piece; the other is to separate a large amount of data one by one. But the data items stored in it include the storage address of the next data, like a direction mark, pointing to the next data, and the whole looks like a connected table, so this structure is called a linked list.

1. Arrays and pointers

As a typical example of sequential storage, an array stores values ​​of the same type and is divided by the storage size of the type. Its length is its capacity. How many values ​​of this type can be accommodated is determined at the beginning of the declaration. Access by subscript (subscript is an integer index, starting from 0).

1.1 A first look at arrays

A common declaration for an array is as follows:

type array[size];

The common declaration of an array is to add the data type, and the array size is enclosed in square brackets after the array name. In the program, arrays are often used to store important data, and their use often needs to be initialized first. The more straightforward way is to initialize the array with a list of values ​​​​enclosed in braces (the number of values ​​must not be greater than the size of the array). as follows:

//完整的初始化
int nums1[4] = {
    
    1, 2, 3, 4};
//部分初始化
int nums2[4] = {
    
    1, 2};
//C99后支持的新特性,只初始化最后一个元素
int nums3[4] = {
    
    [3] = 4};
//不明确指定数组大小的声明初始化
int nums4[] = {
    
    1, 2, 3, 4, 5};
//错误示范
int nums5[4] = {
    
    1, 2, 3, 4, 5};
int nums6[4];
//下面这步是非法的
nums6 = {
    
    1, 2, 3, 4};

As above, the initialization of the array should be carried out at the beginning of the declaration, and the initialization accepts partial initialization. This initialization starts from the first element of the array until the constant value is used up, and the rest is automatically assigned by the compiler. Type zero value. The above-mentioned list enclosed in curly braces to initialize the array is called list initialization in C++ , so let's call it that way here, it's convenient. As in the error example above, when the initialization list of the array is larger than the capacity of the array, the compiler will directly report an error, and after the declaration, it is illegal to initialize the list in the following statement, and an error will also be reported, but you can declare a fixed-size array instead of Not initialized. Also, if the size is not specified like nums4 above, after the declaration is initialized, its size is the length of the initialized list, that is to say, the length of nums4 is 5.

Use of arrays

An array is a collection. The use of an array is often the reading and writing of elements in the array, and the call of an element in the array can index the element through the array subscript, and the subscript index of the array starts from 0. Here are a few ways to apply:

//1
int nums[4] = {
    
    1, 2, 3, 4};
scanf("%d", &nums[0]);      //"5"
printf("%d\n", nums[0]);    //"5"

//2
int nums1[4] = {
    
    [3] = 10};
printf("%d, %d\n", nums1[0], nums1[3]); //"0, 10"
scanf("%d", &nums[0]);                  //"8"
printf("%d, %d\n", nums1[0], nums1[3]); //"8, 10"

//3
int nums2[4];
printf("%d, %d, %d, %d\n", nums2[0], nums2[1], nums2[2], nums2[3]); //"8, 0, 20, 0"
scanf("%d%d%d%d", &nums2[0], &nums2[1], &nums2[2], &nums2[3]);  //"0 1 2 3"
printf("%d, %d, %d, %d\n", nums2[0], nums2[1], nums2[2], nums2[3]); //"0, 1, 2, 3"

As shown in the above three examples, three integer arrays, read and write in three states (full initialization, partial initialization, declaration only). In fact, the use of arrays is naturally adapted to the counting loop of for, as follows:

int nums[4], i;
//写入数据
for(i = 0;i < 4;i++)
    scanf("%d", &nums[i]);  //逐行输入"0"、"1"、"2"、"3"
//读取
for(i=0 ;i < 4; i++)
    printf("%d, ", nums[i]);
//"0, 1, 2, 3,"

Another way of calling array elements

The above method of using array elements is based on subscripts, but there is another way to call them. The value of the array name itself is a pointer constant , which is the address of the first element of the array, so the call of the array element can also be performed with a pointer. as follows:

int nums[4] = {
    
    0, 1, 2, 3};
printf("%d, %d\n", *nums, *(nums + 2)); //"0, 2"

int *p = nums, i;
for(i = 0;i < 4; i++)
    printf("%d, ", *(p + i));           //"0, 1, 2, 3, "

All of the above can be used, but when using pointers, you should pay attention to the * value operator and & address operator. The pointer itself is a variable pointing to an address, and the function of the * value operator is to obtain the value of the address pointed to by the pointer. However, the address-taker is often of little use to the pointer itself, because the usage scenario often pays more attention to the value of the address pointed to by the pointer. Here, I want to remind you not to use the address-taker & like a pointer as an ordinary variable.

It should be noted that the use of arrays has a common beginning with pointers, but they are not equivalent to pointers.

1.2 Multidimensional arrays

In real life, data sets can be in the form of a set or a matrix. For the processing of this kind of data, arrays are often used in C, but the form of the array is different. Set lists use one-dimensional arrays, matrices use two-dimensional arrays, and there are even three-dimensional arrays and four-dimensional arrays to deal with more complex data structures. This part is about learning and interpreting arrays.

Two-dimensional array

The so-called two-dimensional and three-dimensional aspects of the array are reflected here, and it will be more intuitive to display them with subscripts. The following declaration defines a two-dimensional array:

//完全初始化
int matrix[2][3] = {
    
    
    {
    
    0, 1, 2},
    {
    
    3, 4, 5}
};

//部分初始化
int matrix1[2][3] = {
    
    
    {
    
    0, 1},
    {
    
    3, 4}
};

//不指定数组大小的声明初始化
int matrix2[][3] = {
    
    
    {
    
    0, 1, 2},
    {
    
    3, 4, 5}
};

To review, an array is a list of elements of the same type. A one-dimensional array is a simple list that stores constant values ​​of the same type. What about a two-dimensional array? It is an alternative list that stores one-dimensional arrays one by one. Therefore, without delving into the elements in the array, in fact, two-dimensional arrays are the same as one-dimensional arrays and even multi-dimensional arrays. They are all an ordered list.

Based on the above conclusion, the above example is much simpler. matrix is ​​a list that stores two arrays, and the inner array stores three integer data. Therefore, when the size range of the array is fixed, it can be initialized with a list that is not enough in size, such as matrix1, and when the number of outer arrays is not clear, use a certain number of arrays that meet the size of the inner layer To initialize the array, such as matrix2 (still a bit of a mouthful). The above example can also be changed to look like this:

//完全初始化
int matrix[2][3] = {
    
     {
    
    0, 1, 2},{
    
    3, 4, 5} };
int matrix[2][3] = {
    
    0, 1, 2, 3, 4, 5};

//部分初始化
int matrix1[2][3] = {
    
     {
    
    0, 1},{
    
    3, 4} };
int matrix1[2][3] = {
    
    0, 1, 3, 4};

//不指定数组大小的声明初始化
int matrix2[][3] = {
    
     {
    
    0, 1, 2},{
    
    3, 4, 5} };
int matrix2[][3] = {
    
    0, 1, 2, 3, 4, 5};

From this point of view, it is more intuitive. In fact, the elements of the two-dimensional array are also stored sequentially. For the access of two-dimensional array elements, it is more intuitive to use subscripts, but it is also possible to use pointers to access, but relatively speaking, in terms of form, it is more troublesome, layer by layer.

int matrix[2][3] = {
    
    0, 1, 2, 3, 4, 5};
int *p = matrix;        //GNU中会有警告,没有在vs尝试
printf("%d\n", *(p+4)); //"4"
printf("%d\n", *((p+1)+1)); //"2",这里被计算器理解成p+2的取值
int (*p1)[3] = matrix;       //定义一个int [3]类型的指针,初始化其值使其指向matrix
printf("%d, %d\n", *(*p1 + 1), *(*(p1+1)+1));      //读取二维数组第一行第二列和第二行第二列的值,输出"1, 4"

In general, there are two ways to call a pointer to a two-dimensional array. One is to call as a normal offset of a one-dimensional array. The second is to declare a pointer to the inner array type and initialize it to point to the matrix. address. Because the priority of the * operator is lower than that of the [] operator, in order to indicate the identity of the pointer, it is necessary to enclose the variable name and the * operator; in addition, it is important to note that the above p1 is a pointer of type int [4] , (a pointer is a variable pointing to a certain address, specifically the address of a certain type of value), so, in fact, the type is a constraint on the pointer, preventing it from accessing storage beyond the bounds and obtaining an unexpected value, while int [4 ] type is also a constraint. You can regard the storage space connected by such four integers as a unit. The current pointer points to the address of such a unit. When it is initialized to the address of the matrix and then offset, it actually It is to offset in units of the inner array of the matrix. By the way, there are pointers of definite type and pointers of void type.

Two-dimensional array used with for loop

Because a two-dimensional array can be expanded into a one-dimensional array, there are nested and non-nested uses of loop calls, as follows:

int matrix[2][3] = {
    
    
    {
    
    0, 1, 2},
    {
    
    3, 4, 5}
};
int i, j, *p = matrix;

//嵌套循环
for(i = 0;i< 2;i++) {
    
    
    for(j = 0;j < 3;j++)
        printf("%d, ", matrix[i][j]);
    printf("\n");
}
//"0, 1, 2, "
//"3, 4, 5, "

//不嵌套循环,编译器报警系列
for(i = 0;i < 6; i++)
    printf("%d, ", *(p + i));
//"0, 1, 2, 3, 4, 5, "

//嵌套循环
int (*p1)[3] = matrix;
for(i = 0;i<2;i++) {
    
    
	for(j=0;j<3;j++)
		printf("%d, ", *(*(p + i) + j));
    printf("\n");
}
//"0, 1, 2, "
//"3, 4, 5, "

Multi-dimensional arrays and two-dimensional arrays are common, but the collection is nested one level deeper, so it will not be expanded here.

1.3 Variable-length arrays and dynamic arrays

In daily applications, the length of an array is often fixed, and there are different ways to fix it. It is more common to use macro definitions to set the length of the array, and this kind of array often stores constant data one by one. Basic data, such as the year array should have 12 numbers, and each number should store the number of days in each month; the week array should store several values ​​​​from Sunday to Monday, whether it is a string or an integer number See need. as follows:

#define MONTH 12
#define WEEK 7

//const限定符
const int leap_year[MONTH] = {
    
    31, 28, 31, 30, 31, 30, 31, 30, 31, 31, 30, 31};
const char week[WEEK] = {
    
    "Sunday", "Monday", "Tuesday", "Wedsday", "Thursday", "Friday", "Saturday"};

In addition, integer variables or integer expressions can also be used to determine the length of the array. Before C99, the stipulated standard was to determine the size of the array by an integer constant expression instead of an integer expression. This was added after C99 new features. In fact, from a personal point of view, it is nothing more than the difference between one sentence and two sentences. However, this also explains the importance of the word relative, variable, uncertain, but it is definite in the running program, that is, it is fixed relative to the running of the program. This is the variable length array - VLA .

int sum, a, b;
//输入a、b值
//c99之前
sum = a * b;
int num[sum];
//c99以后
int num[a*b];

Compared with the array whose area is given by a constant at the beginning, the variable-length array is also considered a dynamic array. The array whose length is determined when the program is running is a dynamic array. Conversely, what is determined before running is Static array (because the constant is determined before running). In addition, there is also a dynamic array. The size of this array is determined according to the needs of the program. The memory space is also applied for by itself and released by itself after use. The memory for this kind of application comes from the heap. Since the array can be nested layer by layer, for this kind of array, it needs to be created layer by layer from the outside to the inside, and the release is reversed, releasing layer by layer from the inside to the outside. And this is the so-called dynamic array .

Heap area and stack area
For a running program, memory is often divided into stack area, heap area, constant area, static area and code area. Initially, the program is stored in the disk in the form of executable code. When the operating system runs the program, it will load the code and static data (such as initialization variables) into the memory. After loading, it allocates memory to the runtime stack (storage Local variables, function parameters and return addresses, main is also a function), in addition, there is heap memory allocated by the program's explicit request, for arrays, some data structures (linked list, hash table, tree, etc.) need heap storage, Changes gradually as the program runs.

In C language, there are two special functions for explicit request and release of memory - malloc function and free function, while in C++, new operator and delete operator apply and release. The prototypes of the above two functions are as follows:

#include <stdlib.h>
void *malloc(int num);
void free(void *address);
//另外几个相关函数
void *calloc(int num, int size);
//在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。
void *realloc(void *address, int newsize);
//该函数重新分配内存,把内存扩展到 newsize。

The usage of the malloc function is to apply for num specified bytes of memory and return a null pointer pointing to the memory. Generally, a forced type conversion is required before assigning a value to a specific pointer; free is to release the heap memory pointed to by address without returning a value. The common use of matching arrays is as follows:

#include <stdlib.h>
int *p;
p = (int *)malloc(4* sizeof(int));
for(i = 0;i < 4; i++)
	p[i] = i;
for(i = 0;i < 4;i++)
	printf("%d, ", p[i]);
free(p);

As above, apply for a memory space of 4 integers, and assign it to the integer pointer p after the forced type conversion. At this time, you can perform array operations on the p pointer (pointer operations are also available). The key point, remember to free after use.

dynamic two-dimensional array

There are already two-dimensional array learning above, so here is mainly an example of calling nested malloc and free

#include <stdio.h>
#include <stdlib.h>

int main() {
    
    
    
    int **p, i, j;
    //定义一个指向指针数组的指针,并指向能存下3个整型指针的连续内存
    p = (int **)malloc(3*sizeof(int *));
    //逐层分配空间并赋值
    for (i = 0;i < 3; i++){
    
    
        p[i] = (int *)malloc(4*sizeof(int));
        for(j = 0;j < 4;j++)
            p[i][j] = i * 3 + j + 1;
    }
    //从数组末开始读取值,一行读取完毕就释放那一行的内存
    for(i = 2;i >= 0;i--){
    
    
        for(j=3;j>= 0;j--)
            printf("%d, ", p[i][j]);
        printf("\n");
        free(p[i]);
    }
    //释放最外层的指针存放内存
    free(p);
	return 0;
}
/*输出
10, 9, 8, 7,
7, 6, 5, 4,
4, 3, 2, 1,
*/

As above, memory application and release for dynamic two-dimensional arrays can better reflect the nature of two-dimensional arrays. The outer array stores the address of the inner array, and the content of the array is accessed through the address, and the inner array also uses subscripts to store information Split more finely. Well, although the above example starts accessing from the deepest part of the array and gradually releases outwards, I normally release from the first line of the inner array without any problems. There is no problem for the time being, but it can still be released backwards Come on backwards.

1.4 More forms of pointers

A pointer is just a function, pointing to a certain address, and according to the address it points to, it has different names:

  • Simple pointers, pointing to simple variables of the same type, such as simple arrays;
  • Double pointers, pointers to simple pointers;
  • Array of pointers, an array of pointers, which can be assigned to double pointers;
  • Function pointer, a pointer to a function, looks a bit like an alias in terms of use, but it is actually for the convenience of calling;
  • Structure pointer, a pointer to a data structure

1.5 Array Bounds

Arrays are continuous memory, and unexpected behavior will occur once the bounds are exceeded when accessing internal elements. For example, there is an array a with an element length of 4, but when accessing a[4], it is obviously out of bounds, but the compiler will not report an error, and the outbreak of this problem will occur when the program is running. , sometimes outputs values ​​other than expected, and sometimes stops unexpectedly. So here is a warning. When accessing subscripts, be careful not to cross the boundary. In addition, for character arrays like strings, you should habitually place a '\0' character at the end of the array.

1.6 Similarities and differences between arrays and pointers

  • From the perspective of memory storage, an array is a collection of elements of the same type, and it is a large storage of a fixed number of types; a pointer is an object that stores addresses of the same type, and its storage is different depending on the system, but it is not larger than 8 bytes Memory
  • From the point of view of the declaration definition, the overall definition of the array must be at the time of declaration, otherwise it can only be assigned one by one, and the pointer can be disassembled, and it can also point to different addresses
  • From the point of view of use, arrays use subscripts to access elements, pointers use the * operator to obtain values, and arrays can be used for address offsets

###Arrays and pointers are different from each other and related to each other. They are inseparable and inseparable. The application scenarios and angles of use of the two are different.

Two, the structure

As an important component of composite types, structures can be composed of various other data types to form a data structure expressing special meaning, which is also an important starting point of object-oriented programming. Cutting into the scene, when we want to describe an object, such as a student, what data can we use? Grades, height, weight, grade, class, etc. are all available. According to the scene we need, we extract important attributes into a structure. What the program needs to process is the attribute data of the structure. When we need to build a grade system, we need students' grades in various subjects. When we need to do health checks, we need data such as students' height, weight, vision, etc. The needs are different and the attributes used are different. When we have determined a data structure, we have determined a custom type, and we can use this type to declare and define the variables required. The general form of a structure is as follows:

struct tag {
    
     
    member-list
    member-list 
    member-list  
    ...
} variable-list ;

In the above, member-list is the member list, which is filled with characters, integers, and floating-point types. The variable name is the attribute name of the structure tag, and variable-list is the member list. Generally speaking, if it is not declared as a function Internal local variables will not declare this type of variable when declaring the structure, so the general global tag is left blank with a semicolon at the end.
The declaration of the structure is actually a user-defined type, which tells the compiler what data this type consists of.

2.1 Initialize the structure

A structure is a collection of different types, and an array is a collection of the same type, but from the perspective of memory, the type is actually divided into bytes and the reading rules are different. Both are a large piece of memory, so the initialization of the array requires many Arrays can be referenced.
To initialize a structure, first declare a structure, now extract the student object as a structure, and abstract its name, age and grade as basic attributes, as follows:

struct student {
    
    
    char name[10];
    int age;
    float total_score;
};
struct student a = {
    
    "Xiao Ming", 14, 89.7};

The above is a declaration of the student structure (this declaration is generally placed outside all functions as a global variable), and the a object of the student type is declared, which is also initialized with parentheses. It should be noted that the declaration of structure objects in C and C++ is a little different. In C, the declaration needs to add struct to highlight the structure type, and in C++, it is optional, which can be added or not.
Of course, it is also possible to declare and then initialize element by element, but the name attribute of student is an array, so it can only be initialized at the time of declaration, or assigned by methods such as strcpy.

//只声明不初始化
struct student temp;
temp.name = "Siri";                 //error: assignment to expression with array type
strcpy(temp.name, "Siri");          //使用前要引入头文件string.h
temp.age = 10;                      //合法
printf("%f\n", temp.total_score);   //"0.000000",编译器自动初始化为float类型零值

Another initialization method is:

struct student b = {
    
    .name = "Xiao Fei",
            .age = 15,
            .total_score = 98.6};

For example, the above method is the designated initializer added to the structure after C99 and C11. The dot operator and attribute name are used to identify specific elements within curly braces, and the equal sign is assigned to initialize. Of course, it can also be partially initialized. In this case, the part that is not initialized will be automatically initialized to a type zero value by the compiler.

2.3 Access structure

At present, the dot operator can be used to access the properties of the structure, such as the usage in the designated initializer above, the dot operator plus the property name can access the corresponding property, which is somewhat similar to the array subscript. In fact, general structure objects only have this access method, and structure pointers have another way to access structure properties.

struct student *p = &a;
printf("%f\n", p->total_score); //"98.6"

As above, as a pointer to a structure, the applicable method of accessing the properties of the structure is the arrow, highlighting the existence of the pointer.

2.4 Structure type derivation

The structure is a self-defined type. Any type in c can generate pointers and arrays, so there are also structure arrays and structure pointers. There is nothing special about their declaration and definition and access to specific elements, but together they will make People feel a little uncomfortable all of a sudden.

struct student stu[3] = {
    
    
    {
    
    "Xiao Ming", 14, 89.7},
    {
    
    "Siri", 11, 80},
    {
    
    "Xiao Fei", 88}
}//错误,这种声明只能在结构体类型声明的时候进行,如下

struct student {
    
    
    char name[10];
    int age;
    float total_score;
} stu[3] = {
    
    
    {
    
    "Xiao Ming", 14, 89.7},
    {
    
    "Siri", 11, 80},
    {
    
    "Xiao Fei", 8}
};
printf("%d\n", stu[2].age); //"8"

//只声明,后续可以像数组只声明那样进行类似的初始化
struct student stu[4];

The above is the declaration and definition of the structure array. The declaration of the structure pointer is relatively simple, just add * to the simple type, and then use the address of the same type of ordinary variable to assign a value to initialize. In addition, the element call of the structure array uses subscript access, and the structure pointer uses arrow access, but when the pointer points to the array, it uses the subscript access method.

Composite nesting

From the beginning above, the properties of the structure have been incorporated into the composite type of array, so the structure can also be embedded with pointers, and it will be much more convenient to change the array into a pointer. Of course, in the C language, convenience is equal to danger, and the simpler the place, the easier it is to have an accident, but it will not be discussed here. As a structure describing an object, the object can also have object attributes inside, so the structure can also be nested. For example, if the name attribute is disassembled for the student, the name attribute can be used as an object, and the first and last names are filled inside, as follows:

struct Name {
    
    
    char *surname;
    char *name;
};

struct student {
    
    
    struct Name name;
    int age;
    float total_score;
};

Originally, I wanted to directly put the student in front and the Name to make a pre-declaration, but I found that it was not possible, and the declaration of the structure could not be initialized, and there were various features. . . . . . Or c++ is used too much, and the struct of c is always regarded as a class.
The declaration is relatively simple. Let's introduce its initialization and use:

//main内
int i;
//普通student对象
struct student a = {
    
    {
    
    "Jack", "Chen"}, 25, 100.0};
//student数组
struct student stu[2] = {
    
    
    {
    
    {
    
    "Hong", "Pan"}, 26, 88},
    {
    
    {
    
    "Jin", "Jiang"}, 27, 98}
};
//student指针
struct student *p = &a;
printf("name: %s %s\n", a.name.surname, a.name.name);
for(i=0;i<2;i++)
	printf("name: %s %s, score: %.2f\n", stu[i].name.surname, stu[i].name.name, stu[i].total_score);
printf("age: %d\n", p->age);

//声明student对象但不初始化
struct student temp;
temp.name.surname="Siri";
temp.name.name="MI";
scanf("%d", &temp.age);         //80
printf("age: %d\n", temp.age);  //"80"

It is relatively simple, and the specific experiment depends on personal experience. When these simple concepts are combined to become huge, it is easier to understand and use them by focusing on the characteristics of the bottom part.

2.5 Structure allocated from heap memory

The dynamic array was introduced above, which is explicitly applied for a specific size of space by malloc to the heap memory and released by free; similarly, the structure can also be used, it should be said, it is often used in this way, when it is used as An important data structure when.

#include <stdlib.h>

struct student *p;
p = (struct student *)malloc(2*sizeof(struct student));
/*一堆应用操作*/
free(p);

In fact, it is not complicated here, but it is only a record of a concept. What is complicated is the matter after the data structure is introduced. The space here is not enough, just a record.

significant byte alignment

Let me give you an example:

struct Name {
    
    
    char *surname;
    char *name;
};

struct A {
    
    
    int age;
    struct Name name;
    float total_score;
};
struct B {
    
    
    int age;
    float total_score;
    struct Name name;
};
printf("A: %d, B: %d\n", sizeof(struct A), sizeof(struct B));
//输出:A: 32, B: 24
printf("ptr: %d, int: %d, float: %d, Name: %d\n", sizeof(char *), sizeof(int), sizeof(float), sizeof(struct Name));
//辅助数据:"ptr: 8, int: 4, float: 4, Name: 16"

The above example refers to the two structures A and B with the same attributes, but because the order of declaration of the attributes is different, the memory occupied by sizeof is different. The reason for this is what is going to be introduced here – byte alignment.

First, explain how byte alignment is aligned. In C, a variable, a block of memory, a compound variable, a large block of memory, such as an array, but the structure is different, because it stores different types of variables, unlike the internal elements of the array are consistent and tidy. Therefore, byte alignment is an adjustment for the structure. The so-called alignment means that all members in the structure must be aligned to the largest member when allocating memory. The largest member is used as a standard. When the first member does not exceed this standard, the following members do not exceed this standard. They will be added together to see if this standard is reached. When it arrives, you can open a new piece of memory with the highest standard. , excluding composite types). as follows:

struct A {
    
    
    char a;
    int i;
};

struct B {
    
    
    char a;
    char b;
    int i;
};

struct C {
    
    
    char a;
    char b;
    char c;
    char d;
    char e;
    int i;
};

struct D {
    
    
    char a;
    int i;
    double db;
};

struct E {
    
    
    char a;
    int i;
    int i2;
    double db;
};

printf("char: %d, int: %d, double: %d\n", sizeof(char), sizeof(int), sizeof(double));
//"char: 1, int: 4, double: 8"
printf("A: %d, B: %d, C: %d, D: %d\n", sizeof(struct A), sizeof(struct B), sizeof(struct C), sizeof(struct D));
//"A: 8, B: 8, C: 12, D: 16, E: 24"

The spatial structure of the above example can be roughly referred to as follows:
struct A

a null null null
i i i i
occupies 8 bytes

struct B
a b null null
i i i i
occupies 8 bytes

struct C
a b c e
e null null null
i
occupies 12 bytes

struct D
a null null null
---- ----
db
occupies 16 bytes


struct E
|a|empty|empty|empty|i|i|i|i| |
----|----|—|—|—|-|-|-|-|-| |
i2|i2 |i2|i2|empty|empty|empty|empty
||db||||||||||
occupied 24 bytes


The above are all interpretations of the structure from simple basic types. What if it is a composite type? What about adding it to an array or other structure? This kind of existence is quite special. It belongs to the big head protruding from the good memory block, just like the acne on the face, which makes the face not so neat and elegant.
In fact, it’s nothing to add an array. The structure is also a collection of basic types. Although it is not necessary to disassemble the array and leave it blank, it is enough to fill the gap. The most terrible thing is the embedded structure. as follows:

struct Name {
    
    
    char *surname;
    char *name;
};

struct A {
    
    
    int a;
    struct Name name;
    float f;
}sa;
struct B {
    
    
    int a;
    float f;
    struct Name name;
}sb;
struct C {
    
    
    struct Name name;
    int a;
    float f;
}sc;
printf("ptr: %d, int: %d, float: %d, Name: %d\n", sizeof(char *), sizeof(int), sizeof(float), sizeof(struct Name));
//辅助信息:"ptr: 8, int: 4, float: 4, Name: 16"
printf("A: %d, B: %d, C: %d\n", sizeof(struct A), sizeof(struct B), sizeof(struct C));
//"A: 32, B: 24, C: 24"
printf("A: %p, %p\nB: %p, %p\nC: %p, %p\n", &sa.a, &sa.name, &sb.f, &sb.name, &sc.name, &sc.a);
/*辅助性地址信息
A: 00000000004079C0, 00000000004079C8
B: 00000000004079A4, 00000000004079A8
C: 0000000000407980, 0000000000407990
*/

The above example is to customize a regular structure that stores two pointers, and then put it as a bulk into three structures that also store basic types, and conduct experiments in different orders. The intuitive memory structure is as follows :

struct A

a a a a null null null null
name
name
f f f f null null null null
Occupies 32 bytes, name occupies 16 bytes exclusively, and is divided into 8 bytes, a and f are separated by name, so each occupies 8 bytes, but the type is limited, it actually occupies 4 bytes, and the rest of the bytes are vacant


struct B

a a a a f f f f
name
name
Occupies 24 bytes, a and f are combined to make up 8 bytes, and name occupies two 8 bytes exclusively. Similarly, struct C is also constructed in the same way


struct C

name
name
a a a a f f f f
Same as big B above. It is clear from the above that even if it is a compound type such as a structure, it is not simply to look at its overall memory. In fact, there must be an upper limit in this part, that is, the maximum byte alignment unit limited by the system. When the largest member of all members exceeds For this unit, follow the system. The 64-bit system limits the maximum alignment to 8 bytes, and the 32-bit system limits it to 4 bytes.

The meaning of byte alignment


When there is no byte alignment, the memory allocation of the structure is the same as the array, and they are all pasted together. In this way, there will be a few data that make up an odd memory block, and the CPU reads the data according to the data bus. Taken, the 16-bit data bus can read two bytes of data at a time, the 32-bit data bus can read 4 bytes of data at a time, and the 64-bit system is 8 bytes of data. When the char member and the int member are combined together, it is 5 bytes of memory, and the 32-bit system cannot read it correctly once, and needs to read it a second time, so expand, the char member occupies a word of 4 bytes of memory section, and the rest are blank, so that the CPU can read correctly every time. This is the meaning of exchanging space and memory for time. The existence of byte alignment is for the read efficiency of the CPU.

manual byte alignment


When two machines communicate and they need to use a data structure to transmit information, it is necessary for them to adopt consistent byte alignment rules. In this case, manual byte alignment rule adjustments can be performed. There are two methods, one is to set the rules in precompilation, and the other is to set the alignment rules when the array is declared, as follows:

//设定其后的代码中字节对齐以n为准
#pragma pack(n)
struct A {
    
    
    ...//balabala
};
//设定其后的代码中字节对齐根据默认进行
#pragma pack()


struct B {
    
    
    ...
}__attribute__((packed));
//上面的__attribut__是一个给编译器看的设置,重点关注里面的参数
//packed参数,表示按照实际来进行字节对齐,也就是不对齐

final words

The above is only personal reference to books, blogs and other materials and then experimental results, the words of the family.

Guess you like

Origin blog.csdn.net/weixin_44948269/article/details/127711012