0

I have a program that starts with a minimum of 10 responses(int) stored in a dynamic array but should double in size using a void function if need be .

typedef int* Statistician;

int main(int argc, char** argv) {
  Statistician answer;
  answer=(int*)calloc(SIZE,sizeof(int));

  add(answer, &SIZE);
  
}

void add(Statistician answer, int *SIZE){
    answer=(int*)calloc(*SIZE * 2,sizeof(int)); 
}

is this the right way to double its size? I need a little nudge in the right direction.

Sans
  • 1
  • 2
  • Duplicate of https://stackoverflow.com/questions/32619428/increasing-array-size-dynamically-in-c and https://stackoverflow.com/questions/39985106/dynamically-increase-size-of-array-int-in-c – id01 Sep 11 '20 at 07:43
  • 2
    You are leaking memory, use `realloc` instead. – David Ranieri Sep 11 '20 at 07:44
  • 1
    @DavidRanieri - not only leaking-memory, but throwing away all original data when the original pointer to the original block containing the data is over-written `:)` Bad juju... – David C. Rankin Sep 11 '20 at 07:57

1 Answers1

1

The biggest misunderstanding is you are passing answer by-value to add() so add() receives a copy and any changes to answer in add() are lost on function return. And since the type is void you have no chance of making changes to answer seen back in the caller (main() here)

To fix the issue, pass the address of answer to add() so you operate on the original pointer address when you realloc() not calloc() to double the size, e.g.

void add(Statistician *answer, int *SIZE){
    void *tmp = realloc(*answer, *SIZE * 2 * sizeof **answer);
    if (!tmp) {
        perror ("add-realloc-answer");
        return;
    }
    *answer = tmp;
    /* optional - zero new memory mimicking calloc() */
    memset (*answer + *SIZE, 0, *SIZE * sizeof **answer);
    *SIZE *= 2;
}

You call add like:

    add(&answer, &SIZE);

(note, SIZE must be a global int properly initialized. It should actually be replace in main() with size_t size = SIZE; at the beginning and then pass a pointer to size).

Your confusion is compounded because you typedeffed the pointer, see Is it a good idea to typedef pointers?. Additionally, in C, there is no need to cast the return of malloc (or calloc, or realloc), it is unnecessary. See: Do I cast the result of malloc?

Eliminating The typedef Of The Pointer

To keep the levels of pointer indirection apparent it is far easier to avoid the typedef of the pointer and simply use int* where the type is needed. Making those changes, and changing the use of SIZE with a local variable size to ensure any local copies passed as parameters don't collide with a global variable. Adding simple routines to fill each integer and validate every integer before/after add() is accounted for, a preferred approach would be:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE 128

int *add (int **answer, size_t *size)
{
    /* always realloc to a temporary pointer, when realloc fails
     * it returns NULL and if you assign to original pointer you
     * create a memory leak with loss of your stored data.
     */
    void *tmp = realloc(*answer, *size * 2 * sizeof **answer);
    if (!tmp) { /* validate every allocation */
        perror ("add-realloc-answer");
        return NULL;
    }
    *answer = tmp;  /* assign reallocated block to pointer */
    /* optional - zero new memory mimicking calloc() */
    memset (*answer + *size, 0, *size * sizeof **answer);
    *size *= 2;     /* update size only on success */
    
    return *answer; /* return pointer indicating success */
}

int main(void) {
    
    int *answer;
    size_t size = SIZE;
    
    answer = calloc(size, sizeof *answer);  /* allocate */
    if (answer == NULL) {                   /* validate */
        perror ("calloc-answer");
        return 1;
    }
    
    for (size_t i = 0; i < size; i++)       /* fill memory */
        answer[i] = i;
    
    if (!add(&answer, &size))   /* add and validate return */
        fputs ("realloc failed, using original size.\n", stderr);
    else
        printf ("realloc succeeded -- %zu integers.\n", size);
    
    for (size_t i = size/2; i < size; i++)  /* fill new memory */
        answer[i] = i;
    
    for (size_t i = 0; i < size; i++)       /* validate complete block */
        if (i != (size_t)answer[i])
            fprintf (stderr, "error: answer[%zu] != %d\n", i, answer[i]);
    
    free (answer);      /* free allocated memory */
}

Note: since realloc() can fail, your function needs a way to communicate success or failure back to the caller. Choose a meaningful return type (int 0/1 or a pointer valid address/NULL, etc...)

Example Use/Output

$ ./bin/realloc-add
realloc succeeded -- 256 integers.

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/realloc-add
==8594== Memcheck, a memory error detector
==8594== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8594== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8594== Command: ./bin/realloc-add
==8594==
realloc succeeded -- 256 integers.
==8594==
==8594== HEAP SUMMARY:
==8594==     in use at exit: 0 bytes in 0 blocks
==8594==   total heap usage: 3 allocs, 3 frees, 2,560 bytes allocated
==8594==
==8594== All heap blocks were freed -- no leaks are possible
==8594==
==8594== For counts of detected and suppressed errors, rerun with: -v
==8594== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85