Tuesday, November 17, 2009

Passing a pointer by reference in C

Today I needed a way to pass a pointer to an int to a function, modify the location of that pointer (via malloc) in that function, and then access the modified pointer from outside the function (from the caller).

I tried to code it like such (incorrectly):

int f(int *arr, int n) {
    int i;
    arr = malloc( n * sizeof (*arr));
    for (i = 0; i < n; ++i, ++arr) {
        *arr = i;
    }
    arr -= i;
    printf("Second element (from function) : %d\n", *(arr + 1));
    return 0;
}

int main()
{
    int *arr;
    f(arr, 5);
    printf("Second element (from caller) : %d", *(arr + 1));
    free(arr);
    return 0;
}
This is the output:

Second element (from function) : 1
Second element (from caller) : 1032044593

As you can see from the above output, the correct element is displayed when accessing the array from the function, but when I try to access the array from outside of the function (after it has been malloced and data was added), the value displayed seemed like a value from an unmalloced address!

What was I doing wrong?

The problem with the above code is that I am passing the pointer to an int by value to the function; ie a copy of the pointer.  That is why when I changed its address, the change was only visible from inside the function.

The Correct Way

The correct way to do it is to pass a pointer to the pointer to int (int **arr) to the function, like such:

int f(int **arr, int n) { //arr is now a pointer to a pointer to int
    int i;
    int *temp = malloc(n * sizeof **arr); //declare and malloc temporary space
    for (i = 0; i < n; ++i, ++temp) {
        *temp = i;
    }
    temp -= i;

    *arr = temp;  //Assign the value of the inputted location to the temporary one created earlier on

    printf("Second element (from function) : %d\n", *(temp + 1));
    return 0;
}

int main()
{
    int *arr;
    f(&arr, 5);  //The address of the pointer is now passed, not the pointer itself
    printf("Second element (from caller) : %d", *(arr + 1));
    free(arr);
    return;
}
Now the output from the above function is correct:

Second element (from function) : 1
Second element (from caller) : 1