指向结构的指针作为函数的参数有什么用?

What is the use of a pointer to pointer to struct as an arguments of a function?

问题的原因是似乎没有理由使用像“struct **pointer”这样的双重间接寻址。

为了理解指向结构的指针作为函数参数的功能,我准备了一个示例。目的是查看函数对结构所做的更改是否超出了函数的范围,或者它们是否只在函数内部产生影响。这是按值或按引用传递参数的常见问题。

#include <stdio.h>

/* A simple structure is defined to see the effect that the functions 
 * have on it. */
typedef struct est
{
  int val1;
  int val2;
} est_t;

/* These three functions simply assign the values val1 and val2 to the 
 * structure. Each of these functions uses a different way of passing 
 * the parameter to the function referring to the structure. There are 
 * more comments below, in the definition of each function. */
void foo(int, int, est_t);
void bar(int, int, est_t*);
void baz(int, int, est_t**);

/* This is a function to print the values of a structure and make the 
 * code cleaner. It also contains a parameter about a structure! */
void print_est(est_t*);

int main (int argc, char *argv[])
{
  est_t a;

  foo(10, 11, a);
  print_est(&a);
  
  bar(20, 21, &a);
  print_est(&a);
  
  est_t *p = &a;
  baz(30, 31, &p);
  print_est(&a);
  
  return 0;
}

void foo(int v1, int v2, est_t ve)
{
  /* In this case the structure is "directly put" into the function.
   * As the structure already is inside the function, the values of each 
   * element are assigned with the ". " */
  ve.val1 = v1;
  ve.val2 = v2;
  
  /* With these printf you can see what is happening within the 
   * function. */
  printf("[foo] val1 = %d\n", ve.val1);  
  printf("[foo] val2 = %d\n", ve.val2);
}

void bar(int v1, int v2, est_t *ve)
{
  /* In this case the structure is passed into the function using a 
   * pointer. */
  ve->val1 = v1;
  ve->val2 = v2;
  
  printf("\n[bar] val1 = %d\n", ve->val1);  
  printf("[bar] val2 = %d\n", ve->val2);
}

void baz(int v1, int v2, est_t **pp)
{
  /* In this case the structure is passed into the function using a 
   * pointer to a pointer to a struct. */
  (*pp)->val1 = v1;
  (*pp)->val2 = v2;
  
  printf("\n[baz] val1 = %d\n", (*pp)->val1);  
  printf("[baz] val2 = %d\n", (*pp)->val2);
}

void print_est(est_t *addr)
{
  printf("[main] val1 = %d\n", addr->val1);
  printf("[main] val2 = %d\n", addr->val2);
}

这是您 运行 程序时的输出。

foo(10, 11, a);  
[foo] val1 = 10  
[foo] val2 = 11  
[main] val1 = -1238356256  
[main] val2 = 32764

您可以看到在函数内分配的值并没有保留在函数外

让我们看看 barbaz 的输出。

bar(20, 21, &a);    
[bar] val1 = 20  
[bar] val2 = 21  
[main] val1 = 20  
[main] val2 = 21

est_t *p = &a;  
baz(30, 31, &p);  
[baz] val1 = 30  
[baz] val2 = 31  
[main] val1 = 30  
[main] val2 = 31 

他们好像也是这样。我的意思是在这两种情况下,值都保留在函数之外。

Foobar是典型的by-valueby-reference,即使更改不是永久性的,从节省资源的角度来看,通过引用传递结构的想法也很有趣。参见 advantage of passing pointer to a struct as argument?

但正如 barbaz 所做的一样,是否有任何理由使用双指针将参数传递给函数?

is there any reason to use a double pointer to pass arguments to a function

是的。只要您想更改传递的参数,就可以使用指针。所以如果你想改变一个指针,那么你需要一个指向指针的指针。

简单示例:

void foo(struct bar **ptr)
{
    *ptr = malloc(10 * sizeof **ptr);
    
    for(int i=0; i<10; i++)
        (**ptr)->x = i;
}

函数接受类型 T ** 的参数(对于任何类型 T)通常有两个原因:

  • 参数对应一个指针数组;
  • 该函数用于更新指针值。

作为第一个例子,假设我们有这样的代码

int main( void )
{
  char *strs[] = { "foo", "bar", "bletch", "blurga", NULL };
  func( strs );
  ...
}

strs是一个指针数组,但是由于通常的衰减规则,func接收到的是指向指针的指针:

void func( char **s )
{
  ...
}

或者,您的函数旨在更新指针值。假设我们已经动态分配了一个 struct 类型的数组,我们需要用 realloc 扩展并初始化扩展内存。为了使它更清晰一些,我们将其放在一个单独的函数中:

void extend( struct s **ptr, size_t *size )
{
  struct s *tmp = realloc( *ptr, *size * 2 );
  if ( tmp )
  {
    for ( size_t i = *size; i < *size * 2; i++ )
      init ( &tmp[i] );
    *ptr = tmp;
    *size *= 2;
  }
}