Previous review
- Custom precon H header file. Function: it is contained in the core header file, so as to call the relevant defined data
- Define Boolean and Status return value types, which are essentially int types
- SqList and ElemType are two structures defined. In fact, they belong to independent structures Struct nesting
- Define and initialize SysConfig structure
1, This goal
According to the functional characteristics of the student management system and the ADT description of the linear table, determine and write the relevant basic operations to facilitate the subsequent file call.
GitHub: https://github.com/ITchujian/StudentManagementSystem_2022_C
Note: in order to share the experience of this development, I will put the analysis process and code writing process in the development record in the form of words and pictures, but I will not explain or explain some very basic actions.
Current location: [1.2 C case] please write a thousand lines student management system with me in C language
Jump:
- [1C case] please write a thousand line student management system with me in C language
- [1.1 case C] please write a thousand lines student management system with me in C language
- [1.3 case C] please write a thousand line student management system with me in C language
2, Development record
Step 1
ADT -- Abstract Data Type, which means Abstract Data Type. My understanding:
Describe the mathematical model of data structure and the operation that the model can make
Please note that we use the sequential storage structure of linear table instead of chain structure, which has been reflected in the code of Article 1.1.
Therefore, the linear table ADT of the student management system is as follows:
- Data object: D = {student 1, student 2,..., student n}, n > = 0
- Data relationship: R = {there is a sequential relationship between adjacent students}
- Basic operation table:
Serial number | Function name | Operation results |
---|---|---|
1 | InitList | Initialize and construct an empty linear table |
2 | DestroyList | Destroy linear table |
3 | ClearList | Empty linear table |
4 | ListEmpty | Judge empty linear table |
5 | ListLength | Return linear table length |
6 | GetElem | Gets the data element of the linear table |
7 | LocateElem | Returns the bit order of data elements that meet certain conditions |
8 | SearchElem | Search linear table by data item |
9 | ListInsert | Insert element into linear table |
10 | ListDelete | Delete element to linear table |
11 | ListTraverse | Traverse the data elements of the linear table and perform operations of the same type |
12 | SwapElem | Exchange the location of two data elements |
13 | ListSort | Sort entire linear table |
In the above table, we will probably use most of the operations. Let's wait and see.
Step 2
Create kernel_list.h (it means that this is a core file related to the table, and other subsequent files need to call the operation in the header file)
At this time, VS2022 has added the following code to the top line of our header file:
#pragma once
It seems that the official ide of Microsoft also recommends us to use this kind of IDE 😂
Step 3
Import the custom header file precon h.
#include "preconf.h"
Step 4
In this step, we will implement all operations in the basic operation table. The name of the function is the same as that presented in the table Nomenclature of large hump (according to the development specification, this is the core file, and the function is named in this way).
Initialize and construct an empty linear table
Apply for a piece of memory through malloc function, and then judge whether the memory allocation is successful;
Give the initial value of length in the SqList structure 0, that is, there are now 0 students;
The initial allocated space value is 128, and the unit is sizeof(ElemType).
Status InitList(SqList* L) { L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if (!(L->elem)) exit(OVERFLOWED); L->length = 0; L->list_size = LIST_INIT_SIZE; return OK; }
Destroy linear table
Release dynamic memory;
Length is 0;
The allocated space is also 0;
Status DestroyList(SqList* L) { free(L->elem); L->elem = NULL; L->length = 0; L->list_size = 0; return OK; }
Empty linear table
Status ClearList(SqList* L) { L->length = 0; return OK; }
Judge empty linear table
Boolean ListEmpty(SqList* L) { if (L->length == 0) return TRUE; else return FALSE; }
Return linear table length
We may not be able to use it here, because if length is involved, we can directly quote L - > length, but it doesn't hurt to write. Obsessive compulsive disorder causes trouble 🤣
int ListLength(SqList* L) { return L->length; }
Gets the data element of the linear table
Pass the value of the ith data element to e of the same type. If i is illegal, exit and return ERROR.
Status GetElem(SqList* L, int i, ElemType* e) { if (i < 1 || i > ListLength(L)) exit(ERROR); *e = *((L->elem) + i - 1); return OK; }
Returns the bit order of data elements that meet certain conditions
I remember writing in the last chapter that C + + is different in some places compatible with C. while learning Python and Java, I also have a simple understanding of some C + + syntax. One of the more interesting is that in C, if it is written like this
int Test(float(*tt)()) { return 0; }
Do you think that a function in a parameter is a function without parameters?
In fact, it's not. If there are no parameters, we should write it like this
int Test(float(*tt)(void)) { return 0; }
Adding void is the best proof that the function has no parameters, which is not advisable for us in C + +. Our project is based on C + +, although it is written in C, so we can avoid some mistakes and big holes by understanding this.
The following function pointer Status(*compare)(ElemType, ElemType) represents a function whose return type is Status and has two parameters. The parameter type is ElemType.
int LocateElem(SqList* L, ElemType* e, Status(*compare)(ElemType, ElemType)) { ElemType* p; int i = 1; p = L->elem; while (i <= (L->length) && !(*compare)(*e, *(p++))) ++i; if (i <= (L->length)) return i; else return 0; }
Search linear table by data item
Parameter cur_e is the search term. In the previous definition structure, I noted that the student number is the only value in all student tables, which cannot be repeated. Therefore, we must make good use of this when querying student information;
Parameter mode_manner indicates the deviation of the search. If it is - 1, that is to find the precursor of the student corresponding to a student number, 0 is to query the student itself, and 1 is to query the successor of the student corresponding to a student number.
Finally, pass the searched students to parameter e.
Status SearchElem(SqList* L, int cur_e, ElemType* e, int mode_manner) { ElemType* r = L->elem; int i = 1; while (i <= L->length && r->num != cur_e) { i++; r++; } if (i > L->length) return INFEASIBLE; switch (mode_manner) { case -1: *e = *(--r); return OK; case 0: *e = *r; return OK; case 1: *e = *(++r); return OK; default: return ERROR; } return OK; }
Insert element into linear table
To insert an element before the i(i ∈ [1, n]) th element, move the i → n elements backward by one position.
Status ListInsert(SqList* L, int i, ElemType e) { if (i < 1 || i >(L->length) + 1) return ERROR; if (L->length >= L->list_size) { ElemType* new_base = (ElemType*)realloc(L->elem, (L->length + LIST_INCREMENT) * sizeof(ElemType)); if (!new_base) exit(OVERFLOWED); L->elem = new_base; L->list_size += LIST_INCREMENT; } ElemType* q = &(L->elem[i - 1]); ElemType* p = &(L->elem[(L->length) - 1]); for (p = &(L->elem[(L->length) - 1]); p >= q; --p) /* The element at and after the insertion position is shifted to the right */ *(p + 1) = *p; *q = e; ++(L->length); return OK; }
Delete element to linear table
To delete the i(i ∈ [1, n]) th element, move the i+1 → N element forward one position.
Status ListDelete(SqList* L, int i, ElemType* e) { if (i < 1 || i > L->length) return ERROR; ElemType* p = L->elem + i - 1; *e = *p; ElemType* q = L->elem + L->length - 1; for (++p; p <= q; ++p) *(p - 1) = *p; --(L->length); return OK; }
Traverse the data elements of the linear table and perform operations of the same type
Status ListTraverse(SqList* L, void(*visit)(ElemType*)) { int i; ElemType* p = L->elem; for (i = 1; i <= L->length; ++i) visit(p); return OK; }
Exchange the location of two data elements
Status SwapElem(ElemType* x, ElemType* y) { ElemType* z = (ElemType*)malloc(sizeof(ElemType*)+1); if (z == NULL) return OVERFLOWED; *z = *x; *x = *y; *y = *z; return OK; }
Sort entire linear table
Because I've only learned bubble sorting at present, let's use it here
sort_ When manner ≤ 0, it indicates descending sorting;
sort_ When manner > 1, it indicates ascending sorting;
Key is the primary key for sorting. According to the project situation, it can only sort the following items:
- Chinese l
- Mathematics m
- English e
- Average score a
- Total score s
- Student number n
Status ListSort(SqList* L, int sort_manner, char key) { int i, j; Boolean flag = TRUE; for (i = 0; i < (L->length) - 1 && flag == TRUE; i++) { flag = FALSE; for (j = 0; j < (L->length) - i - 1; j++) { if (sort_manner <= 0) { switch (key) { case 'a': if ((L->elem)[j].average_score < (L->elem)[j + 1].average_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 's': if ((L->elem)[j].sum_score < (L->elem)[j + 1].sum_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'l': if ((L->elem)[j].score_literature < (L->elem)[j + 1].score_literature) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'm': if ((L->elem)[j].score_math < (L->elem)[j + 1].score_math) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'e': if ((L->elem)[j].score_english < (L->elem)[j + 1].score_english) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'n': if ((L->elem)[j].num < (L->elem)[j + 1].num) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; default: printf("The primary key does not support sorting\n"); return ERROR; } } else { switch (key) { case 'a': if ((L->elem)[j].average_score > (L->elem)[j + 1].average_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 's': if ((L->elem)[j].sum_score > (L->elem)[j + 1].sum_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'l': if ((L->elem)[j].score_literature > (L->elem)[j + 1].score_literature) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'm': if ((L->elem)[j].score_math > (L->elem)[j + 1].score_math) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'e': if ((L->elem)[j].score_english > (L->elem)[j + 1].score_english) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'n': if ((L->elem)[j].num > (L->elem)[j + 1].num) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; default: printf("The primary key does not support sorting\n"); return ERROR; } } } } return OK; }
kernel_ list. Complete code of H の:
#pragma once / / Solve various strange problems caused by macro names /********************************************************************* * Reprint please indicate the source * @FileName kernellist.h * @Description core linear table * @History * version author data introduction and operations * 1.0 Initial view: create on January 23, 2022 * *** *** ****-**-** ******* */ #include "preconf.h" /********************************************************************* * @chujian(cn) Import custom header file * @chujian(en) Import custom header files */ Status InitList(SqList* L) { L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if (!(L->elem)) exit(OVERFLOWED); L->length = 0; L->list_size = LIST_INIT_SIZE; return OK; } /********************************************************************* * @chujian(cn) initialization * @chujian(en) Initialize */ Status DestroyList(SqList* L) { free(L->elem); L->elem = NULL; L->length = 0; L->list_size = 0; return OK; } /********************************************************************* * @chujian(cn) Destroy * @chujian(en) Destroy */ Status ClearList(SqList* L) { L->length = 0; return OK; } /********************************************************************* * @chujian(cn) empty * @chujian(en) Reset */ Boolean ListEmpty(SqList* L) { if (L->length == 0) return TRUE; else return FALSE; } /********************************************************************* * @chujian(cn) Judge empty table * @chujian(en) Judge the empty table */ int ListLength(SqList* L) { return L->length; } /********************************************************************* * @chujian(cn) Return length * @chujian(en) Retrun length of the table */ Status GetElem(SqList* L, int i, ElemType* e) { if (i < 1 || i > ListLength(L)) exit(ERROR); *e = *((L->elem) + i - 1); return OK; } /********************************************************************* * @chujian(cn) Get the i th data element * @chujian(en) Get the i-th data element */ int LocateElem(SqList* L, ElemType* e, Status(*compare)(ElemType, ElemType)) { ElemType* p; int i = 1; p = L->elem; while (i <= (L->length) && !(*compare)(*e, *(p++))) ++i; if (i <= (L->length)) return i; else return 0; } /********************************************************************* * @chujian(cn) Returns the bit order of data elements satisfying a specific relationship * @chujian(en) Returns the bit order of data elements that satisfy a specified relationship */ Status SearchElem(SqList* L, int cur_e, ElemType* e, int mode_manner) { ElemType* r = L->elem; int i = 1; while (i <= L->length && r->num != cur_e) { i++; r++; } if (i > L->length) return INFEASIBLE; switch (mode_manner) { case -1: *e = *(--r); return OK; case 0: *e = *r; return OK; case 1: *e = *(++r); return OK; default: return ERROR; } return OK; } /********************************************************************* * @chujian(cn) Student number search engine * @chujian(en) Student ID Search Engine */ Status ListInsert(SqList* L, int i, ElemType e) { if (i < 1 || i >(L->length) + 1) return ERROR; if (L->length >= L->list_size) { ElemType* new_base = (ElemType*)realloc(L->elem, (L->length + LIST_INCREMENT) * sizeof(ElemType)); if (!new_base) exit(OVERFLOWED); L->elem = new_base; L->list_size += LIST_INCREMENT; } ElemType* q = &(L->elem[i - 1]); ElemType* p = &(L->elem[(L->length) - 1]); for (p = &(L->elem[(L->length) - 1]); p >= q; --p) /* The element at and after the insertion position is shifted to the right */ *(p + 1) = *p; *q = e; ++(L->length); return OK; } /********************************************************************* * @chujian(cn) insert * @chujian(en) insert */ Status ListDelete(SqList* L, int i, ElemType* e) { if (i < 1 || i > L->length) return ERROR; ElemType* p = L->elem + i - 1; *e = *p; ElemType* q = L->elem + L->length - 1; for (++p; p <= q; ++p) *(p - 1) = *p; --(L->length); return OK; } /********************************************************************* * @chujian(cn) delete * @chujian(en) delete */ Status ListTraverse(SqList* L, void(*visit)(ElemType*)) { int i; ElemType* p = L->elem; for (i = 1; i <= L->length; ++i) visit(p); return OK; } /********************************************************************* * @chujian(cn) Traverse all data for the same operation * @chujian(en) Iterate over all data for the same operation */ Status SwapElem(ElemType* x, ElemType* y) { ElemType* z = (ElemType*)malloc(sizeof(ElemType*)+1); if (z == NULL) return OVERFLOWED; *z = *x; *x = *y; *y = *z; return OK; } /********************************************************************* * @chujian(cn) Exchange the location of two data elements * @chujian(en) Swap the positions of two data elements */ Status ListSort(SqList* L, int sort_manner, char key) { int i, j; Boolean flag = TRUE; for (i = 0; i < (L->length) - 1 && flag == TRUE; i++) { flag = FALSE; for (j = 0; j < (L->length) - i - 1; j++) { if (sort_manner <= 0) { switch (key) { case 'a': if ((L->elem)[j].average_score < (L->elem)[j + 1].average_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 's': if ((L->elem)[j].sum_score < (L->elem)[j + 1].sum_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'l': if ((L->elem)[j].score_literature < (L->elem)[j + 1].score_literature) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'm': if ((L->elem)[j].score_math < (L->elem)[j + 1].score_math) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'e': if ((L->elem)[j].score_english < (L->elem)[j + 1].score_english) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'n': if ((L->elem)[j].num < (L->elem)[j + 1].num) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; default: printf("The primary key does not support sorting\n"); return ERROR; } } else { switch (key) { case 'a': if ((L->elem)[j].average_score > (L->elem)[j + 1].average_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 's': if ((L->elem)[j].sum_score > (L->elem)[j + 1].sum_score) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'l': if ((L->elem)[j].score_literature > (L->elem)[j + 1].score_literature) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'm': if ((L->elem)[j].score_math > (L->elem)[j + 1].score_math) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'e': if ((L->elem)[j].score_english > (L->elem)[j + 1].score_english) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; case 'n': if ((L->elem)[j].num > (L->elem)[j + 1].num) { SwapElem(&((L->elem)[j]), &((L->elem)[j + 1])); flag = TRUE; } break; default: printf("The primary key does not support sorting\n"); return ERROR; } } } } return OK; } /********************************************************************* * @chujian(cn) Bubble sort (looks cumbersome because there are many different sort items) * @chujian(en) Bubble Sor(Seems cumbersome here because of multiple different sort items) */