简体   繁体   中英

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

I'm new to the C programming 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]));
    }
}

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."

You've got multiple levels of pointers confusing you. But what if the dynamically allocated array you're dealing with lives inside a struct? Then we only have to deal with the pointer passing ("reference") semantics on that struct.

Consider the below. See the comments for explanation.

/* Basic stuff. */    
#include <stdio.h>
#include <stdlib.h>

/* Here's our struct. 
 * It contains the size of the array,
 * and the pointer to the memory allocated for the array.
 */
typedef struct dyn_int_array {
    size_t size;
    int * array;
} dyn_int_array_t; 

/* Forward declarations for a function which creates and 
 * returns our dynamic_int_array struct.
 */
dyn_int_array_t * create_dyn_int_array();
/* ... and here's where you see that we don't want to 
 * pass the struct by value, but rather effectively by 
 * reference by passing a pointer to it. 
 */
void scan_into_dyn_int_array(dyn_int_array_t * da);

int main(void) {
    dyn_int_array_t * da = create_dyn_int_array();
    /* I never bothered to free up the allocated memory,
     * because it's not really critical for demonstration here.
     */
}

The implementations of those functions are below, but aren't really critical to this demonstration, as you've hopefully see the pass by reference use of pointers, without having to directly worry about or get confused by two levels of pointer indirection.

dyn_int_array_t * create_dyn_int_array() {
    dyn_int_array_t * result = malloc(sizeof(dyn_int_array_t));

    fprintf(stdout, "Input an array size: ");
    fscanf(stdin, "%zd", &(result->size));
    
    result->array = malloc(sizeof(int) * result->size);
    
    /* Because "da" is already a pointer to dye_int_array_t
     * there's no need to get its address.
     */
    scan_into_dyn_int_array(result);
    
    return result;  
}

void scan_into_dyn_int_array(dyn_int_array_t * da) {
    for (int i = 0; i < da->size; i++) {
        /* We do have to pass the address of the current 
         * element of the array to fscanf.
         */
        fscanf(stdin, "%d", &(da->array[i]));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM