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

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

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

int select(int nfds, fd_set *readfds,fd_set *writefds,
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)的参数。这些特殊规定应该在文档中写清楚。

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

/* populator.h */
#ifndef POPULATOR_H
#define POPULATOR_H
typedef struct {
int number;
char msg[20];
} unit_t;
extern void set_unit(unit_t *);
#endif

/* populator.c */
#include <string.h>
#include "populator.h"
void set_unit(unit_t *p)
{
if (p == NULL)
return; /* ignore NULL parameter */
p->number = 3;
strcpy(p->msg, "Hello World!");
}
/* main.c */
#include <stdio.h>
#include "populator.h"
int main(void)
{
unit_t u;
set_unit(&u);
printf("number: %d\nmsg: %s\n", u.number, u.msg);
return 0;
}

二级指针的参数:

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

二级指针做传出参数

/* 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;
}

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

通过参数分配内存示例:

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中分配的内存

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

/* 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;
}

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

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

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

返回值是指针的情况

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

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

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

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

unit_t *func(void);

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

调用者

实现者

1、调用函数

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

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

2、返回一个指针

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

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

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

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分配的内存。

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

/* ret_allocator.h */
#ifndef RET_ALLOCATOR_H
#define RET_ALLOCATOR_H typedef struct {
int number;
char *msg;
} unit_t;
extern unit_t *alloc_unit(void);
extern void free_unit(unit_t *); #endif
/* ret_allocator.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ret_allocator.h" unit_t *alloc_unit(void)
{
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!");
return p;
} void free_unit(unit_t *p)
{
free(p->msg);
free(p);
}
/* main.c */
#include <stdio.h>
#include "ret_allocator.h" int main(void)
{
unit_t *p = alloc_unit(); printf("number: %d\nmsg: %s\n", p->number, p->msg);
free_unit(p);
p = NULL;
return 0;
}

C语言的本质(16)——函数接口的传入参数与传出参数的更多相关文章

  1. JS中用apply、bind实现为函数或者类传入动态个数的参数

    为纪念10年没写blog,第一篇博文就以这样一个有趣的窍门开始吧 -___- 在ES5中,当我们调用一个函数时,如果要传入的参数是根据其他函数或条件判断生成的,也就是说不确定会传入多少个参数时,在不改 ...

  2. Java8函数接口实现回调及Groovy闭包的代码示例

    本文适用于想要了解Java8 Function接口编程及闭包表达式的筒鞋. 概述 在实际开发中,常常遇到使用模板模式的场景: 主体流程是不变的,变的只是其中要调用的具体方法. 其特征是:   Begi ...

  3. C语言的本质(15)——C语言的函数接口入门

    C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...

  4. C语言的本质(18)——函数的可变参数

    一般而言,在设计函数时会遇到许多数学和逻辑操作,是需要一些可变功能.例如,计算数字串的总和.字符串的联接或其它操作过程. 实现一个函数,要求在函数中计算传入的所有参数之和,并输出到屏幕上.这个函数实现 ...

  5. C语言的本质(12)——指针与函数

    往往,我们一提到指针函数和函数指针的时候,就有很多人弄不懂.下面详细为大家介绍C语言中指针函数和函数指针. 1.指针函数 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需 ...

  6. 【Go语言学习笔记】函数做参数和闭包

    函数做参数 在Go语言中,函数也是一种数据类型,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型.类似于重写(同名覆盖). 回调函数:函数有一个参数是函数类型,这个 ...

  7. IO端口和IO内存的区别及分别使用的函数接口

    每个外设都是通过读写其寄存器来控制的.外设寄存器也称为I/O端口,通常包括:控制寄存器.状态寄存器和数据寄存器三大类.根据访问外设寄存器的不同方式,可以把CPU分成两大类.一类CPU(如M68K,Po ...

  8. C语言的本质(7)——C语言运算符大全

    C语言的本质(7)--C语言运算符大全 C语言的结合方向 C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左).例如算术运算符的结合性是自左至右,即先左后右.如有表达式 x- ...

  9. C语言的本质(4)——浮点数的本质与运算

    C语言的本质(4)--浮点数的本质与运算 C语言规定了3种浮点数,float型.double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长 ...

随机推荐

  1. FAT16文件系统简介

    有必要说明一下,以下对FAT16系统的介绍,很多都是参考文献.由于FAT16系统一般在U盘.MMC卡.SD卡以及一些小型存储设备上使用比较多,以后把这些小型存储设备统称为存储卡,这里仅局限于对存储卡的 ...

  2. USB系列之一:列出你的USB设备

    USB现在已经成为PC机必不可少的接口之一,几乎所有的设备都可以接在USB设备上,USB键盘.鼠标.打印机.摄像头,还有常用的U盘等等,从本篇文章开始,将集中篇幅介绍一下在DOS中使用USB设备的方法 ...

  3. keil C51 指针总结

    变量就是一种在程序执行过程中其值能不断变化的量.要在程序中使用变量必须先用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间.定义一个变量的格式如下: [存储种 ...

  4. SQL Server 2008空间数据应用系列七:基于Bing Maps(Silverlight) 的空间数据展现

    原文:SQL Server 2008空间数据应用系列七:基于Bing Maps(Silverlight) 的空间数据展现 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft ...

  5. 使用XAML在WPF项目中承载ArcGIS Engine地图控件开发

    原文 http://blog.csdn.net/flexmapserver/article/details/5868882 用Windows Form进行ArcGIS Engine二次开发时常见的形式 ...

  6. mybatis写demo时遇到的问题

    写demo的时候,用mybatis的配置文件链接数据库,始终链接不上,太急人了.仔细查阅,发现在mysql中新增的表没有事务支持.还有就是mysql搜索引擎支持的不对.我换了一下 innodb的引擎, ...

  7. httrack,webdup,WinHTTrack,WebZip

    怎么下载摄像头游戏jabbo,并使其能离线运行?修改 1.摄像头游戏jabbo:JABBO Ultimatum by LiveMurals Interactive电脑为:windows 7 32位.试 ...

  8. ###Android 断点调试和高级调试###

    转自:http://www.2cto.com/kf/201506/408358.html 有人说Android 的调试是最坑的,那我只能说是你不会用而已,我可以说Android Studio的调试是我 ...

  9. qsort排序算法

      七种qsort排序方法 <本文中排序都是采用的从小到大排序> 一.对int类型数组排序 int num[100]; Sample: int cmp ( const void *a ,  ...

  10. HDU5437 Alisha’s Party (优先队列 + 模拟)

    Alisha’s Party Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...