4.7 KiB
Since you are currently navigating the transition from "fixed-size" allocation (calculating everything upfront) to "dynamic" allocation (growing as you go), the best way to learn is to build in stages.
The reason you didn't need a count or capacity in your Darray is that you did Pre-Calculation: you figured out exactly how many digits were in the number before you called malloc.
But for user input, you can't pre-calculate. You need Dynamic Growth.
Here is a learning path split into 4 sub-assignments.
Assignment 1: The "Fixed" Dynamic Array
Goal: Master the basic Struct \rightarrow Pointer relationship.
Task: Create a program that asks the user "How many numbers do you want to enter?".
- Create a struct
IntArraywithint *dataandsize_t length. mallocthe struct.mallocthedataarray based on the user's number.- Fill it, print it, and free it. Key Lesson: Understanding that the struct is the "handle" and the pointer is the "payload."
Assignment 2: The "Growing" Dynamic Array
Goal: Understand capacity vs length and realloc.
Task: Modify Assignment 1, but don't ask the user for the size upfront.
- Add
size_t capacityto your struct. - Start
capacityat 2. - Use a
whileloop to take input until the user types-1. - Inside the loop: If
length == capacity, usereallocto double thecapacity. Key Lesson:lengthis how many items are actually there;capacityis how many items can fit before you need more memory.
Assignment 3: The "Pointer to Pointers" (The String Array)
Goal: Understand "Deep Allocation" (Double Indirection).
Task: Change your IntArray to a StringArray.
- Your struct now has
char **words. - When you
reallocthewordsarray, you are only making room for more addresses (pointers). - For every string the user enters, you must
malloca new piece of memory for that specific string andstrcpythe input into it. Key Lesson: Achar**is just an array of pointers. The array itself is one allocation, but every string it points to is a separate allocation.
Assignment 4: The "Deep Free"
Goal: Prevent memory leaks in complex structures.
Task: Write a dedicated free_string_array(StringArray *sa) function.
- It must loop through the array and
free()every individual string first. - Then it must
free()thewordspointer array. - Finally, it must
free()the struct itself. Key Lesson: You must free memory in the exact reverse order that you allocated it.
The "Scaffold" (Start here for Assignment 3/4)
If you want to jump straight to the strings, here is a clean scaffold. I have left "TODOs" where the logic should go.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **words;
size_t length;
size_t capacity;
} StringArray;
// Initialize the "Manager" struct
StringArray* create_sa() {
StringArray *sa = malloc(sizeof(StringArray));
sa->length = 0;
sa->capacity = 4; // Start small to force realloc to happen
sa->words = malloc(sizeof(char*) * sa->capacity);
return sa;
}
void add_word(StringArray *sa, const char *input) {
// TODO 1: Check if length == capacity.
// If so, use realloc to double the capacity of sa->words.
// TODO 2: Allocate memory for the new string.
// Hint: strlen(input) + 1
// TODO 3: Copy the input into the newly allocated memory.
// TODO 4: Store that pointer in sa->words[sa->length] and increment length.
}
void free_sa(StringArray *sa) {
// TODO 5: Loop through sa->words and free each individual string.
// TODO 6: Free the sa->words array itself.
// TODO 7: Free the sa struct.
}
int main() {
StringArray *myList = create_sa();
char temp[100];
printf("Enter words (type 'exit' to stop):\n");
while (scanf("%99s", temp) == 1 && strcmp(temp, "exit") != 0) {
add_word(myList, temp);
}
printf("\nStored words:\n");
for (size_t i = 0; i < myList->length; i++) {
printf("%zu: %s\n", i, myList->words[i]);
}
free_sa(myList);
return 0;
}
Final tip on your Darray logic:
In your Darray code, you did:
DigDarr->digits = (int *)(DigDarr + 1);
This is very "clever" code, but in professional C, it is often avoided unless you are writing a high-performance kernel or library. It makes the code harder to read and makes realloc almost impossible (because the data is physically glued to the struct).
The standard way is to have the struct hold a pointer to a separate block of memory. It's slightly slower (one extra pointer jump), but it's much safer and more flexible.