【问题标题】:Passing a double pointer in a function在函数中传递双指针
【发布时间】:2020-06-04 22:32:03
【问题描述】:

我正在用 C 语言创建一个带有链表的程序。我在 main 函数中创建了一个 head 变量。然后我通过引用函数insert_at_head(struct node **head) 传递它。

我还有另一个在尾部插入的功能。在基本条件下,如果我的列表为空,我想再次调用insert_at_head(struct node **head)。但是我在传递实际参数时感到困惑。应该是insert_at_head (&(*head)) 还是insert_at_head (head)?

就个人而言,我认为两者是相同的,因为它们都传递了一个双指针。我应该使用哪一个?

【问题讨论】:

  • insert_at_head(head)
  • 假设在你调用insert_at_head的函数中有struct node *head;(即head是一个单个指针)那么你应该调用insert_at_head(&head)。在调用函数中没有取消引用 head。没有minimal reproducible example 这是我的猜测。
  • 我尝试使用一个简单的程序来增加数字的值。两种格式都正确地增加了值。我认为他们两个都应该工作。 ide.codingblocks.com/s/182435
  • 在功能上它们是一样的,你可以将各种括号和& 和* 组合在一起,然后编译并运行。没有必要在这里添加任何内容。如果head 是struct node** 类型并且您有一个接受struct node** 类型的函数,则只需传递head。它是最简单、最简洁、最清晰的代码。任何额外的东西,虽然仍然有效,但不是最佳实践,并且会在代码审查时引起一些注意。

标签: c pointers linked-list parameter-passing


【解决方案1】:

假设head 的类型是struct node **,那么您可以将其直接传递给期望该类型参数的函数。

&(*head) 与head 完全相同。当& 运算符的操作数是一元* 运算符的结果时,它们会相互抵消,实际上都不会对它们进行计算。

这在 C 标准的第 6.5.3.2p3 节中关于地址和间接运算符进行了详细说明:

一元& 运算符产生其操作数的地址。如果 操作数的类型为“type”,结果的类型为“pointer to type”。 如果 操作数是一元 * 运算符的结果,也不是 运算符也不是 & 运算符被评估,结果就像 两者都被省略了, 除了对运算符的约束仍然 应用,结果不是左值。

所以如果head 是一个指针,那么表达式head 和&(*head) 是等价的。

【讨论】:

    【解决方案2】:

    关于insert_at_head 函数,您必须记住,在C 中所有参数都是按值传递的。这意味着该值被复制到参数变量中。在函数内部,如果你修改了参数变量(比如赋值给它),那么你只修改了副本,而不是原来的。

    如果要修改原始值,则必须模拟按引用传递,这可以通过使用地址运算符& 传递指向该值的指针来完成。如果你想对一个指针变量执行此操作,那么你会得到一个指向指针的指针。

    这是一个简单的例子:

    #include <stdio.h>
    
    // Define two global variables
    int a = 10;
    int b = 20;
    
    void change1(int *x)
    {
        x = &b;  // Make x point to b
    }
    
    void change2(int **x)
    {
        *x = &b;  // Make *x point to b
    }
    
    int main(void)
    {
        // Define a pointer variable, and make it point to the global variable a
        int *pointer_to_a = &a;
    
        // Will print the value of a, i.e. 10
        printf("*pointer_to_a = %d\n", *pointer_to_a);
    
        // Try to change where pointer_to_a is pointing
        change1(pointer_to_a);
    
        // Here pointer_to_a is *still* pointing to a, it wasn't changed, will print the value 10
        printf("*pointer_to_a = %d\n", *pointer_to_a);
    
        // Do another attempt to change where pointer_to_a is pointing
        change2(&pointer_to_a);
    
        // Now pointer_to_a is no longer pointing to a, it points to b and 20 will be printed
        printf("*pointer_to_a = %d\n", *pointer_to_a);
    }
    

    回到insert_at_head 函数(我们只能推测其功能)我猜它会添加到链表的头部。它将通过修改列表头指向的位置来做到这一点。

    如上例所示,除非我们将指针传递给指针,否则一旦函数结束,对头部的赋值就会丢失。这可以通过使用指向指针的指针来解决,并在调用函数时使用地址运算符&amp; 传递头指针。

    【讨论】:

      【解决方案3】:

      在 C 语言中,您不能通过引用传递变量。

      但是,您可以将 指针 传递给您的变量。当然指针是按值传递的。例如,如果您声明一个变量 head,它是指向结构 node 的指针:

      struct node* head;
      

      你可以准备一个函数来操作这样的变量:

      void insert_at_head(struct node** ptr);
      

      然后你调用它:

      insert_at_head(&head);
      

      where 运算符 &amp; 产生一个指向变量的指针,即 struct node ** 类型的值。该 值 被传递给被调用者。

      当然,另一个函数,比如:

      void append_at_end(struct node** ptr);
      

      可以在必要时调用前一个函数,将它作为参数得到的指针传递给它:

      void append_at_end(struct node** ptr)
      {
          if(*ptr == NULL)          /* list is empty */
              insert_at_head(ptr);  /* the same pointer to a head pointer */
          else {
              /* other stuff */
          }
      }
      

      【讨论】:

        【解决方案4】:

        应该是 insert_at_head (&(*head)) 还是 insert_at_head (head)?

        没有。他们都错了,也一样。

        如果您在主程序中执行了以下操作:

        struct node head;
        

        您的代码将开始出现问题。

        你可以让它工作,但这是错误的方式。

        要使其正常工作,您必须在 main 中执行以下操作:

        struct node head;
        struct node *phead=&head;
        
        insert_at_head(&phead).
        

        正确的方法是:

        struct node *head=NULL;
        
        insert_at_head(&head).
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-05-19
          • 2014-06-20
          • 1970-01-01
          • 2013-10-25
          • 2010-09-29
          • 2015-04-29
          • 1970-01-01
          相关资源
          最近更新 更多