C语言 sprintf() 与自动内存分配?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3774417/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
sprintf() with automatic memory allocation?
提问by the-shamen
I'm searching for a sprintf()-like implementation of a function that automatically allocates required memory. So I want to say
我正在寻找一个类似于 sprintf() 的自动分配所需内存的函数的实现。所以我想说
char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s nice %05d string", a, b, c, d );
and my_str retrieves the adress of an allocated memory that holds the result of this sprintf().
并且 my_str 检索保存此 sprintf() 结果的已分配内存的地址。
In another forum, I read that this can be solved like this:
在另一个论坛上,我读到可以这样解决:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
int numbytes;
numbytes = sprintf( (char*)NULL, "%s %d %s!", a, c, b );
printf( "numbytes = %d", numbytes );
ret = (char*)malloc( ( numbytes + 1 ) * sizeof( char ) );
sprintf( ret, "%s %d %s!", a, c, b );
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
But this immediatelly results in a segfault when the sprintf() with the NULL-pointer is invoked.
但是,当调用带有 NULL 指针的 sprintf() 时,这会立即导致段错误。
So any idea, solution or tips? A small implementation of a sprintf()-like parser that is placed in the public domain would already be enought, then I could get it myself done.
那么有什么想法,解决方案或提示吗?放置在公共域中的类似 sprintf() 的解析器的一个小实现已经足够了,然后我可以自己完成它。
Thanks a lot!
非常感谢!
回答by Tarun
Here is the original answer from Stack Overflow. As others have mentioned, you need snprintfnot sprintf. Make sure the second argument to snprintfis zero. That will prevent snprintffrom writing to the NULLstring that is the first argument.
这是Stack Overflow的原始答案。正如其他人所说,你需要的snprintf不是sprintf。确保的第二个参数snprintf是zero。这将阻止snprintf写入NULL作为第一个参数的字符串。
The second argument is needed because it tells snprintfthat enough space is not available to write to the output buffer. When enough space is not available snprintfreturns the number of bytes it would have written, had enough space been available.
需要第二个参数是因为它snprintf表明没有足够的空间可用于写入输出缓冲区。当没有足够的空间可用时,snprintf返回它会写入的字节数,如果有足够的空间可用。
Reproducing the code from that link here ...
从此处复制该链接的代码...
char* get_error_message(char const *msg) {
size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno) + 1;
char *buffer = malloc(needed);
sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
return buffer;
}
回答by Mike Axiak
GNU and BSD have asprintfand vasprintf that are designed to do just that for you. It will figure out how to allocate the memory for you and will return null on any memory allocation error.
GNU 和 BSD 有asprintf和 vasprintf 专为您而设计。它将弄清楚如何为您分配内存,并在任何内存分配错误时返回 null。
asprintf does the right thing with respect to allocating strings -- it first measures the size, then it tries to allocate with malloc. Failing that, it returns null. Unless you have your own memory allocation system that precludes the use of malloc, asprintf is the best tool for the job.
asprintf 在分配字符串方面做了正确的事情——它首先测量大小,然后尝试使用 malloc 进行分配。如果失败,它将返回 null。除非您有自己的内存分配系统来阻止使用 malloc,否则 asprintf 是完成这项工作的最佳工具。
The code would look like:
代码如下所示:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
ret = asprintf( "%s %d %s!", a, c, b );
if (ret == NULL) {
fprintf(stderr, "Error in asprintf\n");
return 1;
}
printf( "ret = >%s<\n", ret );
free( ret );
return 0;
}
回答by Pavel ?imerda
If you can live with GNU/BSD extentions, the question is already answered. You can use asprintf()(and vasprintf()for building wrapper functions) and be done.
如果您可以接受 GNU/BSD 扩展,那么问题已经得到解答。您可以使用asprintf()(并vasprintf()用于构建包装函数)并完成。
But snprintf()and vsnprintf()are mandated by POSIX, according to the manpage, and the latter can be used to build up your own simple version of asprintf()and vasprintf().
但是snprintf()andvsnprintf()是 POSIX 强制要求的,根据联机帮助页,后者可用于构建您自己的asprintf()and的简单版本vasprintf()。
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap1;
size_t size;
char *buffer;
va_copy(ap1, ap);
size = vsnprintf(NULL, 0, fmt, ap1) + 1;
va_end(ap1);
buffer = calloc(1, size);
if (!buffer)
return -1;
*strp = buffer;
return vsnprintf(buffer, size, fmt, ap);
}
int
asprintf(char **strp, const char *fmt, ...)
{
int error;
va_list ap;
va_start(ap, fmt);
error = vasprintf(strp, fmt, ap);
va_end(ap);
return error;
}
You can do some preprocessor magic and use your versions of functions only on systems that don't support them.
您可以执行一些预处理器魔术,并仅在不支持它们的系统上使用您的函数版本。
回答by Jerry Coffin
- If possible, use
snprintf-- it gives an easy way to measure the size of data that would be produced so you can allocate space. - If you reallycan't do that, another possibility is printing to a temporary file with
fprintfto get the size, allocate the memory, and then use sprintf.snprintfis definitelythe preferred method though.
- 如果可能,请使用
snprintf-- 它提供了一种测量将产生的数据大小的简单方法,以便您可以分配空间。 - 如果你真的做不到,另一种可能是打印到一个临时文件
fprintf以获取大小,分配内存,然后使用 sprintf。snprintf是肯定虽然优选的方法。
回答by Paul Kuliniewicz
The GLiblibrary provides a g_strdup_printffunction that does exactly what you want, if linking against GLib is an option. From the documentation:
该GLib库库提供了一个g_strdup_printf功能,你想要做什么,如果链接到的GLib的是一种选择。从文档:
Similar to the standard C
sprintf()function but safer, since it calculates the maximum space required and allocates memory to hold the result. The returned string should be freed withg_free()when no longer needed.
与标准 C
sprintf()函数类似但更安全,因为它计算所需的最大空间并分配内存来保存结果。返回的字符串应该在g_free()不再需要时释放。
回答by jose miguel parra tomas
/* casprintf print to allocated or reallocated string
char *aux = NULL;
casprintf(&aux,"first line\n");
casprintf(&aux,"seconde line\n");
printf(aux);
free(aux);
*/
int vcasprintf(char **strp,const char *fmt,va_list ap)
{
int ret;
char *strp1;
char *result;
if (*strp==NULL)
return vasprintf(strp,fmt,ap);
ret=vasprintf(&strp1,fmt,ap); // ret = strlen(strp1) or -1
if (ret == -1 ) return ret;
if (ret==0) {free(strp1);return strlen(*strp);}
size_t len = strlen(*strp);
*strp=realloc(*strp,len + ret +1);
memcpy((*strp)+len,strp1,ret+1);
free(strp1);
return(len+ret);
}
int casprintf(char **strp, const char *fmt, ...)
{
int ret;
va_list ap;
va_start(ap,fmt);
ret =vcasprintf(strp,fmt,ap);
va_end(ap);
return(ret);
}
回答by John Haxby
POSIX.1 (aka IEEE 1003.1-2008) provides open_memstream:
POSIX.1(又名 IEEE 1003.1-2008)提供 open_memstream:
char *ptr;
size_t size;
FILE *f = open_memstream(&ptr, &size);
fprintf(f, "lots of stuff here\n");
fclose(f);
write(1, ptr, size); /* for example */
free(ptr);
open_memstream(3) is available on at least Linux and macOS and has been for some years. The converse of open_memstream(3) is fmemopen(3) which makes the contents of a buffer available for reading.
open_memstream(3) 至少在 Linux 和 macOS 上可用,并且已经存在多年了。open_memstream(3) 的反面是 fmemopen(3),它使缓冲区的内容可供读取。
If you just want a single sprintf(3) then the widely-implemented but non-standard asprintf(3) might be what you want.
如果您只想要一个 sprintf(3),那么广泛实施但非标准的 asprintf(3) 可能就是您想要的。

