3. 兩層指針的參數

兩層指針也是指針,同樣可以表示傳入參數、傳出參數或者Value-result參數,只不過該參數所指的內存空間應該解釋成一個指針變數。用兩層指針做傳出參數的系統函數也很常見,比如pthread_join(3)void **參數。下面看一個簡單的例子。

例 24.3. 兩層指針做傳出參數

/* redirect_ptr.h */
#ifndef REDIRECT_PTR_H
#define REDIRECT_PTR_H

extern void get_a_day(const char **);

#endif

想一想,這裡的參數指針是const char **,有const限定符,卻不是傳入參數而是傳出參數,為什麼?如果是傳入參數應該怎麼表示?

/* redirect_ptr.c */
#include "redirect_ptr.h"

static const char *msg[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
			"Thursday", "Friday", "Saturday"};
void get_a_day(const char **pp)
{
     static int i = 0;
     *pp = msg[i%7];
     i++;
}
/* main.c */
#include <stdio.h>
#include "redirect_ptr.h"

int main(void)
{
     const char *firstday = NULL;
     const char *secondday = NULL;
     get_a_day(&firstday);
     get_a_day(&secondday);
     printf("%s\t%s\n", firstday, secondday);
     return 0;
}

兩層指針作為傳出參數還有一種特別的用法,可以在函數中分配內存,調用者通過傳出參數取得指向該內存的指針,比如getaddrinfo(3)struct addrinfo **參數。一般來說,實現一個分配內存的函數就要實現一個釋放內存的函數,所以getaddrinfo(3)有一個對應的freeaddrinfo(3)函數。

表 24.4. 通過參數分配內存示例:void alloc_unit(unit_t **pp); void free_unit(unit_t *p);

調用者實現者
  1. 分配pp所指的指針變數的空間

  2. 調用alloc_unit分配內存

  3. 讀取pp所指的指針變數,通過後者使用alloc_unit分配的內存

  4. 調用free_unit釋放內存

  1. 規定指針參數的類型unit_t **

  2. alloc_unit分配unit_t的內存並初始化,為pp所指的指針變數賦值

  3. free_unit釋放在alloc_unit中分配的內存


例 24.4. 通過兩層指針參數分配內存

/* para_allocator.h */
#ifndef PARA_ALLOCATOR_H
#define PARA_ALLOCATOR_H

typedef struct {
     int number;
     char *msg;
} unit_t;

extern void alloc_unit(unit_t **);
extern void free_unit(unit_t *);

#endif
/* para_allocator.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "para_allocator.h"

void alloc_unit(unit_t **pp)
{
     unit_t *p = malloc(sizeof(unit_t));
     if(p == NULL) {
	  printf("out of memory\n");
	  exit(1);
     }
     p->number = 3;
     p->msg = malloc(20);
     strcpy(p->msg, "Hello World!");
     *pp = p;
}

void free_unit(unit_t *p)
{
     free(p->msg);
     free(p);
}
/* main.c */
#include <stdio.h>
#include "para_allocator.h"

int main(void)
{
     unit_t *p = NULL;

     alloc_unit(&p);
     printf("number: %d\nmsg: %s\n", p->number, p->msg);
     free_unit(p);
     p = NULL;
     return 0;
}

思考一下,為什麼在main函數中不能直接調用free(p)釋放內存,而要調用free_unit(p)?為什麼一層指針的函數介面void alloc_unit(unit_t *p);不能分配內存,而一定要用兩層指針的函數介面?

總結一下,兩層指針參數如果是傳出的,可以有兩種情況:第一種情況,傳出的指針指向靜態內存(比如上面的例子),或者指向已分配的動態內存(比如指向某個鏈表的節點);第二種情況是在函數中動態分配內存,然後傳出的指針指向這塊內存空間,這種情況下調用者應該在使用內存之後調用釋放內存的函數,調用者的責任是請求分配和請求釋放內存,實現者的責任是完成分配內存和釋放內存的操作。由於這兩種情況的函數介面相同,應該在文檔中說明是哪一種情況。