Excess buffer using fgets

This is a discussion on Excess buffer using fgets within the C Programming forums, part of the General Programming Boards category; Hi to all... I checked the FAQ and some tutorials over the net regarding not use scanf or gets just ...

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    115

    Excess buffer using fgets

    Hi to all... I checked the FAQ and some tutorials over the net regarding not use
    scanf or gets just use fgets to control buffer. I created this code for a test but it seems
    like the excess buffer is not removed but the other fgets received it and it won't allow
    me to input another value.

    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    
    int main()
    {
        char name[ 22 ];
        char age[ 4 ];
        int length;
        char *newlineName;
        char *newlineAge;
    
        fputs( "Enter name: (20 maximum characters) ", stdout );
    
        if( fgets( name, 22, stdin ) )
        {
            newlineName = strchr( name, '\n' );
            if( newlineName )
              *newlineName = '\0';
        }
    
        fputs( "Enter age: ", stdout );
        if( fgets( age, 2, stdin ) )
        {
            newlineAge = strchr( age, '\n' );
            if( newlineAge )
              *newlineAge = '\0';
        }
    
        fputs( "Your name is: " , stdout );
        fputs( name, stdout );
    
        fputs( "\nYour age is: ", stdout );
        fputs( age, stdout );
    
        return 0;
    }
    I can't think of any resolution to this problem. Thanks for the help in advance..

  2. #2
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,699
    Works fine for me:

    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    char* get( char* buffer, size_t size, FILE* in )
    {	
    	char *newline, *result = fgets( buffer, size, in );
    	if( result )
    	{
    		newline = strchr( buffer, '\n' );
    		if( newline )
    			*newline = 0;
    	}
    	return result;
    }
    
    int main()
    {
        const int size = 22;	
        char name[ size ];
        char age[ size ];
        int length;
        char *newlineName;
        char *newlineAge;
    
        fputs( "Enter name: (21 maximum characters) ", stdout );
        get( name, size, stdin );
        fputs( "Enter age: ", stdout );
        get( age, size, stdin );
        fprintf( stdout, "Your name is: %s\n" , name );
        fprintf( stdout, "Your age is: %s\n" , age );
        return 0;
    }
    Code:
    bool fun(bool value)
    {
        return std::pow(std::exp(1), std::complex<float>(0, 1) 
        * std::complex<float>(std::atan(1)*(1 << (value + 2))))
        .real() > 0;
    }

  3. #3
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,047
    I'm not sure what's the issue here but the second fgets() is a few characters short of the array size
    Code:
    char age[ 4 ];
    ...
    if( fgets( age, 2, stdin ) ) /* perhaps the 2 should really be a 4? */

  4. #4
    DESTINY BEN10's Avatar
    Join Date
    Jul 2008
    Location
    in front of my computer
    Posts
    804
    It worked properly except for it's not printing the age properly. I think what you're trying to do is hitting enter after the "Enter name: (20 maximum characters)" line.
    HOPE YOU UNDERSTAND.......

    By associating with wise people you will become wise yourself
    It's fine to celebrate success but it is more important to heed the lessons of failure
    We've got to put a lot of money into changing behavior


    PC specifications- 512MB RAM, Windows XP sp3, 2.79 GHz pentium D.
    IDE- Microsoft Visual Studio 2008 Express Edition

  5. #5
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,699
    >> I'm not sure what's the issue here but the second fgets() is a few characters short of the array size

    Right. fgets makes sure there is room for the null terminator, so it really isn't necessary to pass it a smaller size than the buffer.
    Code:
    bool fun(bool value)
    {
        return std::pow(std::exp(1), std::complex<float>(0, 1) 
        * std::complex<float>(std::atan(1)*(1 << (value + 2))))
        .real() > 0;
    }

  6. #6
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,047
    Quote Originally Posted by Sebastiani View Post
    Right. fgets makes sure there is room for the null terminator, so it really isn't necessary to pass it a smaller size than the buffer.
    Yep! in this case it'll read just one char and null-terminate age[], so it's biased towards kiddos

  7. #7
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,484
    > if( fgets( age, 2, stdin ) )
    Don't you mean 4 (the size of the array)?

    Since one of these two is reserved for the \0, that leaves just ONE character for your input AND a \n (that doesn't fit).
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  8. #8
    Registered User
    Join Date
    Oct 2008
    Posts
    115
    Quote Originally Posted by BEN10 View Post
    It worked properly except for it's not printing the age properly. I think what you're trying to do is hitting enter after the "Enter name: (20 maximum characters)" line.
    Yes.. I modified the code and applied sebastiani's code:


    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    char *getString( char *buffer, size_t size, FILE* input );
    
    int main()
    {
        const int size = 22;
    
        char name[ size ];
        char age[ size ];
    
        fputs( "Enter name: ", stdout );
        getString( name, size, stdin );
    
        fputs( "Enter age: ", stdout );
        getString( age, size, stdin );
    
        fprintf( stdout, "Name is: %s\n",name );
        fprintf( stdout, "Age  is: %s\n", age );
    
        return 0;
    }
    
    char *getString( char *buffer, size_t size, FILE *input )
    {
        char *newLine, *stringResult = fgets( buffer, size, input );
    
        if( stringResult )
        {
            newLine = strchr( stringResult, '\n' );
            if( newLine )
              *newLine = '\0';
        }
    
        return stringResult;
    }
    The size of the string is 22, if I entered more than 22 the age input will accept the excess buffer that was from the first input. So I can't input for the age at all and the age will print out the excess from the first input.

    here's the output of the program:
    Code:
    Enter name: Maria Theresa Acosta D
    Enter age: Name is: Maria Theresa Acosta
    Age  is: D
    
    Process returned 0 (0x0)   execution time : 42.703 s
    Press any key to continue.
    As you can see the 22nd character is D and it's directly retrieved by age...
    Last edited by $l4xklynx; 06-25-2009 at 12:44 AM. Reason: providing output

  9. #9
    Registered User
    Join Date
    Oct 2008
    Posts
    115
    Quote Originally Posted by Salem View Post
    > if( fgets( age, 2, stdin ) )
    Don't you mean 4 (the size of the array)?

    Since one of these two is reserved for the \0, that leaves just ONE character for your input AND a \n (that doesn't fit).
    Yes I'm sorry, I forgot to change the code to it's original state for I was playing with it earlier.

  10. #10
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,699
    >> The size of the string is 22, if I entered more than 22 the age input will accept the excess buffer that was from the first input.

    I see. Well, I don't know how portable this is, but you could try:

    Code:
    char *getString( char *buffer, size_t size, FILE *input )
    {
        char *newLine, *stringResult = fgets( buffer, size, input );
    
        if( stringResult )
        {
            newLine = strchr( stringResult, '\n' );
            if( newLine )
              *newLine = '\0';
        }
        fflush( input );
        return stringResult;
    }

    EDIT:

    OK, so it's actually not recommended (read: undefined behavior). So apparently you'll need an OS-specific solution...
    Last edited by Sebastiani; 06-25-2009 at 01:08 AM.
    Code:
    bool fun(bool value)
    {
        return std::pow(std::exp(1), std::complex<float>(0, 1) 
        * std::complex<float>(std::atan(1)*(1 << (value + 2))))
        .real() > 0;
    }

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,588
    Quote Originally Posted by Sebastiani
    I don't know how portable this is
    It is not portable because fflush() is not defined for input streams or update streams for which the last operation was a read operation. I suggest:
    Code:
    char *getString( char *buffer, size_t size, FILE *input )
    {
        int c;
    
        char *stringResult = fgets( buffer, size, input );
        if( stringResult )
        {
            char *newLine = strchr( stringResult, '\n' );
            if( newLine )
                *newLine = '\0';
        }
    
        while( ( c = fgetc( input ) ) != '\n' && c != EOF );
    
        return stringResult;
    }
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,699
    >> while( ( c = fgetc( input ) ) != '\n' && c != EOF );

    The problem with that, though, is that tries to read from the input even if there's none available.

    What about this:

    Code:
    void flush_input_stream( FILE* in )
    {
    	while( fseek( in, 1, SEEK_CUR ) == 0 )
    		continue;
    } 
    
    char *getString( char *buffer, size_t size, FILE *input )
    {
        char *newLine, *stringResult = fgets( buffer, size, input );
    
        if( stringResult )
        {
            newLine = strchr( stringResult, '\n' );
            if( newLine )
              *newLine = '\0';
        }
        flush_input_stream( input );
        return stringResult;
    }
    EDIT:
    OK, so what I've found is that while it is in fact portable (standard-compliant), it isn't necessarily implemented correctly on all systems. But it should work on most. Also, you could probably compress it into a single fseek by specifying SEEK_END (and 0 for the offset, of course).
    Last edited by Sebastiani; 06-25-2009 at 01:37 AM.
    Code:
    bool fun(bool value)
    {
        return std::pow(std::exp(1), std::complex<float>(0, 1) 
        * std::complex<float>(std::atan(1)*(1 << (value + 2))))
        .real() > 0;
    }

  13. #13
    DESTINY BEN10's Avatar
    Join Date
    Jul 2008
    Location
    in front of my computer
    Posts
    804
    I learned from a thread one thing that returning strings might result in a pointer which might be pointing to the string which is not present there. So, wouldn't this declaration be more safe than yours.
    Code:
    static char *stringResult = fgets( buffer, size, input );
    HOPE YOU UNDERSTAND.......

    By associating with wise people you will become wise yourself
    It's fine to celebrate success but it is more important to heed the lessons of failure
    We've got to put a lot of money into changing behavior


    PC specifications- 512MB RAM, Windows XP sp3, 2.79 GHz pentium D.
    IDE- Microsoft Visual Studio 2008 Express Edition

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    21,588
    Quote Originally Posted by Sebastiani
    The problem with that, though, is that tries to read from the input even if there's none available.
    I think that that would result in EOF being read. But I see what you mean: if this was keyboard input, there would be a problem, but we should be able to cater for keyboard input... I'll post later if this has not been addressed by the time I get back.

    Quote Originally Posted by BEN10
    I learned from a thread one thing that returning strings might result in a pointer which might be pointing to the string which is not present there. So, wouldn't this declaration be more safe than yours.
    Code:
    The function returns a pointer that was passed to it, so if the pointer points to something that exists in the caller, the caller will receive a pointer to something that exists. There is no problem.
    C + C++ Compiler: MinGW port of GCC
    Version Control System: Bazaar

    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,699
    >> I learned from a thread one thing that returning strings might result in a pointer which might be pointing to the string which is not present there. So, wouldn't this declaration be more safe than yours.

    It depends on the function, but fgets, specifically, is guaranteed to return a pointer to the buffer, unless an error occurs, in which case it returns NULL.
    Code:
    bool fun(bool value)
    {
        return std::pow(std::exp(1), std::complex<float>(0, 1) 
        * std::complex<float>(std::atan(1)*(1 << (value + 2))))
        .real() > 0;
    }

Page 1 of 2 12 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 10-29-2006, 04:04 AM
  2. fgets buffer
    By Pythonsnake in forum C Programming
    Replies: 8
    Last Post: 01-10-2006, 11:27 PM
  3. Confused about fgets, clearing the buffer, etc
    By caduardo21 in forum C Programming
    Replies: 1
    Last Post: 06-13-2005, 11:03 AM
  4. buffer contents swapping
    By daluu in forum C++ Programming
    Replies: 7
    Last Post: 10-14-2004, 02:34 PM
  5. Console Screen Buffer
    By GaPe in forum Windows Programming
    Replies: 0
    Last Post: 02-06-2003, 04:15 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21