cfunctionpointersdereferencedouble-pointer

What is the difference between double pointers and single pointer in C functions


I'm new to the C programming language and beginning to mess around with pointers and allocating memory to objects. I wanted to make a simple program that would read in (x) elements from the user and simply print them out using functions.

This was my inital code at the beginning. The code was reading in the user input properly but it gave the wrong output and crashed when displaying elements within the main function.

int main() {
    int *myArr;
    int myArrSize;
    getInputArr(&myArrSize, &myArr);

    for (int i = 0; i < myArrSize; i++) {
        printf("Element No.%i: %i\n", i, myArr[i]);
    }

    free(myArr);
    return 0;
}

void getInputArr(int *arrSize, int *arr) {
    printf("Please Enter Length of Array: \n");
    scanf("%i", arrSize);

    arr = (int *) malloc(*arrSize * sizeof(int));

    printf("Enter %i Numbers!\n", *arrSize);
    for (int i = 0; i < *arrSize; i++) {
        scanf("%i", &arr[i]);
    }
}

After messing around, I finally got it to work using double pointers but I am unsure how it completely works, could someone explain why the code below behaves as expected?

int main() {
    int *myArr;
    int myArrSize;
    getInputArr(&myArrSize, &myArr);

    for (int i = 0; i < myArrSize; i++) {
        printf("Element No.%i: %i\n", i, myArr[i]);
    }

    free(myArr);
    return 0;
}

void getInputArr(int *arrSize, int **myArr) {
    printf("Please Enter Length of Array: \n");
    scanf("%i", arrSize);

    *myArr = (int *) malloc(*arrSize * sizeof(int));

    printf("Enter %i Numbers!\n", *arrSize);
    for (int i = 0; i < *arrSize; i++) {
        scanf("%i", &((*myArr)[i]));
    }
}

Solution

  • There are several surprising things in your implementation, but in the end they all make sense, and indeed they must be present in order for this implementation to work.

    1. You ultimately want int *myArr in main to simulate an array of int, but in getInputArr you refer to it using a "double pointer" int **myArr. But this makes sense, because any time you want to return something from a function "by reference" like this, you need to use a pointer -- an extra pointer. To return an int by reference, you'd use an int *. But to return an int * by reference, you need an int **. The fact that you (correctly) call getInputArr(&myArrSize, &myArr) in main shows that getInputArr's second argument is going to be an int **.
    2. In getInputArr, when you call scanf, you do not have an & next to the argument you pass to scanf for %d to read into. This is highly unusual, but in this case it's absolutely correct, because arrSize is already a pointer.
    3. You then have *myArr = (int *) malloc(*arrSize * sizeof(int)). This was the first thing I spotted in your initial (nonworking) implemenation that was quite wrong. In getInputArr, myArr is a pointer to the pointer that you want to set. So *myArr is the pointer that you want to set.
    4. Finally, you have the jawbreaker call scanf("%i", &((*myArr)[i]));. This looks pretty ugly, and there are probably simpler ways to write it, but it's correct. Let's break it down. Again, myArr is a pointer to the pointer you want to work with. So *myArr is the pointer you want to work with. So (*myArr)[i] is one element of the simulated array (pointed to by the pointer) that you want to work with. You need explicit parentheses, because if you wrote *myArr[i] this would mean, "take the i'th element pointed to by myArr, interpret it as a pointer, and take the contents." But what you want (and, with the parentheses, you have) is "take myArr, interpret it as a pointer, take the thing that it points to, which is *myArr, and interpret that as a pointer, and finally take the i'th element that it (the second pointer) points to."