当前位置: 首页 > 文档资料 > C 语言程序设计 >

函数接口的传入参数与传出参数

优质
小牛编辑
135浏览
2023-12-01

如果函数接口有指针参数,既可以把指针所指向的数据传给函数使用(称为传入参数),也可以由函数填充指针所指的内存空间,传回给调用者使用(称为传出参数),例如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];  
}  
​```c
/* 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;  
}

指针的输入输出特性

  • 指针做输出:被调用函数分配内存
  • 指针做输入:主调用函数 分配内存
int  getMem41(char **myp1/*out*/ , int *mylen1 /*out*/,  char **myp2 /*out*/, int *mylen2 /*out*/)
{
    int        ret = 0;
    char    *tmp1, *tmp2;

    tmp1 = (char *)malloc(100);
    strcpy(tmp1, "1132233");

    //间接赋值 
    *mylen1 = strlen(tmp1);  //1级指针
    *myp1 = tmp1; //2级指针的间接赋值

    tmp2 = (char *)malloc(200);
    strcpy(tmp2, "aaaaavbdddddddd");

    *mylen2 = strlen(tmp2);  //1级指针
    *myp2 = tmp2; //2级指针的间接赋值

    return ret;
}

char *  getMem42(int num)
{
    int        ret = 0;
    char    *tmp1;

    tmp1 = (char *)malloc(num);
    strcpy(tmp1, "1132233");

    return tmp1;
}

int  main44()
{
    int        ret = 0;
    char    *p1 = NULL;
    int        len1 = 0;
    char    *p2 = NULL;
    int        len2 = 0; 

    ret = getMem41(&p1, &len1, &p2, &len2);
    if (ret != 0)
    {
        printf("func getMem41() err:%d \n", ret);
        return ret;
    }

    printf("p1:%s \n", p1);
    printf("p2:%s \n", p2);

    if (p1 != NULL)
    {
        free(p1);
        p1 = NULL;
    }
    if (p2 != NULL)
    {
        free(p2);
        p2 = NULL;
    }

    p1 = getMem42(100);
    printf("p1:%s \n", p1);
    if (p1 != NULL)
    {
        free(p1);
        p1 = NULL;
    }

    printf("p1:%d \n", p1);
    system("pause");
    return ret;
}
//求文件中的两段话的长度
int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
    char *tmp1 = NULL;
    char *tmp2 = NULL;
    tmp1 = (char *)malloc(100);
    if (tmp1 == NULL)
    {
        return -1;
    }
    strcpy(tmp1, "abcdefg");
    *mylen1 = strlen(tmp1);
    *myp1 = tmp1; //间接修改实参p1的值

    tmp2 = (char *)malloc(100);
    if (tmp2 == NULL)
    {
        return -2;
    }
    strcpy(tmp2, "11122233333");
    *mylen2 = strlen(tmp2);
    *myp2 = tmp2; //间接修改实参p1的值
    return 0;
}

int getMem_Free(char **myp1)
{
    /*
    if (myp1 == NULL)
    {
        return ;
    }
    free(*myp1);  //释放完指针变量所指的内存空间
    *myp1 = NULL;  //把实参修改成nULL
    */
    char *tmp = NULL;
    if (myp1 == NULL)
    {
        return -1;
    }
    tmp = *myp1;
    free(tmp);  //释放完指针变量所指的内存空间
    *myp1 = NULL;  //把实参修改成nULL
    return 0;
}

void main11()
{
    char  *p1 = NULL;
    int len1 = 0;

    char *p2 = NULL;
    int len2 = 0;

    int ret = 0;

    ret  = getMem(&p1, &len1, &p2, &len2 );

    printf("p1: %s \n", p1);
    printf("p2: %s \n", p2);

    getMem_Free(&p1);
    getMem_Free(&p2);  

    system("pause");
    return ;
}

int getMem_Free0(char *myp1)
{
    if (myp1 == NULL)
    {
        return -1;
    }
    free(myp1);  //释放完指针变量所指的内存空间
    myp1 = NULL;
    return 0;
}

void main14()
{
    char  *p1 = NULL;
    int len1 = 0;

    char *p2 = NULL;
    int len2 = 0;

    int ret = 0;

    ret  = getMem(&p1, &len1, &p2, &len2 );

    printf("p1: %s \n", p1);
    printf("p2: %s \n", p2);

    if (p1 != NULL)
    {
        free(p1);
        p1 = NULL;
    }
    if (p2 != NULL)
    {
        free(p2);
        p2 = NULL;
    }
    //在被调用函数中把p1所指向的内存给释放掉 ,但是实参p1不能被修改成NULLL,有野指针现象
    getMem_Free0(p1);  
    getMem_Free0(p2);  

    system("pause");
    return ;
}