C函数中双指针和单指针有什么区别

时间:2021-07-12 00:30:46

标签: c function pointers dereference double-pointer

我是 C 编程的新手,开始处理指针和为对象分配内存。我想做一个简单的程序,它可以从用户那里读入 (x) 个元素,然后使用函数简单地将它们打印出来。

这是我一开始的初始代码。代码正在正确读取用户输入,但它给出了错误的输出,并在主函数中显示元素时崩溃。

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]);
    }
}

搞砸之后,我终于使用双指针让它工作了,但我不确定它是如何完全工作的,有人可以解释为什么下面的代码表现得像预期的一样吗?

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]));
    }
}

2 个答案:

答案 0 :(得分:2)

在您的实现中有一些令人惊讶的事情,但最终它们都有意义,而且确实必须存在它们才能使此实现工作。

  1. 您最终希望 int *myArr 中的 main 模拟 int 的数组,但在 getInputArr 中您使用“双指针”int **myArr 来引用它.但这是有道理的,因为任何时候你想从一个像这样“通过引用”的函数返回一些东西,你需要使用一个指针——一个额外的指针。要通过引用返回 int,您需要使用 int *。但是要通过引用返回 int *,您需要一个 int **。您(正确)在 getInputArr(&myArrSize, &myArr) 中调用 main 的事实表明 getInputArr 的第二个参数将是 int **
  2. 在 getInputArr 中,当您调用 scanf 时,您没有在为 & 传递给 scanf 的参数旁边有一个 %d读入。这是非常不寻常的,但在这种情况下它是绝对正确的,因为 arrSize 已经是一个指针。
  3. 然后您就有了 *myArr = (int *) malloc(*arrSize * sizeof(int))。这是我在您最初的(非工作)实现中发现的第一件事,这是非常错误的。在 getInputArr 中,myArr 是指向要设置的指针的指针。所以 *myArr 是您要设置的指针。
  4. 最后,您有惊天动地的呼叫 scanf("%i", &((*myArr)[i]));。这看起来很丑陋,可能有更简单的方法来编写它,但它是正确的。让我们分解一下。同样,myArr 是指向您要使用的指针的指针。所以 *myArr 是您要使用的指针。所以 (*myArr)[i] 是您想要使用的模拟数组(由指针指向)的一个元素。您需要显式括号,因为如果您编写 *myArr[i],这意味着“取 i 指向的第 myArr 个元素,将其解释为指针,并取其内容”。但是你想要的(并且,加上括号,你有)是“取 myArr,将它解释为一个指针,取它指向的东西,即 *myArr,并解释 作为一个指针,最后取它(第二个指针)指向的第 i 个元素。”

答案 1 :(得分:0)

您有多个级别的指针使您感到困惑。但是,如果您正在处理的动态分配的数组存在于结构中呢?然后我们只需要处理该结构上的指针传递(“引用”)语义。

考虑以下问题。请参阅注释以获取解释。

/* 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.
     */
}

这些函数的实现如下,但对本演示并不重要,因为您希望看到指针的引用传递使用,而不必直接担心或获取被两级指针间接混淆。

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]));
    }
}
相关问题