#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2013
    Posts
    109
    Rep Power
    2

    Strdup() function isn't allocating memory ??


    I pass an inputted line to a function. The function uses STRTOK to collect the words and put them into *new_words[]. Then it takes all the DIFFERENT words and puts them in *new_list[].

    The first word passes, but after strtok() collects the second word and strdup() puts it in new_words[], the word becomes NULL.

    I use strdup (which is like malloc()+strcpy() ).

    Code:
    const char PUNCTS[] = " ,;?:.!\"/+-}{)([]*&%#$@\\=-|";
    char *new_words[100];
    char *text;
    char *new_list[100];
    
    
    int function1(char *pointer)
    {
        int j=0, k, x=0;
    
    
        char *ptr=malloc(sizeof(char)*20);
    
    
        ptr=strtok(pointer, PUNCTS);
    
    
        new_words[j]=strdup(ptr);
    
    
        new_list[x]=strdup(new_words[j]);
    
    
        
     
        while(ptr!=NULL)
        {
            ptr=strtok(NULL, PUNCTS); <----After this ptr is an actual word i enetered.
    
    
            new_words[++j]=strdup(ptr); <----After this new_words[j]=<NULL> !   (meaning in j that's been incremented so it's the new j)
    
             ......
    ......
    .......
                 
           
            
        }
        return j;
    }

    So if i wrote "Hello world" the printed output would be "Hello" <NULL>

    According to the syntax of strdup the function is supposed to work ?

    I get no warnings at all and no crashing this time.
  2. #2
  3. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    Too many possibilities, so I'll offer some suggestions that might make the problem more apparent.

    When there are no more tokens, strtok returns NULL. In the while loop, you call strtok for subsequent tokens (ie, by passing NULL to strtok, which is correct). But then regardless of the outcome of that call, you copy that token to new_words. That happens even when strtok returns NULL for "no more tokens". Is that what you want to do? Normally, before processing input (eg, token from strtok, data from fread or fscanf or fgets) you test whether that input operation succeeded; in this case that would be with an if (ptr != NULL). Of course, you might also have planned it this way in order to mark the end of the token list with a NULL, but I have no way to know that. Whenever you do something that might be considered out of the ordinary (eg, a for statement ending in a semicolon so that it has no body), always be sure to comment that, yes, you did intend to do that; otherwise, other programmers will try to "correct" your "mistake", including yourself when you return to that code six months later.

    Originally Posted by Eagleson's law
    Any code of your own that you haven't looked at for six or more months might as well have been written by someone else.
    Next, there's a better way to handle j. I normally call such a variable count because I use it both to index to the new element of the array and to maintain a count of how many elements are in the array. Here's how I use it:
    1. I initialize it to zero (0). That way, it reflects that there are no elements in the array and it indexes to new_words[0], which is where the first element will go.

    2. Whenever I assign a new element to the array, I post-increment count; eg,
    new_words[count++]=strdup(ptr);
    For the first element, it goes in new_words[0] and count becomes 1, since we have one element.
    For the seventh element, count was 6 so it goes into new_words[6] and count is then incremented to 7, since we now have seven elements.

    I offer that approach to handling j because your current use of it appears a bit confusing, which makes it a candidate for being the source of your problem.

    I read the man page for strdup and couldn't find anything about how it handles having NULL passed to it. The libc help file (downloaded it when I downloaded Dev-C++ from Bloodshed) also doesn't say specifically, but does say, "If malloc cannot allocate space for the new string, strdup returns a null pointer." I would have to make an educated guess that if you pass a NULL to strdup then it doesn't do a malloc but rather returns NULL.

    I would agree just from looking at it, though, that strtok should have returned "world" to you. A short compilable version of the function that exhibits the problem would be helpful.
  4. #3
  5. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    Don't malloc to ptr. That's a memory leak.

    I get crashing. Put together a short compilable program that still exhibits the problem and post it.
  6. #4
  7. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    The crashing was because I was passing a string literal to the function. strtok modifies the string that you pass to it.

    My test program incorporating your code:
    Code:
    #include <stdio.h>
    #include <string.h>
    //#include <malloc.h>
    
    const char PUNCTS[] = " ,;?:.!\"/+-}{)([]*&%#$@\\=-|";
    char *new_words[100];
    char *text;
    char *new_list[100];
    
    
    int function1(char *pointer)
    {
        int j=0, k, x=0;
        char *ptr; //=malloc(sizeof(char)*20);
    
        ptr=strtok(pointer, PUNCTS);
        new_words[j]=strdup(ptr);
        new_list[x]=strdup(new_words[j]);
     
        while(ptr!=NULL)
        {
            ptr=strtok(NULL, PUNCTS); //<----After this ptr is an actual word i enetered.
    
            new_words[++j]=strdup(ptr); //<----After this new_words[j]=<NULL> !   (meaning in j that's been incremented so it's the new j)
        }
        return j;
    }
    
    int main()
    {
        int i, n;
        char str[] = "Hello world";
        
        n = function1(str);
    
        printf("There are %d tokens.\n", n);
        for (i=0; i<n; i++)
        {
            printf(" Word %d = ", i);
            if (new_words[i] == NULL)
                printf("<NULL>\n");
            else
                printf("%s\n", new_words[i]);
        }
    
        return 0;
    }
    Output:
    C:TEST>a
    There are 2 tokens.
    Word 0 = Hello
    Word 1 = world

    C:TEST>
    I cannot duplicate your problem.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2013
    Posts
    109
    Rep Power
    2
    The code that you rewrote works really well, thanks a lot, i'll play with it. I'm thinking maybe the issue is somelace else.

    >>I shouldn't alloc to ptr- is it because strtok takes care of that ?
  10. #6
  11. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    Well, yeah. ptr is a char* and strtok returns a char*. If you malloc to ptr before calling strtok, then after the function call ptr no longer points to what you had malloc'd. If nothing else is pointing to that malloc'd memory, then you've "dropped that pointer". A dropped pointer cannot ever be accessed again, which means that it cannot ever be free'd. That is what causes a memory leak.

    There is only a finite amount of memory in the heap, which is where you malloc memory from. Every time you malloc, you diminish the amount of free heap, and when you free something that restores that amount of free heap, thus allowing the free'd memory to be reused in a future malloc. If you keep malloc'ing and never free anything, then you will eventually use up all the heap and the next call to malloc will cause a run-time error that will lead to the program crashing. This will usually manifest itself in a critical program that runs all the time (eg, power station operation, hospital life-support equipment, air-traffic control) suddenly and "inexplicably" crashing.

    Memory leaks are a major cause for such crashes and are notoriously difficult to find and correct. Since C++ can frequently create and delete temporary copies of objects all transparently to the programmer, C++ is also much more susceptible to memory leaks. That is why it is extremely important very early on in your training to recognize possible memory leaks and to learn and constantly practice the proper handling and freeing of pointers, ensuring that you never ever drop one. A hospital patient may one day owe his life to you doing your job right.

    PS
    I vaguely remember either a short story or something like a Twilight Zone episode where a ruthless businessman was producing medical equipment that he knew to be substandard and prone to failure with fatal results. In the conclusion of the story, he was in the hospital with a life-and-death condition and he saw that his own shoddy device was about to be used on him.

    So then you might be that hospital patient who would owe his life to your having maintained the discipline of guarding against dropping a pointer. Program like your life depends on your doing it right, because it just might one day.

    Comments on this post

    • requinix agrees : 200k
    Last edited by dwise1_aol; August 6th, 2013 at 02:07 PM.
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2013
    Posts
    109
    Rep Power
    2
    Yeah, pointers are quite tricky. :cool:

    >>I wish i knew the hardware system that deep, then i wouldn't have to doubt. But these lines certainly added to my understading.

    BTW, my print gives me an additional word when i print my new_list. The word is: <NULL>

    EVEN THOUGH my while loop says: while(ptr!=NULL).

    How can NULL pass thru that condition of ptr!=NULL ?
  14. #8
  15. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    Originally Posted by C learner
    BTW, my print gives me an additional word when i print my new_list. The word is: <NULL>

    EVEN THOUGH my while loop says: while(ptr!=NULL).

    How can NULL pass thru that condition of ptr!=NULL ?
    It doesn't pass through that condition.

    Assuming that your code is still what you posted in Msg#1, from my Msg#2:
    Originally Posted by DWise1
    But then regardless of the outcome of that [strtok] call, you copy that token to new_words. That happens even when strtok returns NULL for "no more tokens". Is that what you want to do?
    So when strtok returns NULL, you go ahead and add it to the list and you increment j.

    Is that what you want it to do? Or not? Either way, that is what it is doing.

    BTW, in your code, j is also one less than the total number of entries in new_words, the third entry being NULL.
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2013
    Posts
    109
    Rep Power
    2
    Oh, wait- in my book they have an example where they put strtok() AT THE END of the while loop.

    I gues that's why they have no problems with null :eh: how embarassing

    Thanks a lot for the help. I actually save these posts so i can re-read what you explain to me.
  18. #10
  19. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    When you do file I/O, you will have a very similar situation. You detect hitting the end of file by calling feof(). The problem is that feof() doesn't detect end of file until you have tried to do a read and failed because of hitting the end of file. So whenever you write a loop to read data, you need to test after each read whether it succeeded before you try to process it. One way is to do an intitial read before the loop, enter the loop and process the data from that initial read, then read the next block of data at the bottom of the loop and return to the top of the loop to test and process it. Or else have an extra if to test within the loop.

    What it boils down to is that you need to learn how to walk through your code and understand what's happening at every line. Which comes with practice and experience.
  20. #11
  21. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2013
    Posts
    109
    Rep Power
    2
    Yeah, i actually remember your answer on one of my posts, where you showed me the debugging at each line, showing the value and everything that's associated with every specific line.

    I've started writing more serious programs just recently, with structs, alloc etc', and i find this method of debugging more useful than clicking "debug" in the compiler-- it just gives me a message i can't really understand. Maybe i will understand when i study hardware.

IMN logo majestic logo threadwatch logo seochat tools logo