以将子函数设计成共别人使用的 API 的思想来考虑如何设计子函数
----------情景:主调函数需要调用子函数 f,函数f返回一段数据区
1、在主调函数中分配好内存,将地址传递给子函数f。这样做的好处是,内存的释放工作交给分配者,子函数不用管,保证子函数的模块独立性。
与之对应的不良编码习惯是,子函数分配内存,将这段内存的首地址返回给主调函数,这样做的坏处很明显,调用者很容易忘记释放这段内存,因为这段内存不是他分配的。
比如这个函数(见 man page):
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
调用者需要自己分配一段内存用于保存点分十进制形式的 IP 地址,然后将首地址传递给 *dst 。
2、在子函数中将这段数据区声明为 static
例如(见 man page):
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
struct hostent *gethostent(void);
这几个函数都会返回一个指向一个
struct hostent 的指针
struct hostent * ,这段数据区是在子函数里面声明的,如果是采用动态分配对内存,调用者很容易忘记释放,因为这段内存不是他分配的;但如果用一般的自动变量,函数返回之后这段内存为自动销毁,所以不可行。因此,设计者将它设计成 static ,这样如果多次调用同一子函数,使用的数据区始终只有一份,可以有效防止内存泄露。
3、声明一对函数,一个分配内存,另一个用来释放内存
例如(见 man page)
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
getaddrinfo() 返回一个链表(res),这个链表在子函数 getaddrinfo() 中生成,用 freeaddrinfo() 来释放这个链表。
----------情景:主调函数需要调用子函数 f,函数f返回一段数据区,但是这段数据区的大小事先不能确定,比如,要设计一个 DBMS ,主调函数传递查询条件(如 SQL 语句)给子函数,子函数返回查询结果集,很明显,这个查询结果集的大小事先无法确定,采用上面的第一种方法似乎行不通。
笔者想到了一种方法,就是调用者事先分配一小段内存,将指向这段内存的指针的地址(注意是指针的地址,即二级指针)传递给子函数,如果这段内存太小,可以在子函数中 realloc ,然后将主调函数中的指针指向 realloc 之后的内存。