Home > Enterprise >  How can realloc be used effectively in loops?
How can realloc be used effectively in loops?

Time:01-07

I'm new to c so sorry if the question might seem basic. I'm trying to read a string with unknown size from the user and store it in a dynamic array. This program is going to make accounts for users but I can't successfully scan the username or the password. Please note that no arrays with fixed size can be used. Any solutions using files can be used and also be really helpful.

void main(){
    user *dummyUser = (user *) malloc(sizeof(user));
    dummyUser->next=NULL; 
    char* password=(char* )malloc(sizeof(char));
    char* name=(char* )malloc(sizeof(char));
    int i=0;
    char c;
    char ptr;
    while((c=getchar())!=' '){
        name[i]=c;
        ptr=(char *)realloc(name,sizeof(char));
        if(ptr==NULL){
            printf("realloc failed");
            exit( EXIT_FAILURE );
        }
        i  ;
    }
    i=0;
    while((c=getchar())!='\n'){
        password[i]=c;
        password=(char *)realloc(password,sizeof(char));
        i  ;
    }
}

realloc seem to work for 1 time, but the next times it returns a null pointer and makes the program crash. note: I know I have to check if realloc returns a null pointer, I checked it in my watchlist so I didn't write the related code.Thanks for helping.

CodePudding user response:

This isn't doing what you want:

ptr=(char *)realloc(name,sizeof(char));

The second argument of realloc specifies the total number of bytes you want allocated, not just how many more bytes to allocate. You're not extending the array by 1 each time, you're just reallocating the same 1-byte space (sizeof (char) is 1 by definition).

Remember that to store an N-character string, you need to allocate at least N 1 bytes to account for the string terminator.

The typical way to do this is to double the size of the buffer each time - you'll need to keep track of the total buffer size as well as the number of bytes used, like so:

#include <ctype.h> // for the isspace call below:
...

size_t size = 2; // 2 bytes can store a 1-character string
char *name = malloc( size ); // no need for a cast
size_t i = 0;
int c;
...

/**
 * You'll probably want to break on EOF and *any* whitespace
 * character
 */
while( (c = getchar()) != EOF && !isspace( c ) )
{
  if ( i == size )
  {
    /**
     * Double the size of the buffer; again, no need for a cast
     *
     * Since realloc will return NULL if it can't extend the
     * buffer, we want to assign the result to a temporary;
     * otherwise we risk overwriting the pointer value in name
     * with a NULL pointer and losing our access to the memory
     * that's been previously allocated.
     */
    char *tmp = realloc( name, size * 2 ); 
    if ( tmp )
    {
      name = tmp;
      size *= 2;
    }
    else
    {
      /**
       * We were unable to extend the buffer; you can treat this
       * as a fatal error and exit immediately, or you can
       * try to proceed with the data you have.  The previous
       * buffer is still allocated at this point.  
       *
       * For this particular program, you'd probably just want
       * to exit with an error message at this point, like so:
       */
      fputs( "Failed to extend name buffer, exiting...", stderr );
      free( name ); // clean up what we've already allocated
      exit( 0 );
    } 
  }
  name[i  ] = c;
}
name[i] = 0; // terminate the string

For convenience's sake, you'll want to move the buffer extension into its own function:

char *extend( char **buf, size_t *size )
{
  char *tmp = realloc( *buf, *size * 2 );
  if ( tmp )
  {
    *buf = tmp;
    *size *= 2;
  }
  return tmp;
}

and you'd call it as

while( (c = getchar()) != EOF && !isspace( c ) )
{
  if ( i == size )
  {
    if ( !extend( &name, &size ) )
    {
      // handle realloc failure
    }
  }
  name[i  ] = c;
}
name[i] = 0; // terminate the string

CodePudding user response:

ptr=(char *)realloc(name,sizeof(char));

Please note that this is allocating a single byte. You have done this several times. A C string is an array of characters with a null terminator ('\0') byte at the end. An array that can only hold one byte can only ever hold an empty C string, or lack that null terminator, resulting in undefined behavior.

You likely want to start by allocating a more useful amount of space, and then grow exponentially as your input grows to avoid excessive calls to realloc.

E.g.

size_t allocated_bytes = 64;
char *name = malloc(allocated_bytes);
size_t read_bytes = 0;

char ch;

while ((ch = getchar()) != ' ') {
    if (read_bytes >= allocated_bytes - 1) {
        allocated_bytes *= 2;
        name = realloc(name, allocated_bytes);
    }

    name[read_bytes  ] = ch;
}

name[read_bytes] = '\0';

Checking the return of realloc and for EOF I leave as an exercise for the OP.

  • Related