c - dynamically allocating a string with unknown size -
i have names
known number of names input 1 string each separated space, have dynamically allocate memory array of strings each string gets name,
char** names; char ch; names = malloc(n*sizeof(char*); /*n defined*/ for(i=0; i<n; i++) {
now have allocate each string without using defined number:
i=0, j=0; while ((ch=getchar) != '\n') { while (ch != ' ') { names[i][j++] = ch; } if (ch == ' ') { names[i][j] = '\0'; i++}} if (ch == '\n') names[i][j] = '\0';
this classic question of how handle dynamic allocation , reallocation store unknown number of strings. (with twist separate each string individual tokens before saving array) worth understanding process in detail serve basis any other circumstance reading unknown number of values (whether structs, floats, characters, etc...).
there number of different types of data structures can employ, lists, trees, etc., basic approach creating array of pointer-to-pointer-to-type (with type being char
in case) , allocating space for, filling data, , assigning starting address new block of memory each pointer data read. short-hand pointer-to-pointer-to-type double-pointer (e.g. char **array;
, technically pointer-to-pointer-to-char or pointer-to-char* if like)
the general, , efficient, approach allocating memory unknown number of lines first allocate reasonably anticipated number of pointers (1 each anticipated token). more efficient calling realloc
, reallocating entire collection every token add array. here, keep counter of number of tokens added array, , when reach original allocation limit, simmply reallocate twice number of pointers currenly have. note, free add incremental amount choose. can add fixed amount each time, or can use scaled multiple of original -- it's you. realloc twice current 1 of standard schemes.
what "a reasonably anticipated number of pointers?" it's no precise number. want take educated guess @ number of tokens roughtly expect , use initial number allocating pointers. wouldn't want allocate 10,000 pointers if expect 100. horribly wasteful. reallocation take care of shortfall, rough guess needed. if have no idea, allocate reasonable number, 64
or 128
, etc.. can declare limit constant @ beginning of code, adjusted. e.g.:
#declare maxptr 128
or accomplish same thing using anonymous enum
enum { maxptr = 128 };
when allocating pointers originally, , part of reallocation, can benefit setting each pointer null
. accomplished original allocation. use calloc
instead of malloc
. on reallocation, requires set new pointers allocated null
. benefit provides first null
acts sentinel indicating point @ valid pointers stop. long insure have @ least 1 null
preserved sentinel, can iterate without benefit of knowing precise number of pointers filled. e.g.:
size_t = 0; while (array[i]) { ... stuff ... }
when done using allocated memory, want insure free memory. while in simple piece of code, memory freed on exit, in habit of tracking memory allocate , freeing when no longer needed.
as particular task, want read line of unknown number of characters memory , tokenize (separate) string tokens. getline
read , allocate memory sufficient hold size character string. can same thing of other input functions, have code repeated checks , reallocations yourself. if getline
available (it in every modern compier), use it. matter of separating input tokens strtok
or strsep
. want duplicate each token preserve each token in own block of memory , assign location array of tokens. following provides short example.
included in example several helper functions opening files, allocating , reallocating. simple error checking keep main body of code clean , readable. on example , let me know if have questions.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define maxl 64 /* initial number of pointers */ /* simple helper/error check functions */ file *xfopen (const char *fn, const char *mode); void *xcalloc (size_t n, size_t s); void *xrealloc_dp (void *ptr, size_t *n); int main (int argc, char **argv) { char **array = null; char *line = null; size_t i, idx = 0, maxl = maxl, n = 0; ssize_t nchr = 0; file *fp = argc > 1 ? xfopen (argv[1], "r") : stdin; array = xcalloc (maxl, sizeof *array); /* allocate maxl pointers */ while ((nchr = getline (&line, &n, fp)) != -1) { while (nchr > 0 && (line[nchr-1] == '\r' || line[nchr-1] == '\n')) line[--nchr] = 0; /* strip carriage return or newline */ char *p = line; /* pointer use strtok */ (p = strtok (line, " \n"); p; p = strtok (null, " \n")) { array[idx++] = strdup (p); /* allocate & copy */ /* check limit reached - reallocate */ if (idx == maxl) array = xrealloc_dp (array, &maxl); } } free (line); /* free memory allocated getline */ if (fp != stdin) fclose (fp); (i = 0; < idx; i++) /* print tokens */ printf (" array[%2zu] : %s\n", i, array[i]); (i = 0; < idx; i++) /* free memory */ free (array[i]); free (array); return 0; } /* fopen error checking */ file *xfopen (const char *fn, const char *mode) { file *fp = fopen (fn, mode); if (!fp) { fprintf (stderr, "xfopen() error: file open failed '%s'.\n", fn); // return null; exit (exit_failure); } return fp; } /* simple calloc error checking */ void *xcalloc (size_t n, size_t s) { void *memptr = calloc (n, s); if (memptr == 0) { fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n"); exit (exit_failure); } return memptr; } /* realloc array of pointers ('memptr') twice current * number of pointer ('*nptrs'). note: 'nptrs' pointer * current number updated value preserved. * no pointer size required known (simply size * of pointer */ void *xrealloc_dp (void *ptr, size_t *n) { void **p = ptr; void *tmp = realloc (p, 2 * *n * sizeof tmp); if (!tmp) { fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__); exit (exit_failure); } p = tmp; memset (p + *n, 0, *n * sizeof tmp); /* set new pointers null */ *n *= 2; return p; }
input file
$ cat dat/captnjack.txt tale of captain jack sparrow pirate brave on 7 seas.
output
$ ./bin/getline_strtok <dat/captnjack.txt array[ 0] : array[ 1] : array[ 2] : array[ 3] : tale array[ 4] : of array[ 5] : captain array[ 6] : jack array[ 7] : sparrow array[ 8] : array[ 9] : pirate array[10] : array[11] : brave array[12] : on array[13] : array[14] : 7 array[15] : seas.
memory/error check
in code write dynamically allocates memory, have 2 responsibilites regarding block of memory allocated: (1) preserves pointer starting address block of memory so, (2) can freed when no longer needed. imperative use memory error checking program insure haven't written beyond/outside allocated block of memory , confirm have freed memory have allocated. linux valgrind
normal choice. there many subtle ways misuse block of memory can cause real problems, there no excuse not it. there similar memory checkers every platform. simple use. run program through it.
$ valgrind ./bin/getline_strtok <dat/captnjack.txt ==26284== memcheck, memory error detector ==26284== copyright (c) 2002-2012, , gnu gpl'd, julian seward et al. ==26284== using valgrind-3.8.1 , libvex; rerun -h copyright info ==26284== command: ./bin/getline_strtok ==26284== array[ 0] : array[ 1] : <snip> array[14] : 7 array[15] : seas. ==26284== ==26284== heap summary: ==26284== in use @ exit: 0 bytes in 0 blocks ==26284== total heap usage: 18 allocs, 18 frees, 708 bytes allocated ==26284== ==26284== heap blocks freed -- no leaks possible ==26284== ==26284== counts of detected , suppressed errors, rerun with: -v ==26284== error summary: 0 errors 0 contexts (suppressed: 2 2)
what want confirm each time "all heap blocks freed -- no leaks possible" , "error summary: 0 errors 0 contexts".
Comments
Post a Comment