Thread: fgets only returns 3 characters

  1. #46
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    1. Your test is for removing the \n (enter-key) at the end, not introducing a \0 character.
    2. It will make your string fit exactly in the array length you supplied -- an 80-character array can only hold 79 "good" characters in it, the 80th being \0; and that 80th character is at array[79], which is the whole "starting at 0" thing again.
    3. I didn't even notice the missing return, which means I'm a bad person -- but if your compiler didn't say anything either, that means you need to turn your warnings up. -Wall in gcc is a good start.

  2. #47
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    If you're trying to make a string type I find the approach you've taken a little weird.

    You should find newlines with strchr and place a zero there, using the pointer that it returns.

    I also find it a bit safer to just make a deep copy of the string upon creation rather than a simple pointer assignment. That way, the body of the string variable will always be available until you destroy the string, and isn't dependent on some other pointer. It would be quite a surprise if the other pointer freed the body of your string variable earlier than you wanted.

    Also, what if you make a string variable from a literal like "hello world"? A copy would be necessary then also.

    If I were doing this, here is what it would look like, since I'm bored. This compiles:

    Code:
    #include <stddef.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    struct strings_t
    {
        char * body;
        size_t length;
    };
    
    struct strings_t makeString (const char * cs)
    {
        struct strings_t str = {NULL, 0};
    
        if (cs == NULL)
            return str;
    
        str.length = strlen(cs);
        str.body = malloc(str.length + 1);
        str.body = (str.body == NULL) ? NULL : strcpy(str.body, cs);
        return str;
    }
    
    struct strings_t readLine (size_t linesize, FILE * stream)
    {
        struct strings_t buf = {NULL, 0};
    
        char * line = malloc(linesize + 1);
        if (line == NULL) 
            return buf;
        
        if (fgets(line, linesize, stream) != NULL) {
            char * nl = strchr(line, '\n');
            if (nl != NULL) 
                *nl = '\0';
    
            buf = makeString(line);
        }
    
        free(line); /** buf.body is a separate copy **/
        return buf;
    }
    
    void destroyString (struct strings_t old)
    {
        free(old.body);
    }

  3. #48
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by tabstop View Post
    1. Your test is for removing the \n (enter-key) at the end, not introducing a \0 character.
    I think it's both; it replaces any \n at the end with a \0 so the string length is determinable. To rephrase my question...

    You say that fgets will always terminate the string with \0. If it reads a \n, where would the \n be positioned in relation to the \0? And why do I care to replace that \n with a \0?

    2. Never mind; I simply read your post wrong.

    Quote Originally Posted by tabstop View Post
    3. I didn't even notice the missing return, which means I'm a bad person -- but if your compiler didn't say anything either, that means you need to turn your warnings up. -Wall in gcc is a good start.
    I've told NetBeans to convert warnings to errors (the highest level available).

    Quote Originally Posted by citizen View Post
    If you're trying to make a string type I find the approach you've taken a little weird.
    Heh, that's not surprising, seeing as I'm a newbie. But how do you mean?

    Quote Originally Posted by citizen View Post
    I also find it a bit safer to just make a deep copy of the string upon creation rather than a simple pointer assignment. That way, the body of the string variable will always be available until you destroy the string, and isn't dependent on some other pointer. It would be quite a surprise if the other pointer freed the body of your string variable earlier than you wanted.
    Is there any case where you would return a struct pointer, then?

    Quote Originally Posted by citizen View Post
    Also, what if you make a string variable from a literal like "hello world"? A copy would be necessary then also.
    ... Why?

    I'm going to pick apart your code to make sure I understand it.
    Code:
        str.body = malloc(str.length + 1);
        str.body = (str.body == NULL) ? NULL : strcpy(str.body, cs);
    Does this serve any purpose (see below)? If so, aren't you trashing the pointer and memory you just allocated (since you're not using a pointer)?

    Code:
        char * line = malloc(linesize + 1);
        if (line == NULL) 
            return buf;
    How does this if-statement help anything? If line is NULL, that's strange because the memory won't be deliberately initialized.

    Code:
    void destroyString (struct strings_t old)
    {
        free(old.body);
    }
    Doesn't this function prove that the string's body can be freed with a pointer to the string itself? If not, does it serve any real purpose?
    Last edited by Jesdisciple; 08-24-2008 at 06:37 PM.

  4. #49
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Jesdisciple View Post
    I think it's both; it replaces any \n at the end with a \0 so the string length is determinable. To rephrase my question...

    You say that fgets will always terminate the string with \0. If it reads a \n, where would the \n be positioned in relation to the \0? And why do I care to replace that \n with a \0?
    The \n would be before the \0. You would need to get rid of it because it doesn't belong. My name is "tabstop", not "tabstop\n" (unless I decide to become a pretentious artist a la e e cummings or Prince).

    Quote Originally Posted by Jesdisciple View Post
    I've told NetBeans to convert warnings to errors (the highest level available).
    That's irrelevant if you don't have warnings turned on in the first place. (I'm not familiar with NetBeans so I can't say more than that, but -Wall and -Werror aren't related.)

    I don't have originals any more (quoting always strips out old quotes), so I can't be sure whether this is your question here. But: there's a difference between
    Code:
    const char *str1 = "hello there";
    char str2[] = "hello there";
    str1 points to where "hello there" has been placed in memory -- and it is NOT writable. So any attempt to do something like str1[5] = 'b' will just instantly blow up. However, str2 sets aside memory (since it is an array, after all) and then copies the contents of "hello there" into that array. So you can do str2[5] = 'b' just fine, since you own that memory.

    And any time you malloc something, you should check whether what comes back is NULL. If it is, you have completely filled up your computer -- there's no room left for what you want to do. So you just have to do something that won't try to access that memory, and soldier on as best you can.

    Edit: Looking over this again, you seem to have attached no small amount of mysticism to pointers. They aren't magic: they are merely a variable that holds an address-in-memory of some other piece of data. So strcpy, for instance, takes a pointer (cs.body is a pointer, oh yes) to where we want a copy of this string to go. It returns that same pointer too, which is why that odd ?: trick works.
    Doesn't this function prove that the string's body can be freed with a pointer to the string itself? If not, does it serve any real purpose?
    All that is what makes this question so nonsensical. There are two different things going on here: a struct (which contains a length and a pointer) and the actual contents of the string (which is not stored in the same place -- the pointer says where it is). When the struct goes out of scope, it goes away. That's all well and good, BUT, the actual contents of the string, which aren't part of the struct itself, must be taken care of. We requested that piece of memory using malloc; when we are done with it, we must give it back to the operating system by using free().
    Last edited by tabstop; 08-24-2008 at 06:50 PM.

  5. #50
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    tabstop has got most of your questions covered.

    Originally Posted by citizen View Post
    I also find it a bit safer to just make a deep copy of the string upon creation rather than a simple pointer assignment. That way, the body of the string variable will always be available until you destroy the string, and isn't dependent on some other pointer. It would be quite a surprise if the other pointer freed the body of your string variable earlier than you wanted.

    Is there any case where you would return a struct pointer, then?
    Well, what you asked and what I was talking about were two different things -- but I'll answer your question first. It's certainly alright to return struct pointers from functions, but I chose not to for aesthetic reasons. Wouldn't look much different either way. There is simply an extra step involved: calling malloc to request the space for a new string_t in makeString, and freeing the string_t itself in destroyString. Currently, makeString just initializes a new string_t kept in automatic storage and returns that.

    But what shouldn't change about makeString is how body is allocated. That is what I was talking about. You might want to reread that part of my post, but I'll explain it with a picture.

    This is the scenario you create when you assign two pointers to the same memory:

    Attachment 8338

    Say the pointer cs frees "hello world" and then goes out of scope. Well, body (part of your string) is still in scope, but its pointing where the string used to be. So what body points to is undefined - maybe even "hello world", which doesn't look wrong, but it is. Your string is supposed to be in sole command of its memory -- this is the concept of ownership -- and yet here it is not.

    And tabstop is right about pointers to literals too. Listen to him on that. I also recommend "Binky" to remove some of that pointer mysticism he talked about.
    Last edited by whiteflags; 08-24-2008 at 08:47 PM.

  6. #51
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    LOL. If Binky hadn't exploded when he tried to dereference y, that video would have been lame.

  7. #52
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by tabstop View Post
    The \n would be before the \0. You would need to get rid of it because it doesn't belong. My name is "tabstop", not "tabstop\n" (unless I decide to become a pretentious artist a la e e cummings or Prince).
    But I'm planning to output the lines I'm reading to another file. Is it poor practice to leave the newline in its place since I have no problem with it?

    Quote Originally Posted by tabstop View Post
    That's irrelevant if you don't have warnings turned on in the first place. (I'm not familiar with NetBeans so I can't say more than that, but -Wall and -Werror aren't related.)
    Project Properties > C/C++ > C Compiler > Command Line > Additional Options

    Quote Originally Posted by tabstop View Post
    I don't have originals any more (quoting always strips out old quotes), so I can't be sure whether this is your question here. But: there's a difference between
    Code:
    const char *str1 = "hello there";
    char str2[] = "hello there";
    str1 points to where "hello there" has been placed in memory -- and it is NOT writable. So any attempt to do something like str1[5] = 'b' will just instantly blow up. However, str2 sets aside memory (since it is an array, after all) and then copies the contents of "hello there" into that array. So you can do str2[5] = 'b' just fine, since you own that memory.
    How does that tie into copying or not copying a string object? (See the "View Post" link for the quote's context - down below on the reply page if necessary.)
    Quote Originally Posted by citizen View Post
    Also, what if you make a string variable from a literal like "hello world"? A copy would be necessary then also.
    Quote Originally Posted by tabstop View Post
    Edit: Looking over this again, you seem to have attached no small amount of mysticism to pointers. They aren't magic: they are merely a variable that holds an address-in-memory of some other piece of data. So strcpy, for instance, takes a pointer (cs.body is a pointer, oh yes) to where we want a copy of this string to go. It returns that same pointer too, which is why that odd ?: trick works.
    I don't think I'm having trouble with pointers so much as with NULL in and of itself. I thought you were dealing with the actual value rather than the pointer. It looks like I need to unlearn the use of the null value in my other languages.

    Here is my own flowchart to illustrate my remaining question. (BTW, how did you include your image directly into that post?) str1 and str2 both point to the same "blah." After str2 has been freed, dereferencing str1 is a no-no. In destroyString, you free a char pointer that surely has aliases floating around on the stack because you passed its owner by value rather than by reference.
    Last edited by Jesdisciple; 08-26-2008 at 09:08 PM.

  8. #53
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    In destroyString, you free a char pointer that surely has aliases floating around on the stack because you passed its owner by value rather than by reference.
    That is precisely the behavior we want though. Passing the structure by value makes no difference here because in C all parameters, even pointer parameters, are passed by value. deleteString works because it will free its parameter's body. This indirectly affects the body pointer in the variable from the calling code. (Think of your picture.) The only gotcha is that

    deleteString(str);
    str.body = NULL;

    would be safest. Especially if str were not due to fall out of scope and might be used again.

    One could say that destroyString is an abstraction of the free function, and you wouldn't free until any aliases were already obsolete.

    Also, your interpretation is not incorrect. For the future, click "Go advanced" and select your attachment from the drop-down list to include it anywhere -- but if you don't it will be appended to your post.

    How does that tie into copying or not copying a string object?
    This goes back to why body shouldn't be assigned makeString's argument.

    Say you make the call

    struct strings_t s = makeString("blah");

    "blah" is a string literal. String literals are stored in read only, anonymous memory (getting the address of a literal is not valid and will not compile). However, your code is very "brittle" if you cannot make an editable string variable from the literal sequence or an alias to a literal sequence. So store a copy in separate storage.
    Last edited by whiteflags; 08-25-2008 at 12:00 AM.

  9. #54
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by citizen View Post
    That is precisely the behavior we want though. Passing the structure by value makes no difference here because in C all parameters, even pointer parameters, are passed by value. deleteString works because it will free its parameter's body. This indirectly affects the body pointer in the variable from the calling code. (Think of your picture.)
    But isn't that the behavior you tried to avoid in the first place? Because the other pointer may be freed before you call deleteString.

    Quote Originally Posted by citizen View Post
    The only gotcha is that

    deleteString(str);
    str.body = NULL;

    would be safest. Especially if str were not due to fall out of scope and might be used again.
    And to incorporate that into deleteString, wouldn't we need to pass the string object by reference (&str), or just make it a pointer in the first place?

    Quote Originally Posted by citizen View Post
    Also, your interpretation is not incorrect. For the future, click "Go advanced" and select your attachment from the drop-down list to include it anywhere -- but if you don't it will be appended to your post.
    What dropdown list?

    I'm also curious why you chose strings_t for the name... Is string_t taken by a standard library? Why not use it instead of a custom class?

  10. #55
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    Quote Originally Posted by Jesdisciple
    What dropdown list?
    Attachment 8344

    I'll let citizen answer the rest of your questions, since they deal with his coding advice.

  11. #56
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Oh, thanks!

    I reiterate this to everyone, not just tabstop:
    But I'm planning to output the lines I'm reading to another file. Is it poor practice to leave the newline in its place since I have no problem with it?
    And of course I would still like answers to my previous post.

    My feof test only reads the first line... Why? (Note that I encountered this problem before the 2 below, so it may not be relevant anymore.)

    Also, I'm getting a new segfault, but gdb isn't helping much. It was coming after I returned 0, but I've modified the code directly after the erroneous section since then; now it backs up before dying. EDIT2: See below.
    Code:
    79	    /* Trim the blank lines. */
    (gdb) 
    80	    struct charr *last;
    (gdb) 
    81	    short done = 0;
    (gdb) 
    82	    while(!done){
    (gdb) 
    81	    short done = 0;
    (gdb) 
    78	    
    (gdb) 
    79	    /* Trim the blank lines. */
    (gdb) 
    
    Program received signal SIGSEGV, Segmentation fault.
    0x08048f6e in main () at main.c:79
    79	    /* Trim the blank lines. */
    (gdb)
    EDIT2: I got past that bug by removing a null dereference, but now I can never find EOF and it just keeps reading garbage in from the start.

    EDIT1: BTW, I'm still applying the changes prescribed by citizen. I was just debugging it before doing so.
    Last edited by Jesdisciple; 08-31-2008 at 01:38 PM.

  12. #57
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Code:
    &result[sizeof(result) - 1];
    sizeof (result) is 4 or 8 - because it is a pointer
    what you want here is a strlen

    Code:
    while(!feof(file))
    read FAQ why you should not use feof to control loops

    you have too much code in header files. Headers are only for prototypes and declarations, function definitions go to c-files

    you do not check the return values of the function you call

    make a smallest compilable sample representing the carash and post it
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  13. #58
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by vart View Post
    Code:
    while(!feof(file))
    read FAQ why you should not use feof to control loops
    OK... Now I wonder what feof is for?

    Quote Originally Posted by vart View Post
    you have too much code in header files. Headers are only for prototypes and declarations, function definitions go to c-files
    Do they get included the same? I've read some about linking files after compiling them; is that necessary?

    Quote Originally Posted by vart View Post
    you do not check the return values of the function you call
    ? What function(s) are you talking about?

    Quote Originally Posted by vart View Post
    make a smallest compilable sample representing the carash and post it
    My smallest will probably be a whale, but I'll give it a shot. EDIT: I don't know if that crash is still relevant, but what would cause fgets to return garbage?
    Last edited by Jesdisciple; 08-26-2008 at 10:58 PM.

  14. #59
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I'm also curious why you chose strings_t for the name... Is string_t taken by a standard library?
    strings_t is not a reserved keyword, and the only standard library for strings is string.h, which operates on char arrays. So, why aren't you using the standard library, yourself?

    My problem with what you had posted in the first place was basically how strings were created here:

    Code:
    struct charr *charr(char *string){
        struct charr *this = malloc(sizeof(struct charr));
        this->pointer = string; 
        this->length = strlen(string);
        return this;
    }
    As I've stated several times in so many words, this assignment is not safe. Your pointer variable is only valid as long as the thing it points to is. That means the pointer is an alias of string, and even if string is valid for the entire lifetime of the string variable you return, you still have problems. String literals are read only, and you cannot really tell if/when pointer should be freed.

    So to circumvent creating these problems it is much less problematic to simply allocate memory and copy your string. You know that the string is editable no matter what, and you know precisely when it needs to be freed.

    Your next comment about deleteString is really a comment about my style and I've reached the conclusion that style is indefensible for anyone long ago. Using structure pointers is not difficult or even a bad idea. But, to me, giving destroyString the responsibility of setting it's parameter to NULL is awkward. For instance, even if you decide to use structure pointers all the time in your implementation, you would still need extra indirection:

    Code:
    struct strings_t * makeString (const char * s);
    void destroyString (struct strings_t ** old);
    struct strings_t * readLine (size_t count, FILE * stream);
    While this isn't bad, following convention can be a good thing. free doesn't require a pointer to the pointer to be deleted. Since you're not even in the habit, why bother? I find a macro to be the simplest way if you bother with it at all.

    Code:
    #define DELETESTRING(p) do { deleteString((p)); (p).body = NULL; } while(0)
    
    DELETESTRING(str);
    Last edited by whiteflags; 08-26-2008 at 11:09 PM.

  15. #60
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by Jesdisciple View Post
    OK... Now I wonder what feof is for?
    if the last read operation failed - you maybe interested to know - it is due to the end of file - or some read error - only in this case it is suitable to use feof

    Quote Originally Posted by Jesdisciple View Post
    Do they get included the same? I've read some about linking files after compiling them; is that necessary?
    yes

    Quote Originally Posted by Jesdisciple View Post
    What function(s) are you talking about?
    all functions you have placed in the header files

    Quote Originally Posted by Jesdisciple View Post
    ... but what would cause fgets to return garbage?
    have you checked the return value of fgets? Probably you are using the value left from the previos call
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Lame null append cause buffer to crash
    By cmoo in forum C Programming
    Replies: 8
    Last Post: 12-29-2008, 03:27 AM
  2. HELP!!!!emergency Problem~expert please help
    By unknowppl in forum C++ Programming
    Replies: 9
    Last Post: 08-21-2008, 06:41 PM
  3. HELP!!!!emergency ~expert please help
    By unknowppl in forum C Programming
    Replies: 1
    Last Post: 08-19-2008, 07:35 AM
  4. gets vs fgets
    By strobo in forum C Programming
    Replies: 10
    Last Post: 03-27-2002, 05:28 PM