I have some code that was written to be as conservative as possible with memory use, so it does things like use realloc() for building strings a character at a time instead of a one-time fixed-length buffer as is commonplace. The advantage I see to this is you don't ever over-use memory in environments where that's important and you don't have to worry about the buffer being too small for corner cases and writing some realloc() code anyway to cover that. My question is if this thrashes memory horribly or can cause any other problems, or if perhaps the compiler optimizes this away.
Here is a sample program that reads a text file using no fixed-length buffers and it works fine. Why would you or would you not write code like this? Is it a patently bad idea?
// Compile with: gcc a.c -o a
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
void *tmp;
char **array = NULL; // array of line strings
size_t len = 0; // length of a line
int i = 0; // line count
// Open file
FILE* f;
if (!(f = fopen("./test.txt", "r"))) {
printf("Err: open file\n");
return 1;
}
// Read file into memory
int chr;
while ((chr = fgetc(f)) != EOF) {
// Reserve mem for another line
if (len == 0) {
// add 2 -- 1 for null terminator, 1 cuz i starts at 0
if ((tmp = realloc(array, (i+2)*sizeof(*array))) == NULL) {
printf("Err: realloc\n");
return 1;
}
array = tmp;
array[i] = NULL;
array[i+1] = NULL;
}
// Increase mem for next char in the line
// add 2 -- 1 for null terminator, 1 cuz len starts at 0
// Q: Why is best practice to omit sizeof knowing that for char it is always 1... all other alloc's need to use sizeof so the inconsistency leads to mistakes
if ((tmp = realloc(array[i], (len+2)*sizeof(*(array[i])))) == NULL) {
// if ((tmp = realloc(array[i], len+2)) == NULL) {
printf("Err: realloc\n");
return 1;
}
array[i] = tmp;
array[i][len] = (char)chr;
array[i][len+1] = '\0';
len++;
// newline
if (chr == '\n') {
len = 0;
i++;
}
}
if (ferror(f)) {
printf("Err: read file\n");
return 1;
}
// Verify input
printf("\nRead:\n-----\n");
for (i=0; array[i] != NULL; i++) { // array is NULL terminated
printf("[%d] %s", i+1, array[i]);
free(array[i]);
}
free(array);
printf("\nDone\n");
fclose(f);
return 0;
}
When you call
malloc()orrealloc()for the first time, he gonna allocated a big chunk of memory. That for future call and not asking for more memory at every single use.realloc()Before everything look behind the allocated block and see if he can extend the block with some free space. if he can't hemalloc()a new memory space for you. That a good strategy, but that don't work that well in your case.The problem here is you
realloc()multiple pointer. Every time hemalloc()a new block for you, he need to copy the previous block in the new one. After that he free your first block and return the new one. So when you try torealloc()multiple pointer, your memory look like a mess and you just create some fragmentation.Other thing, memory is really cheap now day's, you can allocated much more. To compare with c++ container implementation, they alloc the space they need * 2, avoiding linear time to increase performance. You lose a lot of time with all the
realloc()call's.