我有一个给出两个整数的函数并返回一个字符串。现在我有这个:
char* myfunc( int a, int b, int* len )
{
int retLen = ...
char* ret = malloc( retLen + 1 );
if ( len != NULL )
{
*len = retLen;
}
return ret;
}
但是,C库中的大多数函数往往更像是:
int myfunc( char* ret, int a, int b )
{
...
return retLen;
}
然后,您需要为要填充的函数分配内存。这使您可以更多地选择分配字符串的位置。
在这种情况下,尽管函数中需要一些数学来获取长度,并且没有理由拥有除所需大小之外的任何大小的缓冲区。缓冲区的大小没有上限(不管是否合理)。
在返回给定输入时动态找到长度的字符串时,什么是良好的做法?
答案 0 :(得分:7)
我在内核模式程序中看到的模式是:
样品:
int myfunc(
__out char* output,
__in size_t given,
__out size_t needed_or_resulted,
extra params ...
){
... implementation
}
needed_or_resulted
也可用于传输在成功的情况下使用了多少给定内存。
用作:
int result = myfunc(output, given, needed_or_resulted, extra params ...);
if(result == OK) {
// all ok, do what you need done with result of size "needed_or_resulted" on "output"
} else if(result == ERROR_NOT_ENOUGH_MEMORY) {
output = malloc(needed ...
result = myfunc(output, given, needed_or_resulted, extra params ...);
if(result == OK) {
// all ok, do what you need done with result of size "needed_or_resulted" on "output"
} else if(result == ERROR_OTHER) {
// handle other possible errors
} else {
// handle unknown error
}
} else if(result == ERROR_OTHER) {
// handle other possible errors
} else {
// handle unknown error
}
答案 1 :(得分:3)
后者更好,因为它在调用者中提供了关于谁负责释放内存的线索。如果调用者和被调用者使用不同的malloc实现(例如在Windows上,调试和发布经常使用不兼容的内存模型),前者会导致大问题。
答案 2 :(得分:2)
您对int myfunc( char* ret, int a, int b )
签名是首选的原因是正确的。它实际上解释了另一件事 - 为什么需要返回长度(缓冲区的大小为MAX
,因此我们通常需要通知调用者我们实际使用了多少)。
在函数内部分配字符串时,通常不会返回字符串的大小,因为可以使用strlen
来查找字符串。查看strdup
以获取动态分配字符串的函数示例。所以我会将你的功能签名改为
char* myfunc( int a, int b) {
...
}
答案 3 :(得分:2)
按照snprintf
的界面,标准函数具有完全相同的问题:
size_t myfunc(char *s, size_t n, int a, int b);
超出n-1的输出字节应被丢弃而不是被丢弃 写入数组,并在结尾写入空字节 实际上写入数组的字节。
成功完成后,snprintf()函数将返回字节数 写入s的n已足够大,不包括终止空值>字节。
如果在调用snprintf()时n的值为零,则不会有任何内容 写入后,已写入的字节数为n 足够大,不包括终止空值,应退还, 和s可能是一个空指针。
典型用法:
size_t needed = myfunc(0, 0, a, b) + 1;
char *buf = malloc(needed);
if (buf) {
myfunc(buf, needed, a, b);
}
您可以在返回的计数中包含nul字节 - 它使调用代码更简单,虽然对于习惯标准snprintf
的人来说稍微不那么熟悉。
如果计算retLen
的成本非常高,那么可能会有一个函数在生成字符串时计算它的参数,并返回一个正确大小的分配缓冲区(可能有realloc
方式)。但我通常不会考虑它。为了方便想要分配的用户,只需将上面的代码放在函数myfunc_alloc
中,别担心它会复制一些工作。已有缓冲区的用户可以直接调用myfunc
:
if (myfunc(buf, bufsize, a, b) >= bufsize) {
printf("buffer too small, string (%s) has been truncated\n", buf);
}
答案 4 :(得分:1)
我会通过引用传递char *
。如果函数成功运行,则分配字符串并将其分配给指针引用并返回字符串的长度。如果遇到错误,请设置errno并返回-1。
int myfunc( int a, int b, char ** str)
{
int retLen;
/* code to calculate string length required */
if (!(str))
{
errno = EINVAL;
return(-1);
};
if (!(*str = malloc(retLen)))
return(-1);
/* calculate new value and store to string */
return(retLen);
}