The following example shows how to create a wrapper to another structure, which helps to manage the size of a structure – how many elements are in total and for how many is there room for? Wrapper structure holds those numbers and additionally it holds the pointer to the structure array which we allocate dynamically.
To test the program workflow, you have been provided with a sample data file. With the sample data, it creates a structure that can hold 2 members. Then it says it wishes to insert two members and enters the details for those. After this, it attempts to increase it by 3 new data lines. Depending on the implementation used, the result will differ. To try out the test data, use stream redirection ( ./program < test_data.txt )
1 2 3 4 5 6 7 8 |
2 2 0114 Rasmus Aare 38101012210 9.20 0115 Tarmo Waldorf 38909091120 14.20 3 0116 Jane Kask 48511113320 12.20 0117 Aare Kallas 38412124330 7.45 0118 Alexandra Sepp 49105040334 15.00 |
The following code files are given: .c file and the header file. There’s a macro in the codefile called EXPANDABLE, which value decides if the structure can be resized during reading or not. When setting the value to 1, it uses realloc() to expand the memory area. If it is set to 0, the structure size is fixed and cannot be increased.
The expanding is done with the principle, that each time the amount of memory is increased by 2x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#ifndef WRAPPER_H #define WRAPPER_H #define EID_LEN 12 #define MAX_NAME 2511 typedef struct { int code; char fName[MAX_NAME]; char lName[MAX_NAME]; char eID[EID_LEN]; float wagePerHour; } employee; typedef struct { employee *db; unsigned limit; unsigned used; } employee_list; void AllocateList(employee_list *wf); void InsertMembers(employee_list *wf); void InsertMembersWithExpandability(employee_list *wf); void printMembers(employee_list wf); employee CreateMember(); #endif // WRAPPER_H |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
/** * File: wrapper_struct.c * Author: Risto Heinsar * Created: 28.02.2018 * Edited: 13.02.2024 * * Description: The following code will demonstrate a couple of hints on allocating * memory in C. * First of all, it will demonstrate the use of a wrapper structure * which will give better control over size management. In this case, * it will track how many memory slots are in use and how many are * there in total. This allows to keep the data neatly together * and avoid array overflows. * Secondly, this code also demonstrates a possibility to safely * expand the storage using reallocation. The reallocate step is 2x * of what it was previously, so it would be more optimal in speed. */ #include <stdio.h> #include <stdlib.h> #include "wrapper.h" // Use 0 for non-expandable list, 1 for expandable using realloc. #define EXPANDABLE 1 int main(void) { employee_list workForce = {NULL, 0, 0}; AllocateList(&workForce); if (EXPANDABLE) { InsertMembersWithExpandability(&workForce); InsertMembersWithExpandability(&workForce); } else { InsertMembers(&workForce); InsertMembers(&workForce); } printMembers(workForce); free(workForce.db); return 0; } /** * Allocate an initial list, mostly for demonstrative purposes and compatibility * with both methods of inserting members (fixed and expandable) */ void AllocateList(employee_list *wf) { unsigned n; printf("For many employees is storage required?\n"); scanf("%u", &n); wf->db = (employee *)calloc(n, sizeof(employee)); if (wf->db == NULL) { perror("Error allocating memory"); exit(EXIT_FAILURE); } wf->limit = n; } /** * Insert members to the structure array. This is only capable of adding members * until the array is full. It will also keep track of how many members have been * added. The array cannot be expanded using this function */ void InsertMembers(employee_list *wf) { unsigned n; printf("How many employees to add?\n"); scanf("%u", &n); if (n <= wf->limit - wf->used) { for (int i = 0; i < n; i++) { printf("Adding member %d out of %d\n", i + 1, n); *(wf->db + wf->used) = CreateMember(); wf->used++; } } else { printf("Error! %d out of %d slots are in use. Storage left for %d " "Employees\n", wf->used, wf->limit, wf->limit - wf->used); } } /** * Function to create a new struct member with data. The struct will be returned * so it can be added to the appropriate array later using the preferred method. */ employee CreateMember() { employee newEmployee; printf("Enter employee code:\n"); scanf("%d", &newEmployee.code); printf("Enter first name:\n"); scanf("%s", newEmployee.fName); printf("Enter last name:\n"); scanf("%s", newEmployee.lName); printf("Enter Estonian identification number:\n"); scanf("%s", newEmployee.eID); printf("Enter wage per hour:\n"); scanf("%f", &newEmployee.wagePerHour); return newEmployee; } /** * Prints the list of members in the struct array */ void printMembers(employee_list wf) { printf("%d out of %d slots are in use\n", wf.used, wf.limit); printf("%7s %12s %15s %15s %6s\n", "code", "eID", "fName", "lName", "wage"); for (int i = 0; i < wf.used; i++) { printf("%7d %12s %15s %15s %6.2f\n", (wf.db + i)->code, (wf.db + i)->eID, (wf.db + i)->fName, (wf.db + i)->lName, (wf.db + i)->wagePerHour); } } /** * Insert members to the structure array. This function will try to expand * the dynamically allocated array if it is possible. The reallocate will be done * safely. If for some reason the expansion fails, the program can be allowed to * continue */ void InsertMembersWithExpandability(employee_list *wf) { unsigned n; printf("How many employees to add?\n"); scanf("%u", &n); int i = 0; while (i < n) { // first lets create some code that detects if array is full if (wf->limit == wf->used) { // let's make the new limit 2x from what we had int newLimit = 2 * wf->limit; printf("We ran out of memory. Trying to expand array from %d to %d\n", wf->limit, newLimit); // reallocate the new memory employee *pTemp = realloc(wf->db, sizeof(employee) * newLimit); // check if we were successful if (pTemp != NULL) { // "sync" the pointers and update the limit wf->db = pTemp; wf->limit = newLimit; printf("Allocation success! New limit is %d\n", wf->limit); } else { printf("Allocation failed. Old limit of %d is being used\n", wf->limit); } } // Check if we are have capacity to write new data if (wf->used < wf->limit) { printf("Adding member %d out of %d\n", i + 1, n); *(wf->db + wf->used) = CreateMember(); wf->used++; i++; } else { printf("Unfortunately the program ran out of memory.\n" "Expansion of the employee list has been aborted\n" "You can continue with what you have so far\n"); break; } } } |