关键词搜索

源码搜索 ×
×

C语言的本质(16)——函数接口的传入参数与传出参数

发布2014-07-17浏览8724次

详情内容

 

如果函数接口有指针参数,既可以把指针所指向的数据传给函数使用(称为传入参数),也可以由函数填充指针所指的内存空间,传回给调用者使用(称为传出参数),例如strcpy的函数原型为

 

char *strcpy(char *dest, const char *src);

其中src参数是传入参数,dest参数是传出参数。有些函数的指针参数同时担当了这两种角色,如select函数。其函数原型为:

  1. int select(int nfds, fd_set *readfds,fd_set *writefds,
  2. fd_set *exceptfds, struct timeval*timeout);

其中的fd_set *参数,既是传入参数又是传出参数,这称为Value-result参数。

 

传入参数示例:

假如我们实现一个函数,其参数通过地址来传入一个值,其原型如下:

void func(const unit_t *p);

其调用者与实现者之间的协议如下:

调用者

实现者

1、分配p所指的内存空间;

2、在p所指的内存空间中保存数据;

3、调用函数;

4、由于有const限定符,调用者可以确信p所指的内存空间不会被改变。

1、规定指针参数的类型unit_t *;

2、读取p所指的内存空间。

 

 

传出参数示例:

假如我们实现一个函数,其参数通过地址传出一个值,其原型如下:

 

void func(unit_t *p);

 

其调用者与实现者之间的协议如下:

调用者

实现者

1、分配p所指的内存空间

2、调用函数

3、读取p所指的内存空间

1、规定指针参数的类型unit_t *

2、在p所指的内存空间中保存数据

 

 

 

Value-result参数示例:

 

void func(unit_t *p);

其调用者与实现者之间的协议如下:

调用者

实现者

1、分配p所指的内存空间

2、在p所指的内存空间保存数据

3、调用函数

4、读取p所指的内存空间

1、规定指针参数的类型unit_t *

2、读取p所指的内存空间

3、改写p所指的内存空间

 

注意:由于传出参数和Value-result参数的函数接口完全相同,应该在文档中说明是哪种参数。

很多系统函数对于指针参数是NULL的情况有特殊规定:如果传入参数是NULL表示取缺省值,例如pthread_create(3)的pthread_attr_t *参数,也可能表示不做特别处理,例如free的参数;如果传出参数是NULL表示调用者不需要传出值,例如time(2)的参数。这些特殊规定应该在文档中写清楚。

下面是一个传出参数的完整例子:

 

  1. /* populator.h */
  2. #ifndef POPULATOR_H
  3. #define POPULATOR_H
  4. typedef struct {
  5. int number;
  6. char msg[20];
  7. } unit_t;
  8. extern void set_unit(unit_t *);
  9. #endif

  1. /* populator.c */
  2. #include <string.h>
  3. #include "populator.h"
  4. void set_unit(unit_t *p)
  5. {
  6. if (p == NULL)
  7. return; /* ignore NULL parameter */
  8. p->number = 3;
  9. strcpy(p->msg, "Hello World!");
  10. }


  1. /* main.c */
  2. #include <stdio.h>
  3. #include "populator.h"
  4. int main(void)
  5. {
  6. unit_t u;
  7. set_unit(&u);
  8. printf("number: %d\nmsg: %s\n", u.number, u.msg);
  9. return 0;
  10. }


二级指针的参数:

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

 

二级指针做传出参数

 

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

 

这里的参数指针是const char **,有const限定符,却不是传入参数而是传出参数。

 

  1. /* redirect_ptr.c */
  2. #include "redirect_ptr.h"
  3. static const char *msg[] ={"Sunday", "Monday", "Tuesday","Wednesday",
  4. "Thursday","Friday", "Saturday"};
  5. void get_a_day(const char **pp)
  6. {
  7. static int i = 0;
  8. *pp = msg[i%7];
  9. i++;
  10. }

  1. /* main.c */
  2. #include <stdio.h>
  3. #include "redirect_ptr.h"
  4. int main(void)
  5. {
  6. const char *firstday = NULL;
  7. const char *secondday = NULL;
  8. get_a_day(&firstday);
  9. get_a_day(&secondday);
  10. printf("%s\t%s\n", firstday, secondday);
  11. return 0;
  12. }

二级指针作为传出参数还有一种特别的用法,可以在函数中分配内存,调用者通过传出参数取得指向该内存的指针,一般来说,实现一个分配内存的函数就要实现一个释放内存的函数。

 

通过参数分配内存示例:

 

  1. void alloc_unit(unit_t **pp);
  2. 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中分配的内存

 

下面是一个通过二级指针参数分配内存的例子


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

  1. /* main.c */
  2. #include <stdio.h>
  3. #include "para_allocator.h"
  4. int main(void)
  5. {
  6. unit_t *p = NULL;
  7. alloc_unit(&p);
  8. printf("number: %d\nmsg: %s\n", p->number, p->msg);
  9. free_unit(p);
  10. p = NULL;
  11. return 0;
  12. }

二级指针参数如果是传出的,可以有两种情况:

第一种情况,传出的指针指向静态内存(比如上面的例子),或者指向已分配的动态内存(比如指向某个链表的节点);

第二种情况是在函数中动态分配内存,然后传出的指针指向这块内存空间,这种情况下调用者应该在使用内存之后调用释放内存的函数,调用者的责任是请求分配和请求释放内存,实现者的责任是完成分配内存和释放内存的操作。由于这两种情况的函数接口相同,我们在撰写文档或添加注释时应该说明是哪一种情况。

  

返回值是指针的情况

返回值显然是传出的而不是传入的,如果返回值传出的是指针,和通过参数传出指针的情况类似,也分为两种情况:

第一种是传出指向静态内存或已分配的动态内存的指针;

第二种是在函数中动态分配内存并传出指向这块内存的指针,这种情况通常还要实现一个释放内存的函数,所以有和malloc对应的free。由于这两种情况的函数接口相同,应该在文档中说明是哪一种情况。

 

返回指向已分配内存的指针示例:

 

unit_t *func(void);

其调用者与实现者之间的协议如下:

调用者

实现者

1、调用函数

2、将返回值保存下来以备后用

1、规定返回值指针的类型unit_t *

2、返回一个指针

 

 

下面的例子演示返回指向已分配内存的指针

 

  1. /* ret_ptr.h */
  2. #ifndef RET_PTR_H
  3. #define RET_PTR_H
  4. extern char *get_a_day(int idx);
  5. #endif
  1. /* ret_ptr.c */
  2. #include <string.h>
  3. #include "ret_ptr.h"
  4. static const char *msg[] = {"Sunday","Monday", "Tuesday", "Wednesday",
  5. "Thursday","Friday", "Saturday"};
  6. char *get_a_day(int idx)
  7. {
  8. return msg[idx];
  9. }
  1. /* main.c */
  2. #include <stdio.h>
  3. #include "ret_ptr.h"
  4. int main(void)
  5. {
  6. printf("%s %s\n", get_a_day(0));
  7. return 0;
  8. }


动态分配内存并返回指针示例:

 

unit_t *alloc_unit(void); voidfree_unit(unit_t *p);

其调用者与实现者之间的协议如下:

调用者

实现者

1、调用alloc_unit分配内存;

2、将返回值保存下来以备后用;

3、调用free_unit释放内存。

 

1、规定返回值指针的类型unit_t *;

2、alloc_unit分配内存并返回指向该内存的指针;

3、free_unit释放由alloc_unit分配的内存。

 

 

 

以下是一个完整动态分配内存并返回指针的例子

 

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


相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载