Search code examples
cpointerssegmentation-faultcoredump

Reason for Segmentation fault vs returning random values with the following C code


Please explain (reason for the output) what happens as a result of running the two segments of code. Please explain their difference too. There are two versions of setArr(int, int) as explained below...

#include <stdio.h>

void setArr(int, int);

int *arr[10]; // array of 10 int pointers

int main(int argc, char *argv[]) {
    int i;

    setArr(0, 0);
    setArr(1, 100);
    setArr(2, 200);
    setArr(3, 300);
    setArr(4, 400);

    for (i = 0; i < 5; i++) 
        printf("arr[%d]: %d\n", i, *arr[i]);   /* should be 0,100, 200,300,400 */

    return 0;
}

Versions of setArr

Version A

void setArr(int index, int v) {
    int i = v; 
    *arr[index] = i;
}

Output: Segmentation fault (core dumped)

Version B

void setArr(int index, int v) {
    int i = v;
    arr[index] = &i;
}

Output:

arr[0]: 400
arr[1]: 32748
arr[2]: 32748
arr[3]: 32748
arr[4]: 32748

I presume the values from running Version B are just random values.

I am fairly new to pointers I have knowledge in Java, so please explain it as beginner friendly as you can :)


Solution

  • You are hitting a lot of undefined behavior scenarios, but I will explain what is likely happening.

    arr is an array of 10 pointers to integers.

    int * arr[10]; // array of 10 int pointers
    

    And when declared as a global variable, all of those pointers are going to be zero-initialized - so hence, it's an array of 10 NULL pointers.

    So this line in version A, is dereferencing the address at arr[index]:

    * arr[index] = i;
    

    Is effectively saying this:

    *(NULL) = i;
    

    And that will certainly crash consistently.

    In Version B, you have it as:

        int i = v;
        arr[index] = &i;
    

    So now you are correctly assigning a pointer to a slot in the array. However that address getting assigned is to a local stack variable, i, which goes out of scope as soon as the function returns. So when you print the value at that address, it's most certainly been clobbered from other calls writing on top of the stack. (Or technically this "undefined behavior" of accessing a memory address of a stack variable that has gone out of scope.)

    Better:

    void setArr (int index, int v){
        arr[index] = malloc(sizeof(int));
        *arr[index] = v;
    }
    

    The above allocates memory for the address that you want to copy that value into. You're on your own for how to free that memory.

    Alternatively:

    Just declare arr as an array of integers instead of pointers:

    int arr[10];
    void setArr (int index, int v){
        arr[index] = v;
    }
    

    And then print normally without the * deference thing on arr.

    printf("arr[%d]: %d\n", i, arr[i]);