我正在寻找一个类似
sprintf()
的函数实现,可以自动分配所需的内存。所以我想说
char *my_str = dynamic_sprintf("Hello %s, this is a %.*s nice %05d string", a, b, c, d);
并且
my_str
接收已分配内存块的地址,该内存块保存此 sprintf()
的结果。
在另一个论坛中,我读到这可以这样解决:
#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;
}
但是,当调用带有空指针的
sprintf()
时,这会立即导致段错误。
有什么想法、解决方案或技巧吗?放置在公共领域的类似
sprintf()
的解析器的小型实现就已经足够了,然后我可以自己完成它。
非常感谢!
这是原始答案来自 Stack Overflow。正如其他人提到的,您需要
snprintf
而不是 sprintf
。确保 snprintf
的第二个参数是 zero
。这将阻止 snprintf
写入第一个参数 NULL
字符串。
需要第二个参数,因为它告诉
snprintf
没有足够的空间可用于写入输出缓冲区。当没有足够的空间可用时,snprintf
返回如果有足够的空间可用,它将写入的字节数。
从此处复制该链接的代码...
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;
}
GNU 和 BSD 有 asprintf 和
vasprintf
旨在为您做到这一点。它将弄清楚如何为您分配内存,并在任何内存分配错误时返回 null。
asprintf
在分配字符串方面做了正确的事情——它首先测量大小,然后尝试使用 malloc
进行分配。如果失败,则返回 null。除非您有自己的内存分配系统,无法使用 malloc
,否则 asprintf
是完成这项工作的最佳工具。
代码如下所示:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
int err = asprintf(&ret, "%s %d %s!", a, c, b );
if (err == -1) {
fprintf(stderr, "Error in asprintf\n");
return 1;
}
printf("ret = >%s<\n", ret);
free(ret);
return 0;
}
如果您可以接受 GNU/BSD 扩展,那么问题就已经得到解答。您可以使用
asprintf()
(和 vasprintf()
来构建包装函数)并完成。
但是根据手册页,
snprintf()
和vsnprintf()
是POSIX强制要求的,后者可用于构建您自己的asprintf()
和vasprintf()
的简单版本。
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap1;
int len;
char *buffer;
int res;
va_copy(ap1, ap);
len = vsnprintf(NULL, 0, fmt, ap1);
if (len < 0)
return len;
va_end(ap1);
buffer = malloc(len + 1);
if (!buffer)
return -1;
res = vsnprintf(buffer, len + 1, fmt, ap);
if (res < 0)
free(buffer);
else
*strp = buffer;
return res;
}
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;
}
您可以使用一些预处理器魔法,并仅在不支持它们的系统上使用您的函数版本。
snprintf
——它提供了一种简单的方法来测量将生成的数据的大小,以便您可以分配空间。fprintf
打印到临时文件以获取大小,分配内存,然后使用sprintf。 snprintf
绝对是首选方法。GLib 库提供了
g_strdup_printf
函数,如果可以选择链接到 GLib,它可以完全满足您的需求。 来自文档:
与标准C类似
功能但更安全,因为它 计算所需的最大空间 并分配内存来保存 结果。返回的字符串应该是 当不再存在时,用sprintf()
释放 需要。g_free()
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) 至少在 Linux 和 macOS 上可用,并且已经存在多年了。 open_memstream(3) 的逆过程是 fmemopen(3),它使缓冲区的内容可供读取。
如果您只想要一个 sprintf(3),那么广泛实现但非标准的 asprintf(3) 可能就是您想要的。
/* 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);
}
我的版本 (v3) https://stackoverflow.com/a/10388547/666907 – 更通用一点 [注意我是 C++ 菜鸟,使用风险自担 :P]:
#include <cstdarg>
char* myFormat(const char* const format...) {
// `vsnprintf()` changes `va_list`'s state, so using it after that is UB.
// We need the args twice, so it is safer to just get two copies.
va_list args1;
va_list args2;
va_start(args1, format);
va_start(args2, format);
size_t needed = 1 + vsnprintf(nullptr, 0, format, args1);
// they say to cast in C++, so I cast…
// https://stackoverflow.com/a/5099675/666907
char* buffer = (char*) malloc(needed);
vsnprintf(buffer, needed, format, args2);
va_end(args1);
va_end(args2);
return buffer;
}
char* formatted = myFormat("Foo %s: %d", "bar", 456);
Serial.println(formatted); // Foo bar: 456
free(formatted); // remember to free or u will have a memory leak!