Thread: fgets() array size memory concern

  1. #1
    Registered User
    Join Date
    Jul 2004
    Posts
    30

    fgets() array size memory concern

    If I'm using fgets() for console input, yet I don't know how long the input will be, what would be the most appropriate value for the second parameter? Also, would specifying a very large buffer size as the second parameter cause memory problems?

    Is there a better method for accepting keyboard input of unknown and potentially large size?

  2. #2
    & the hat of GPL slaying Thantos's Avatar
    Join Date
    Sep 2001
    Posts
    5,681
    The best way is to specify the size of your buffer.
    Ie:
    Code:
    char buffer[50];
    fgets(buffer, sizeof buffer, stdin);
    The second parameter is how much room you have, not how much you expect to get.

  3. #3
    Registered User
    Join Date
    Jul 2004
    Posts
    30
    OK, thanks!

  4. #4
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> Is there a better method for accepting keyboard input of unknown and potentially large size?

    you can use a static buffer to read input:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    /* 
        function reads a line from 'stream' and 
        returns a pointer to a static buffer.
        if 'stream' is NULL, returns contents of
        last read operation.
        if buffer can't grow any further, 
        a partial line is returned.
    */ 
    const char * line(FILE * stream)
    {
     static char * ig_buffer = NULL;
     static int ig_buffer_size = 0;
     const int block_size = 32;
     char * temp;
     int ch, read = 0;
     if(NULL == stream) {
      return ig_buffer;
      }
     if(ig_buffer = NULL) {
      ig_buffer = malloc(1);
      ig_buffer[0] = 0; 
      }  
     if(feof(stream)) {
      return NULL;
      }
     while(EOF != (ch = fgetc(stream)) && ch != '\n') {  
      if(read == ig_buffer_size) {
       temp = realloc(ig_buffer, ig_buffer_size + block_size + 1);
       if(NULL == temp) {
        break;
        }
       ig_buffer = temp;
       ig_buffer_size += block_size;
       }
      ig_buffer[read++] = ch; 
      }
     ig_buffer[read] = 0; 
     return ig_buffer; 
    }
    
    
    int main(int, char ** argv)
    {
     while(*(++argv)) {
     FILE * fptr = fopen(*argv, "r");
     if(fptr) {
      while(line(fptr)) {
       puts(line(NULL));
       }
      fclose(fptr);
      }  
     }
     return 0;
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  5. #5
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    I think you need to delete a few more Sebastiani, it still isn't right.

    Plus it's a horrible memory leak

    Your initial call to malloc is a hack - there is no need for it.
    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.

  6. #6
    Registered User
    Join Date
    Jul 2004
    Posts
    101
    >>Is there a better method for accepting keyboard input of unknown and potentially large size?
    You have two choices. Create an array sized by your best guess of the largest input and handle a full buffer gracefully, or dynamically resize a buffer as the input is received. For the former, BUFSIZ is generally a good all around choice for the array size. To handle a full buffer, simply test the last valid character in the string against '\n'. Don't forget to account for the null character as well. Otherwise your test will give incorrect results.

    The latter option is more flexible, but less efficient due to calls to the memory manager. The low level string manipulation is also error prone. Here is one way to go about it.
    Code:
    /* Build a string dynamically, reading character by character */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *readline(FILE *in);
    
    int main()
    {
      char *p;
    
      p = readline(stdin);
      if (p != NULL) {
        printf("\"%s\" is %lu characters long\n", p, (unsigned long int)strlen(p));
      }
      free(p);
    
      return 0;
    }
    
    char *readline(FILE *in)
    {
      int ch;
      char *p = NULL;
      char *save;
      int n = 0;
      int max = 0;
    
      while ((ch = fgetc(in)) != EOF) {
        if (n >= max) {
          max += 2;
          save = realloc(p, max + 1);
          if (save == NULL) {
            free(p);
            return NULL;
          }
          p = save;
        }
        if (ch == '\n') {
          break;
        }
        p[n] = (char)ch;
        ++n;
      }
      if (p != NULL) {
        p[n] = '\0';
        save = realloc(p, n + 1);
        if (save != NULL) {
          p = save;
        }
      }
    
      return p;
    }
    You might also try using fgets within readline to use a block read technique, but that method is more involved than it first appears. Reading a single character at a time is easier to get right.

  7. #7
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> I think you need to delete a few more Sebastiani, it still isn't right.

    *sigh* - you're right you know, but I was in a rush. but it was eating at me all day. =)

    >> Plus it's a horrible memory leak

    no kidding. I honestly thought realloc took care of that. thank god for documentation.

    >> Your initial call to malloc is a hack - there is no need for it.

    it wasn't for realloc - it was the fact that the line 'ig_buffer[read] = 0' would have wreaked havoc if the buffer was still NULL (ie: the stream immediately encountered a newline or EOF while the buffer was still unallocated).

    another obvious problem with my code (did I really just say that?) is that if the user say, invokes the function on an eof'ed stream and then calls the function with a NULL parameter, it returns a good pointer when it should be NULL (to reflect the previous failure). the solution was simply to use separate pointers for the buffer and the return result:

    Code:
    const char * line(FILE * stream)
    {
     static char * ig_ret = NULL, * ig_buffer = malloc(1);
     static int ig_buffer_size = 0;
     const int ig_block_size = 32;
     char * temp;
     int ch, read = 0;
     if(stream == NULL) {
      return ig_ret;
      }
     if(feof(stream)) {
      return ig_ret = NULL;
      }
     while(EOF != (ch = fgetc(stream)) && ch != '\n') {
      if(read == ig_buffer_size) {
       temp = realloc(ig_buffer, ig_buffer_size + ig_block_size + 1);
       if(NULL == temp) {
        break;
        }
       free(ig_buffer); 
       ig_buffer = temp;
       ig_buffer_size += ig_block_size;
       }
      ig_buffer[read++] = ch; 
      }
     ig_buffer[read] = 0; 
     return ig_ret = ig_buffer; 
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Adventures in labyrinth generation.
    By guesst in forum Game Programming
    Replies: 8
    Last Post: 10-12-2008, 01:30 PM
  2. memory leaks
    By TehOne in forum C Programming
    Replies: 4
    Last Post: 10-10-2008, 09:33 PM
  3. Replies: 16
    Last Post: 11-23-2007, 01:48 PM
  4. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  5. Hi, could someone help me with arrays?
    By goodn in forum C Programming
    Replies: 20
    Last Post: 10-18-2001, 09:48 AM