1

I want to create a simple array of integers having 10 elements. I'm using dynamic memory to allocate a space in memory and whenever I exceed that amount It will call realloc to double its size. Whenever I type 'q' it will exit the loop and print the array.

I know my program is full of bugs, so please guide me into where I'm going wrong.

/* Simple example of dynamic memory allocation */

/* When array reaches 10 elements, it calls
   realloc to double the memory space available */

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

#define SIZE_ARRAY 10
int main()
{
    int *a;
    int length=0,i=0,ch;


    a= calloc(SIZE_ARRAY, sizeof(int));
    if(a == NULL)
    {
        printf("Not enough space. Allocation failed.\n");
        exit(0);
    }

    printf("Fill up your array: ");

    while(1)
    {

        scanf("%d",&ch);
        if(ch == 'q')                  //possible mistake here
            break;

        a[length]=ch;
        length++;

        if(length % 10 == 0)   //when length is 10, 20, 30 ..
            {
                printf("Calling realloc to double size.\n");

                a=realloc(a, 2*SIZE_ARRAY*sizeof(int));
            }


    }

    printf("You array is: \n");
    for(;i<length;i++)
        printf("%d ",a[i]);

   return 0;
}

Whenever I type 'q' the program crashes. I'm a beginner so I know I'm doing a dumb mistake. Any help would be greatly appreciated.

5
  • You have multiple errors. a=realloc(a, 2*SIZE_ARRAY*sizeof(int));. The size there is constant. That's obviously not what you want as the size should increase for each realloc. Next, scanf("%d",&ch); if(ch == 'q') doesn't do what you think it does. ch is being parsed as an integer. So the scanf will always fail when you enter a non-numeric value such as 'q' (always check the return value of scanf). Commented Sep 1, 2016 at 1:20
  • BTW, suggest you learn to use a debugger so that you can more effectively find these issues for yourself. Commented Sep 1, 2016 at 1:21
  • Thanks for the advice kaylum. In your first point the SIZE_ARRAY macro is constant but if I mutliply the constant by 2 then wouldn't it give me double that value? ....And also what would you suggest that I should do if I want to exit the loop pressing the character 'q'? My mind is just blocked and can't seem to figure it out. Commented Sep 1, 2016 at 1:34
  • 2*ARRAY_SIZE is a constant value right? So that's realloc with size 80, 80, 80, 80, etc. Can you see the problem there? Size needs to be something like 80, 160, 320, etc. For the exit, one option is to check the return value of scanf. If the input is a valid number it will return 1 (the number of items parsed) otherwise it will return 0 (no items parsed). Commented Sep 1, 2016 at 1:37
  • Oh I see your point now.. I'll try to fix it. Thanks again for the help. Commented Sep 1, 2016 at 1:39

1 Answer 1

2

You should not double the memory every realloc() that can get very large, very quick. You normally expand the memory by small chunks only. realloc() also has the nasty habit to use another part of the memory if the old one cannot be elongated enough. If that fails you loose all of your data in the old memory. This can be avoided by using a temporary pointer to point to the new memory and swap them after a successful allocation. That comes with the cost of just on extra pointer (mostly 4 or 8 bytes) and a swap (needing only a handful of CPU cycles at most. Be careful with x86's xchg it uses a lock in case of multiple processors which is quite expensive!)

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

// would normally be some small power of two, like
// e.g.: 64 or 256
#define REALLOC_GROW 10
int main()
{
  // a needs to be NULL to avoid a first malloc()
  int *a = NULL, *cp;
  // to avoid complications allocated is int instead of size_t
  int allocated = 0, length = 0, i, ch, r;

  printf("Fill up your array: ");
  while (1) {
    // Will also do the first allocation when allocated == length
    if (allocated <= length) {
      // realloc() might choose another chunk of memory, so
      // it is safer to work on copy here, such that nothing is lost
      cp = realloc(a, (allocated + REALLOC_GROW) * sizeof(int));
      if (cp == NULL) {
        fprintf(stderr, "Malloc failed\n");
        // but we can still use the old data
        for (i = 0; i < length; i++) {
          printf("%d ", a[i]);
        }
        // that we still have the old data means that we need to
        // free that memory, too
        free(a);
        exit(EXIT_FAILURE);
      }
      a = cp;
      // don't forget to keep the amount of memory we've just allocated
      allocated += REALLOC_GROW;
    }
    // out, if user typed in anything but an integer
    if ((r = scanf("%d", &ch)) != 1) {
      break;
    }
    a[length] = ch;
    length++;
  }

  printf("Your array is: \n");
  // keep informations together, set i=0 in the loop
  for (i = 0; i < length; i++) {
    printf("%d ", a[i]);
  }
  fputc('\n', stdout);
  // clean up
  free(a);

  exit(EXIT_SUCCESS);
}

If you play around a bit with the start-value of allocated, the value of REALLOC_GROW and use multiplication instead of addition in the realloc() and replace the if(allocated <= length) with if(1) you can trigger the no-memory error and see if it still prints what you typed in before. Now change the realloc-on-copy by using a directly and see if it prints the data. That still might be the case but it is not guaranteed anymore.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot. Just two questions. What do you mean by " // a needs to be NULL to avoid a first malloc()". And also in the "free(a)". Would that be necessary at the end there of the prgram? As I understood it, once the program terminates it will release all the memory right? So is there any reason to include it?
@tadm123 If you call realloc(p,size) with p == NULL it acts like p = malloc(size) but p must be initialized. That way you have only one point of failure (realloc) and not two (the first malloc() and the following realloc()s). You don't need to free() memory at the end of the program. But it is a) good style and b) if you use a memory checking program like e.g.: Valgrind, not free'ing counts as leaks and may drown real leaks. Make it a habit in the first place, and you don't even have to think about it when it counts.
I see. Thanks again.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.