In addition to the other issues discussed, your code leaks memory badly. You cannot simply continue to malloc
data_handler
each iteration. When you do, you overwrite the address of the previously allocated block losing the ability to free
the previously allocated memory. Instead, you must realloc
the original block of memory, preserving the pointer data
pointing to starting address for the block of memory and then update data_handler
accordingly.
This is a fundamental memory management issue.
Instead, it looks like you were attempting to do the following. I apologize if I misinterpreted your goal, but otherwise your code does not make sense:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char *data = NULL;
char *data_handler = NULL;
int i = 0, len = 0, req = 0;
char test[10] = "test";
req = snprintf (NULL, 0, "[%s] ", test);
printf ("%d --> req\n", req);
for (; i < 10; i++) {
if (data == NULL) {
data = malloc (req + 1);
data_handler = data;
}
else {
char *tmp = realloc (data, len + req + 1); /* realloc data */
if (!tmp) { /* validate */
fprintf (stderr, "realloc() error: virtual memory exhausted.\n");
return 1;
}
data = tmp; /* assign tmp to data */
data_handler = data + len; /* update data_handler */
}
len += snprintf (data_handler, sizeof test, "[%s] ", test);
printf ("-- (len: %d)\nData --> %s\n", len, data);
printf ("Data Handler --> %s\n", data_handler);
}
putchar ('\n');
free (data);
return 0;
}
Example Use/Output
$ ./bin/snprintfnoinit
7 --> req
-- (len: 7)
Data --> [test]
Data Handler --> [test]
-- (len: 14)
Data --> [test] [test]
Data Handler --> [test]
-- (len: 21)
Data --> [test] [test] [test]
Data Handler --> [test]
-- (len: 28)
Data --> [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 35)
Data --> [test] [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 42)
Data --> [test] [test] [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 49)
Data --> [test] [test] [test] [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 56)
Data --> [test] [test] [test] [test] [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 63)
Data --> [test] [test] [test] [test] [test] [test] [test] [test] [test]
Data Handler --> [test]
-- (len: 70)
Data --> [test] [test] [test] [test] [test] [test] [test] [test] [test] [test]
Data Handler --> [test]
Memory Use/Error Check
In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind
is the normal choice. It is simple to use.
$ valgrind ./bin/snprintfnoinit
==25279== Memcheck, a memory error detector
==25279== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==25279== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==25279== Command: ./bin/snprintfnoinit
==25279==
7 --> req
-- (len: 7)
Data --> [test]
Data Handler --> [test]
-- (len: 14)
...
==25279==
==25279== HEAP SUMMARY:
==25279== in use at exit: 0 bytes in 0 blocks
==25279== total heap usage: 10 allocs, 10 frees, 395 bytes allocated
==25279==
==25279== All heap blocks were freed -- no leaks are possible
==25279==
==25279== For counts of detected and suppressed errors, rerun with: -v
==25279== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over the changes to the code and let me know if you have any questions.