Thread: Dynamically sized array function behavior

  1. #1
    Registered User
    Join Date
    Sep 2020
    Posts
    2

    Dynamically sized array function behavior

    Hello C boards,

    First time posting here. I am a bit of a noob, but have done my due diligence in trying to understand the behavior. Despite best efforts, I cannot understand or explain the behavior of the below code.

    The program takes a text from stdin. The text is a simple stream of data comprised of numbers separated by newlines.

    The goal of the program is to store these numbers into an array. The input size is unknown and therefore the program must dynamically allocate additional memory.

    When realloc is ran inside main, there is no issue. When realloc is ran from a function call, the program errors with an eventual "realloc(): invalid next size".

    To me, a noob, it appears the process is the same regardless of where realloc is called. There must be some fundamental concept I am missing. Could someone help enlighten my lack of understanding?

    Again: If I leave the realloc portion uncommented in main and comment out the function call, it works as expected. If I comment out the realloc portion in main and uncomment the function call, it fails.

    Below is the code (I will comment out the realloc in main):

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void push_int(int *array, int *counter, int *size, int *value){
    
        if (*counter >= *size){
            array = realloc(array,  (*size + *counter)*sizeof(int)*2);
            *size = (*size + *counter)*2;
        }
        array[*counter] = *value;
    
    
    }
    
    int main (int argc, char *argv[]){
    
        char *line = NULL;
        size_t len = 0;
        ssize_t read;
    
        char *tok = NULL;
        int number = 0;
    
        int size = 1;
        int *array = malloc(sizeof(int));
    
        int counter = 0;
    
        if (stdin == NULL) {
           exit(EXIT_FAILURE);
        }
    
        while ((read = getline(&line, &len, stdin)) != -1) {
    
            tok = strtok(line, "\n");
            number = atoi(tok);
    
            //if (counter >= size){
            //    array = realloc(array,  (size + counter)*sizeof(int)*2);
            //    size = (size + counter)*2;
            //}
    
            push_int(array, &counter, &size, &number);        
            
            //array[counter] = number;
            counter ++;
        }
    }
    I am running gcc version 10.2.0 (GCC) x64 on Linux 5.8.7
    I am compiling with: gcc -lm example.c -o example

    Thank you

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,200
    You need to understand that realloc can move the memory.

    So push_int needs some way to convey that back to main.

    A simple way is to do
    array = push_int(array, &counter, &size, &number);

    and make your function return the appropriate result.
    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.

  3. #3
    Registered User
    Join Date
    Sep 2020
    Posts
    2
    Aha! It works!

    THANK YOU!

    For reference, below is the modified function:

    Code:
    int *push_int(int *array, int *counter, int *size, int *value){
    
        if (*counter >= *size){
            array = realloc(array,  (*size + *counter)*sizeof(int)*2);
            *size = (*size + *counter)*2;
        }
        array[*counter] = *value;
    
        return array;
    }

  4. #4
    Registered User Sir Galahad's Avatar
    Join Date
    Nov 2016
    Location
    The Round Table
    Posts
    174
    You should be incrementing the counter inside of push_int() rather than outside of the function.

    And the resize calculation should probably be something more like this:

    Code:
    int* push_int (int *array, size_t *counter, size_t* size, int value) {
        if (*counter >= *size){
            *size = *size * 2 + 1;
            array = realloc(array,  *size * sizeof(int));
        }
        array[(*counter)++] = value;
        return array;
    }
    The power of null.

  5. #5
    Registered User
    Join Date
    Feb 2019
    Posts
    746
    Notice if you use the same pointer as an argument and return to realloc() call you can end up with a memory leakage if realloc() fails. It is wise to use different pointers. Your code, changed a little bit:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    int push_int( int **array, size_t *counter, int value )
    {
      int *p;
    
      if ( ! ( p = realloc( *array, ( *counter + 1 ) * sizeof value ) ) )
        return 0;
    
      p[( *counter )++] = value;
      *array = p;  // update the pointer!
    
      return 1;
    }
    
    int main( int argc, char *argv[] )
    {
      int *array;
      char *line;
      size_t len, counter;
      int number;
    
      if ( !( array = malloc( sizeof * array ) ) )
      {
        fputs( "ERROR allocating initial array.\n", stderr );
        return EXIT_FAILURE;
      }
    
      line = NULL;
      len = 0;
      counter = 0;
    
      // getline() will allocate a buffer for the line. This buffer must be freed
      // before calling getline() again!
      while ( getline( &line, &len, stdin ) != -1 )
      {
        if ( sscanf( line, "%d", &number ) != 1 )
        {
          fputs( "ERROR geting integer value.\n", stderr );
          free( line );
          free( array );
          return EXIT_FAILURE;
        }
    
        number = atoi( line );
    
        // free allocated line and prepare for next read.
        free( line );
        line = NULL;
        len = 0;
    
        if ( ! push_int( &array, &counter, number ) )
        {
          fputs( "ERROR pushing value.\n", stderr );
          free( array );
          return EXIT_FAILURE;
        }
      }
    
      // ... dosomething with the array here...
      // ...
    
    
      // Finally free the array and return with success.
      free( array );
    
      return EXIT_SUCCESS;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 18
    Last Post: 01-30-2014, 08:42 AM
  2. Replies: 8
    Last Post: 09-06-2012, 11:49 AM
  3. strange behavior with array in function
    By pater in forum C Programming
    Replies: 4
    Last Post: 01-24-2012, 07:02 AM
  4. dynamically defined array size in a function
    By earth_angel in forum C Programming
    Replies: 21
    Last Post: 05-28-2005, 01:44 AM
  5. Dynamically SIzed Global Matrix problems
    By Josh Kasten in forum C++ Programming
    Replies: 2
    Last Post: 01-18-2003, 11:51 PM

Tags for this Thread