3

假设我想编写一个生成字符串的函数,虽然我可以设置字符串大小的上限,但我事先并不知道字符串将占用多少空间。我可以想到两种安排方式:

char *ParametersAsString_1(int temperature, float pressure)
{
    char buffer1[128];
    snprintf(buffer1, 128, "temperature: %d; pressure: %g",
        temperature, pressure);
    return strdup(buffer1);
}

char *ParametersAsString_2(int temperature, float pressure)
{
    char *buffer2 = malloc(128);
    snprintf(buffer2, 128, "temperature: %d; pressure: %g",
        temperature, pressure);
    return buffer2;
}

我能看到的唯一区别是第二种形式可能会浪费一点内存:它使用 128 个字节来buffer2保证该变量的整个存在。第一个函数使用 128 字节buffer1加上字符串“实际”使用的内存,但是当buffer1从堆栈中删除时,唯一使用的内存就是字符串实际需要的内存。

如果字符串是长寿命的并且会有一堆字符串,那么第一个函数看起来会更好。是否有任何其他理由更喜欢其中一种形式而不是另一种?(这是一个学术问题;我实际上并不处于使用额外的 90 个字节会产生影响的情况。)

4

4 回答 4

4

您也可以使用 asprintf。缓冲区将分配所需的大小...(此指针应传递给 free() 以在不再需要时释放分配的存储空间)

char*   ParametersAsString_3(int temp, float pres) {                                                                                                                                        
    char* buffer;                                                                                                                                                                         

    asprintf(&buffer, "temperature: %d; pressure: %g", temp, pres);                                                                                                                           
    return (buffer);                                                                                                                                                                          
}
于 2013-06-13T15:38:01.530 回答
4

如果您在事先不知道长度的情况下寻找最小内存使用量,则解决方案在于特殊使用snprintf. 来自 C11 标准:

7.21.6.5

2. .. 如果 n 为零,则不写入任何内容,并且 s 可能是空指针...

3. snprintf 函数返回如果 n 足够大,将写入的字符数,不计算终止的空字符,如果发生编码错误,则返回负值...

这意味着如果你写:

int size = snprintf(NULL, 0, "format string", arguments);

您将得到一个显示错误的负值(不太可能)或一个正值,说明字符串的最终大小是多少,而无需在任何地方实际写入该字符串。

所以:

int size = snprintf(NULL, 0, "temperature: %d; pressure: %g", temperature, pressure);
/* error checking */
char *str = malloc((size + 1) * sizeof(*str));
/* error checking */
sprintf(str, "temperature: %d; pressure: %g", temperature, pressure);
return str;

请注意strdupasprintf不是标准的。前者是 POSIX 扩展,后者是 GNU 扩展。


这个解决方案会给你一个无界的字符串,所以它非常有用(你不必剪掉字符串)。但是,如果您确实想要剪切字符串,只需分配一个较小的内存(如果size太大)并使用snprintf适当的大小在缓冲区中创建(剪切)字符串。


如果您想更加安全和面向未来以及避免重复代码,您可以使用宏:

#define YOUR_LIB_YOUR_FUNC_NAME_SNPRINTF(s, n)               \
             snprintf(s, n, "temperature: %d; pressure: %g", \
             temperature, pressure)

int size = YOUR_LIB_YOUR_FUNC_NAME_SNPRINTF(NULL, 0);
/* error checking */
char *str = malloc((size + 1) * sizeof(*str));
/* error checking */
YOUR_LIB_YOUR_FUNC_NAME_SNPRINTF(str, size + 1)
return str;

#undef YOUR_LIB_YOUR_FUNC_NAME_SNPRINTF

事实上,稍微修改一下这个函数vsnprintf,你就可以得到一个asprintf可以在任何需要的地方使用的实现。

于 2013-06-13T15:48:23.943 回答
1

至于标题中的问题:strdup() 使用 malloc()。这意味着在strdup之后你应该使用free()。

至于示例,第二个函数分配内存而不释放,第一个没有,所以忘记第二个。不过,对于第一个函数,您应该在不需要时释放函数结果。

编辑:问题已被编辑,所以答案也必须被编辑:)

在编辑问题之前,第二个函数以 strdup(buffer2) 结束。我的意思是,第二个函数泄漏了为 buffer2 分配的内存。这两个函数都返回了应该在之后释放的地址,但第二个函数会导致额外的泄漏。

于 2013-06-13T15:31:41.143 回答
0

strdup电话mallocmemcpy内部。所以函数 1有调用memcpy函数的额外成本。

因此,考虑到这个原因以及@shahbaz 指定的原因,函数 2看起来是更好的选择。

于 2013-06-13T15:59:04.193 回答