【问题标题】:What is the difference between double pointers and single pointer in C functionsC函数中双指针和单指针有什么区别
【发布时间】:2021-09-21 05:37:00
【问题描述】:

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

【问题讨论】:

  • 指针在 C 中用于两种不同的事情: 1) 变量按值传递给函数 - 函数获取变量的副本。如果你想改变函数内部的变量,你需要传递一个指针——这样函数就可以获得指针的副本,并且它们都指向的值可以被改变。 2)分配内存需要一个指针。现在,想想如何组合这些东西——一个你可以改变的函数参数(一个指针),但它指向的值是一个指向已分配内存的指针:它是一个指向指针的指针(你称之为双指针)
  • 不确定您是否具备 C 知识,但您有两条相关信息:数组大小和(动态分配的)数组。您可能希望将它们包装在一个结构中并传递它。

标签: c function pointers dereference double-pointer


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    考虑以下内容。请参阅 cmets 以获得解释。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-03
      • 2011-04-04
      • 2010-12-17
      相关资源
      最近更新 更多