正确的方法从函数返回一个字符串

时间:2011-12-22 13:35:44

标签: c string

我有一个给出两个整数的函数并返回一个字符串。现在我有这个:

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

然后,您需要为要填充的函数分配内存。这使您可以更多地选择分配字符串的位置。

在这种情况下,尽管函数中需要一些数学来获取长度,并且没有理由拥有除所需大小之外的任何大小的缓冲区。缓冲区的大小没有上限(不管是否合理)。

在返回给定输入时动态找到长度的字符串时,什么是良好的做法?

5 个答案:

答案 0 :(得分:7)

我在内核模式程序中看到的模式是:

  1. 你调用函数一次,如果碰巧有一些可用的内存,或者如果碰巧没有可用的那么调用null;
  2. 如果你分配了内存并且函数找到了足够的内容,它会将结果放入该内存并返回OK
  3. 如果您没有传入内存,或者传递的内存太少,则该函数返回ERROR_NOT_ENOUGH_MEMORY,并将输出参数输入所需的内存。
    • 然后分配所需的内存并再次调用该函数
  4. 样品:

    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);
}