简体   繁体   English

通过strdup()和malloc()构造字符串之间是否存在实际差异?

[英]Are there any practical differences between constructing a string via strdup() and malloc()?

Suppose I want to write a function that will produce a string, and while I can set an upper limit on the size of the string I don't know in advance exactly how much space the string will take up. 假设我想编写一个会生成字符串的函数,虽然我可以设置字符串大小的上限,但我事先并不知道该字符串将占用多少空间。 I can think of two ways to arrange this: 我可以想到两种方法来安排这个:

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

The only difference I can see is that the second form will potentially waste a bit of memory: it uses 128 bytes for buffer2 for that variable's entire existence. 我能看到的唯一区别是第二种形式可能会浪费一点内存:它为buffer2使用128个字节来buffer2该变量的整个存在。 The first function uses 128 bytes for buffer1 plus whatever memory the string “actually” uses, but when buffer1 is removed from the stack the only memory that will be used is whatever the string actually needs. 第一个函数使用128个字节用于buffer1以及字符串“实际”使用的任何内存,但是当从堆栈中删除buffer1时,将使用的唯一内存是字符串实际需要的任何内容。

It looks like the first function would be better if the strings are long-lived and there will be a bunch of them. 看起来第一个函数会更好,如果字符串是长寿的并且会有一堆它们。 Are there any other reasons to prefer one of these forms over the other? 有没有其他理由偏爱这些形式中的一种而不是另一种形式? (This is an academic question; I'm not actually in a situation where using an extra 90 bytes makes a difference.) (这是一个学术问题;我实际上并没有使用额外的90个字节会产生影响。)

You can also use asprintf. 您也可以使用asprintf。 buffer will be allocated with the needed size... (This pointer should be passed to free() to release the allocated storage when it is no longer needed) 缓冲区将分配所需的大小...(此指针应传递给free()以在不再需要时释放已分配的存储空间)

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

    asprintf(&buffer, "temperature: %d; pressure: %g", temp, pres);                                                                                                                           
    return (buffer);                                                                                                                                                                          
}

If you are looking for minimum memory usage not knowing the length beforehand, the solution lies in a special usage of snprintf . 如果您正在寻找事先知道最小内存的最小内存使用率,那么解决方案就在于snprintf的特殊用法。 From the C11 standard: 从C11标准:

7.21.6.5 7.21.6.5

2. .. If n is zero, nothing is written, and s may be a null pointer... 2. ..如果n为零,则不写任何内容,s可能是空指针...

3. The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred... 3. snprintf函数返回已经写入的字符数已足够大,不计算终止空字符,如果发生编码错误则返回负值...

This means that if you write: 这意味着如果你写:

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

you will either get a negative value showing error (unlikely) or a positive value saying what would the ultimate size of the string be, without actually writing that string anywhere. 你会得到一个显示错误的负值(不太可能)或一个正值,说明字符串的最终大小是什么,而不是实际上在任何地方写入该字符串。

Therefore: 因此:

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;

Note that strdup and asprintf are not standard. 请注意, strdupasprintf不是标准的。 The former is a POSIX extension and the later is a GNU extension. 前者是POSIX扩展,后者是GNU扩展。


This solution will give you an unbounded string, so it's quite useful (you don't have to cut the string). 这个解决方案会给你一个无界的字符串,所以它非常有用(你不必剪切字符串)。 However, if you do want to cut the string, simply allocate a smaller memory if size was too big and use snprintf with the proper size to create the (cut) string in the buffer. 但是,如果您确实要剪切字符串,只需分配较小的内存(如果size太大)并使用具有适当大小的snprintf在缓冲区中创建(剪切)字符串。


If you want to be more secure and future proof as well as avoid repeating code, you can use a macro: 如果您想要更安全和未来证明以及避免重复代码,您可以使用宏:

#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

In fact, modifying this function a little bit to work with vsnprintf , you get an implementation of asprintf that you could use wherever needed. 实际上,稍微修改此函数以使用vsnprintf ,您将获得asprintf的实现,您可以在需要的地方使用它。

As to the question in title: strdup() uses malloc(). 至于标题中的问题:strdup()使用malloc()。 It means that after strdup you should use free(). 这意味着在strdup之后你应该使用free()。

As to the examples, the second function allocates memory without freeing, the first not, so forget the second. 至于这些例子,第二个函数在没有释放的情况下分配内存,第一个没有释放,所以忘记第二个。 Still, for the first function, you should free the functions result once you don't need it. 但是,对于第一个函数,一旦不需要它,你应该释放函数结果。

EDIT: the question was edited, so the answer must be edited too :) 编辑:问题已编辑,所以必须编辑答案:)

Before the question was edited, the second function ended with strdup(buffer2). 在编辑问题之前,第二个函数以strdup(buffer2)结束。 I meant in my answer, that the second function leacks memory allocated for buffer2. 在我的回答中,我的意思是第二个函数会泄漏为buffer2分配的内存。 Both functions, as they were then, returned address that should be freed afterwards, but the second would cause additional leack. 这两个函数,就像它们那样,返回应该在之后释放的地址,但第二个会导致额外的泄漏。

strdup calls malloc and memcpy internally. strdup内部调用mallocmemcpy So Function 1 has extra cost of calling memcpy function. 因此, 函数1具有调用memcpy函数的额外成本。

So Function 2 looks better option considering this reason along with reasons specified by @shahbaz. 因此,考虑到这个原因以及@shahbaz指定的原因, 功能2看起来更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM