3. 两层指针的参数

两层指针也是指针,同样可以表示传入参数、传出参数或者Value-result参数,只不过该参数所指的内存空间应该解释成一个指针变量。用两层指针做传出参数的系统函数也很常见,比如pthread_join(3)void **参数。下面看一个简单的例子。

例 24.3. 两层指针做传出参数

  1. /* redirect_ptr.h */
  2. #ifndef REDIRECT_PTR_H
  3. #define REDIRECT_PTR_H
  4.  
  5. extern void get_a_day(const char **);
  6.  
  7. #endif

想一想,这里的参数指针是const char **,有const限定符,却不是传入参数而是传出参数,为什么?如果是传入参数应该怎么表示?

  1. /* redirect_ptr.c */
  2. #include "redirect_ptr.h"
  3.  
  4. static const char *msg[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
  5. "Thursday", "Friday", "Saturday"};
  6. void get_a_day(const char **pp)
  7. {
  8. static int i = 0;
  9. *pp = msg[i%7];
  10. i++;
  11. }
  1. /* main.c */
  2. #include <stdio.h>
  3. #include "redirect_ptr.h"
  4.  
  5. int main(void)
  6. {
  7. const char *firstday = NULL;
  8. const char *secondday = NULL;
  9. get_a_day(&firstday);
  10. get_a_day(&secondday);
  11. printf("%s\t%s\n", firstday, secondday);
  12. return 0;
  13. }

两层指针作为传出参数还有一种特别的用法,可以在函数中分配内存,调用者通过传出参数取得指向该内存的指针,比如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. 通过两层指针参数分配内存

  1. /* para_allocator.h */
  2. #ifndef PARA_ALLOCATOR_H
  3. #define PARA_ALLOCATOR_H
  4.  
  5. typedef struct {
  6. int number;
  7. char *msg;
  8. } unit_t;
  9.  
  10. extern void alloc_unit(unit_t **);
  11. extern void free_unit(unit_t *);
  12.  
  13. #endif
  1. /* para_allocator.c */
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include "para_allocator.h"
  6.  
  7. void alloc_unit(unit_t **pp)
  8. {
  9. unit_t *p = malloc(sizeof(unit_t));
  10. if(p == NULL) {
  11. printf("out of memory\n");
  12. exit(1);
  13. }
  14. p->number = 3;
  15. p->msg = malloc(20);
  16. strcpy(p->msg, "Hello World!");
  17. *pp = p;
  18. }
  19.  
  20. void free_unit(unit_t *p)
  21. {
  22. free(p->msg);
  23. free(p);
  24. }
  1. /* main.c */
  2. #include <stdio.h>
  3. #include "para_allocator.h"
  4.  
  5. int main(void)
  6. {
  7. unit_t *p = NULL;
  8.  
  9. alloc_unit(&p);
  10. printf("number: %d\nmsg: %s\n", p->number, p->msg);
  11. free_unit(p);
  12. p = NULL;
  13. return 0;
  14. }

思考一下,为什么在main函数中不能直接调用free(p)释放内存,而要调用free_unit(p)?为什么一层指针的函数接口void alloc_unit(unit_t *p);不能分配内存,而一定要用两层指针的函数接口?

总结一下,两层指针参数如果是传出的,可以有两种情况:第一种情况,传出的指针指向静态内存(比如上面的例子),或者指向已分配的动态内存(比如指向某个链表的节点);第二种情况是在函数中动态分配内存,然后传出的指针指向这块内存空间,这种情况下调用者应该在使用内存之后调用释放内存的函数,调用者的责任是请求分配和请求释放内存,实现者的责任是完成分配内存和释放内存的操作。由于这两种情况的函数接口相同,应该在文档中说明是哪一种情况。