Search code examples
cmallocscanfrealloc

Taking ints with scanf into a memory allocation during a while loop and realloc'ing


I declare an int pointer and give it a memory allocation. I then feed ints into it until the user inputs Ctrl+d. The loop works fine unless I try to input 7 or more, at which point it gives me an error that looks like "realloc(): invalid next size" and gives me a backtrace/memory map. It has to have something to do with my realloc line but I'm not sure what. Can anybody enlighten me?

int  n= 0, *x, i;
double mean = 0.0, median = 0.0, stdDev = 0.0;

x =  malloc(sizeof(int));
for(i = 0; scanf("%d", &x[i]) != -1; i++) {
        n++;
        x = realloc(x, n+1 * sizeof(int));
        mean = findMean(x, n);
        median = findMedian(x, n);
        stdDev = findStandardDeviation(x, n, mean);
}

Solution

  • The problem is order of operations: n + 1 * sizeof(int) means "multiply 1 times the sizeof(int), then add n". You can use parentheses to enforce order: (n + 1) * sizeof(int).

    Memory allocations can be expensive! Ask for a reasonable chunk of memory, then increase it every so often by a factor of, say, 2. On a memory allocation system call, the OS may give you a larger block than you asked for so that subsequent realloc calls are less expensive and use memory already available to your program.

    It's good to check that your malloc and realloc calls succeeded. These functions return NULL if the allocation request failed.

    Also, don't forget to free your allocated memory when you finish with it to avoid memory leaks.

    It might be a fun exercise to write your own "vector/ArrayList/list"-like interface that expands and contracts to size and possibly features operations like slice(start_index, end_index) and remove(index).

    Here's a full example:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int nums_len = 0;
        int nums_capacity = 5;
        int *nums = malloc(nums_capacity * sizeof(int));
    
        while (scanf("%d", &nums[nums_len]) != -1) {
            if (++nums_len >= nums_capacity) {
                printf("increasing capacity from %d to %d\n", 
                       nums_capacity, nums_capacity * 2);
    
                nums_capacity *= 2;
                nums = realloc(nums, nums_capacity * sizeof(int));
    
                if (!nums) {
                    fprintf(stderr, "%s: %d: realloc failed\n", 
                            __func__, __LINE__);
                    exit(1);
                }
            }
    
            printf("got: %d\n", nums[nums_len-1]);
        }
    
        printf("Here are all of the %d numbers you gave me:\n", nums_len);
    
        for (int i = 0; i < nums_len; i++) {
            printf("%d ", nums[i]);
        }
    
        puts("");
        free(nums);
        return 0;
    }
    

    A sample run:

    5
    got: 5
    6
    got: 6
    7
    got: 7
    8
    got: 8
    1
    increasing capacity from 5 to 10
    got: 1
    3
    got: 3
    
    5
    got: 5
    6
    got: 6
    7
    got: 7
    1
    increasing capacity from 10 to 20
    got: 1
    2
    got: 2
    3
    got: 3
    4
    got: 4
    5
    got: 5
    6
    got: 6
    67
    got: 67
    8
    got: 8
    1
    got: 1
    2
    got: 2
    3
    increasing capacity from 20 to 40
    got: 3
    4
    got: 4
    1
    got: 1
    2
    got: 2
    Here are all of the 23 numbers you gave me:
    5 6 7 8 1 3 5 6 7 1 2 3 4 5 6 67 8 1 2 3 4 1 2