c - Pointer of pointers starts to go wild after being freed -


i have function splits string multiple strings char.

#include <stdlib.h> #include <stdio.h> #include "tokens.h"  char isseparator(char character, char *seps) {     int = 0;     while(*(seps + i) != '\0') {         if (*(seps + i) == character) {             return 1;         }         ++i;     }      return 0;     }      int parse_tokens(char *str, char *seps, char ***tokens) {         /* implemente aca la funcion parse_tokens */         char **foundtokens = (char **) malloc(10*sizeof(char*));      int = 0;      int tokencount = 0;     int tokenletter = 0;     while(*(str + i) != '\0') {          if (!isseparator(*(str + i), seps)) {             if(*(foundtokens + tokencount) == null) {                 *(foundtokens + tokencount)= (char *) malloc(30*sizeof(char));             }             *(*(foundtokens + tokencount) + tokenletter) = *(str + i);             ++tokenletter;         } else {             // revisar si es que el slot de token actual no esta vacio             if(*(foundtokens + tokencount) != null) {                 ++tokencount;             }             tokenletter = 0;         }             ++i;     }     if(*(foundtokens + tokencount) != null) {         ++tokencount;     }     *(foundtokens + tokencount + 1) = null;     *tokens = foundtokens;     return tokencount; }  void free_tokens(char **tokens) {     /* implemente aca la funcion free_tokens */     int = 0;     while(*(tokens + i) != null) {         free(*(tokens + i));           ++i;        }     free(tokens); } 

an example of use be:

char **argv; int argc= parse_tokens("hello,world", ",", &argv); free_tokens(argv); 

using this:

{     char **argv;     int argc= parse_tokens("", " ", &argv); } {     char **argv;     int argc= parse_tokens("hello world", ",", &argv); } {     char **argv;     int argc= parse_tokens(         "hola, como te va; bien?",         " ,;",         &argv); } 

makes last parse_tokens fail. showing first token "como" instead of "hola".

the function works fine, when add free_tokens weird bugs start appear. mallocs don't called, previous token found in new allocated array, garabage data same program. playing free function yield different results.

since assaingment answers should contain least code posible. in short question is: may calling free can change values of subsequent function calls? , since does, why it? (the free function isn't showing errors)

when run under valgrind, valgrind complains about:

==27284== conditional jump or move depends on uninitialised value(s) ==27284==    @ 0x40071c: parse_tokens (toks.c:37) ==27284==    0x400521: main (toks.c:79) 

in source code i'm using (reformatted can comprehend it), line 37 is:

 25 int parse_tokens(char *str, char *seps, char ***tokens)  26 {  27     /* implemente aca la funcion parse_tokens */  28     char **foundtokens = (char **) malloc(10 * sizeof(char *));  29   30     int = 0;  31     int tokencount = 0;  32     int tokenletter = 0;  33     while (*(str + i) != '\0')  34     {  35         if (!isseparator(*(str + i), seps))  36         {  37             if (*(foundtokens + tokencount) == null)  38             {  39                 *(foundtokens + tokencount) = (char *) malloc(30 * sizeof(char));  40             } 

the trouble malloc() not initialize memory, there no way know priori whether foundtokens[tokencount] null or not.

changing call malloc() calloc(10, sizeof(char *)) eliminates problem.

i added loop print values parse_tokens(), , still 1 warning:

==27300== conditional jump or move depends on uninitialised value(s) ==27300==    @ 0x4e80f90: vfprintf (vfprintf.c:1655) ==27300==    0x4f44830: __printf_chk (printf_chk.c:36) ==27300==    0x400596: main (stdio2.h:104) ==27300==  0x51fc0d0 = [hello] 0x51fc130 = [world] (nil) = [<null>] ==27300==  

the main() function is:

int main(void) {     char **argv;     int argc = parse_tokens("hello,world", ",", &argv);      (int = 0; <= argc; i++)         printf("%p = [%s]\n", argv[i], (argv[i] != 0) ? argv[i] : "<null>");      free_tokens(argv);     return 0; } 

since close brace line 86 in source code, i'm little puzzled yet.


got it; not null terminating strings. code runs cleanly under valgrind:

#include <stdio.h> #include <stdlib.h>  char isseparator(char character, char *seps); int parse_tokens(char *str, char *seps, char ***tokens); void free_tokens(char **tokens);  char isseparator(char character, char *seps) {     int = 0;     while (*(seps + i) != '\0')     {         if (*(seps + i) == character)         {             return 1;         }         ++i;     }     return 0; }  int parse_tokens(char *str, char *seps, char ***tokens) {     char **foundtokens = (char **) calloc(10, sizeof(char *));     int = 0;     int tokencount = 0;     int tokenletter = 0;      while (str[i] != '\0')     {         if (!isseparator(str[i], seps))         {             if (foundtokens[tokencount] == null)                 foundtokens[tokencount] = (char *) malloc(30 * sizeof(char));             foundtokens[tokencount][tokenletter] = str[i];             ++tokenletter;         }         else         {             if (foundtokens[tokencount] != null)             {                 foundtokens[tokencount][tokenletter] = '\0';                 ++tokencount;             }             tokenletter = 0;         }         ++i;     }     if (foundtokens[tokencount] != null)     {         foundtokens[tokencount][tokenletter] = '\0';         ++tokencount;     }     foundtokens[tokencount+1] = null;     *tokens = foundtokens;     return tokencount; }  void free_tokens(char **tokens) {     (int = 0; tokens[i] != null; i++)         free(tokens[i]);     free(tokens); }  int main(void) {     char **argv;     int argc = parse_tokens("hello,world", ",", &argv);      printf("argc = %d\n", argc);     (int = 0; <= argc; i++)         printf("%p = [%s]\n", (void *)argv[i], (argv[i] != 0) ? argv[i] : "<null>");      free_tokens(argv);     return 0; } 

for example:

==27349== memcheck, memory error detector ==27349== copyright (c) 2002-2012, , gnu gpl'd, julian seward et al. ==27349== using valgrind-3.8.1 , libvex; rerun -h copyright info ==27349== command: ./toks2 ==27349==  argc = 2 0x51fc0d0 = [hello] 0x51fc130 = [world] (nil) = [<null>] ==27349==  ==27349== heap summary: ==27349==     in use @ exit: 0 bytes in 0 blocks ==27349==   total heap usage: 3 allocs, 3 frees, 140 bytes allocated ==27349==  ==27349== heap blocks freed -- no leaks possible ==27349==  ==27349== counts of detected , suppressed errors, rerun with: -v ==27349== error summary: 0 errors 0 contexts (suppressed: 2 2) 

make sure read initialized data time! not rely on data malloc() being zeroed; in general, won't be.


this version of code compiles cleanly, avoids subscripting operations whether written *(ptr + index) or ptr[index].

#include <stdio.h> #include <stdlib.h>  char isseparator(char character, char *seps); int parse_tokens(char *str, char *seps, char ***tokens); void free_tokens(char **tokens);  char isseparator(char character, char *seps) {     while (*seps != '\0')     {         if (*seps++ == character)             return 1;     }     return 0; }  int parse_tokens(char *str, char *seps, char ***tokens) {     char **foundtokens = (char **) calloc(10, sizeof(char *));     char **current = foundtokens;     char *word = null;     char c;      while ((c = *str++) != '\0')     {         if (!isseparator(c, seps))         {             if (word == null)             {                 *current = (char *) malloc(30 * sizeof(char));                 word = *current++;             }             *word++ = c;         }         else         {             if (word != null)                 *word = '\0';             word = null;         }     }     if (word != null)         *word = '\0';     *tokens = foundtokens;     return current - foundtokens; }  void free_tokens(char **tokens) {     (int = 0; tokens[i] != null; i++)         free(tokens[i]);     free(tokens); }  int main(void) {     char **argv;      int argc = parse_tokens("hello,world", ",", &argv);      printf("argc = %d\n", argc);     (int = 0; <= argc; i++)         printf("%p = [%s]\n", (void *)argv[i], (argv[i] != 0) ? argv[i] : "<null>");      free_tokens(argv);      argc = parse_tokens(",abc,,axolotl,,zoological society gardens,,", ",", &argv);      printf("argc = %d\n", argc);     (int = 0; <= argc; i++)         printf("%p = [%s]\n", (void *)argv[i], (argv[i] != 0) ? argv[i] : "<null>");      free_tokens(argv);      return 0; } 

it has second test, more stringent "hello,world" test, passes clean bill of health valgrind.

note none of code secure against string more 9 tokens in it, or token longer 29 characters. fixing requires more care handling lengths.

it possible duplicate source string , split null bytes @ appropriate points. simplify aspects of code (there'd 2 calls free() in code free tokens, example). it's safe; won't ever need more space that. can't have more tokens (strlen(str) + 1)/2, conservative (possibly wasteful) way allocate array of pointers allocate (strlen(str) + 3)/2 pointers front. might win waste in array of pointers allocating 1 chunk of memory hold tokens instead of 1 chunk per token. can realloc() array of pointers correct size before leaving function if discrepancy big enough warrant it.


stress test

i've taken stress test , run against final code. took long ten million iterations, in part because printing scrolling (and printed out data after each invocation of parse_tokens() — in part because otherwise argc used capture return value unused otherwise, , default compiling options complain unused variables). however, cut iterations down 100,000 , redirected standard output file, , valgrind gave code clean bill of health (run time around 18s in vm).

==27741== memcheck, memory error detector ==27741== copyright (c) 2002-2012, , gnu gpl'd, julian seward et al. ==27741== using valgrind-3.8.1 , libvex; rerun -h copyright info ==27741== command: ./toks3 ==27741==  ==27741==  ==27741== heap summary: ==27741==     in use @ exit: 0 bytes in 0 blocks ==27741==   total heap usage: 1,300,007 allocs, 1,300,007 frees, 59,000,310 bytes allocated ==27741==  ==27741== heap blocks freed -- no leaks possible ==27741==  ==27741== counts of detected , suppressed errors, rerun with: -v ==27741== error summary: 0 errors 0 contexts (suppressed: 2 2)  real    0m18.166s user    0m14.772s sys 0m0.757s 

i put first fixed version, piping output file (so faster); still clean bill of health.

==27751== memcheck, memory error detector ==27751== copyright (c) 2002-2012, , gnu gpl'd, julian seward et al. ==27751== using valgrind-3.8.1 , libvex; rerun -h copyright info ==27751== command: ./toks2 ==27751==  ==27751==  ==27751== heap summary: ==27751==     in use @ exit: 0 bytes in 0 blocks ==27751==   total heap usage: 1,300,007 allocs, 1,300,007 frees, 59,000,310 bytes allocated ==27751==  ==27751== heap blocks freed -- no leaks possible ==27751==  ==27751== counts of detected , suppressed errors, rerun with: -v ==27751== error summary: 0 errors 0 contexts (suppressed: 2 2)  real    0m16.205s user    0m15.576s sys 0m0.527s 

the test code (in place of main() shown above) was:

static void print_args(int argc, char **argv) {     printf("argc = %d\n", argc);     (int = 0; <= argc; i++)     {         printf("%p = [%s]\n", (void *)argv[i],                (argv[i] != 0) ? argv[i] : "<null>");     } }  static void test(void) {     {         char **argv;         int argc= parse_tokens("", " ", &argv);          print_args(argc, argv);         free_tokens(argv);     }     {         char **argv;         int argc= parse_tokens("hello world", ",", &argv);         print_args(argc, argv);         free_tokens(argv);     }     {         char **argv;         int argc= parse_tokens("hola, como te va; bien?"," ,;",&argv);          print_args(argc, argv);         free_tokens(argv);     }     {         char **argv;         int argc= parse_tokens("/bin:/usr/bin:/u/jperez",":",&argv);         print_args(argc, argv);         free_tokens(argv);      } }  int main(void) {     char **argv;      int argc = parse_tokens("hello,world", ",", &argv);     print_args(argc, argv);     free_tokens(argv);      argc = parse_tokens(",abc,,axolotl,,zoological society gardens,,", ",", &argv);     print_args(argc, argv);     free_tokens(argv);      (int = 0; < 100000; i++)           test();      return 0; } 

Comments

Popular posts from this blog

PHPMotion implementation - URL based videos (Hosted on separate location) -

javascript - Using Windows Media Player as video fallback for video tag -

c# - Unity IoC Lifetime per HttpRequest for UserStore -