以以下几行为例:
char a = 'a';
char b[] = "bc";
char res[2];
sprintf(res, "%c%s", a, b);
printf("%s", res);
我想做的是能够将sprintf调用保存在res ("abc")中的字符串传递给其他函数调用(在这种情况下为printf),而不首先将其保存在res中。或者,作为替代方案,在显式地将其写入内存之前知道它将占用多少内存,因此我可以首先分配所需的内存(这个例子不起作用,因为res不够大)。我不需要特别使用sprintf,只要任何可以产生字符串和字符组合的字符串,或者任何其他C类型。实现上述任何一种最简单的方法是什么,或者我能做的最接近的事情是什么?
我认为我可以在执行之前找到特定sprintf调用的结果字符串的长度,因为我知道参数以及它们将如何影响结果字符串的长度。但是在这种情况下,我应该对不同的调用做不同的计算。这是唯一的办法吗?
没有标准函数可以做到这一点。但是,实现您自己的方法相当简单。下面是一个简单的例子:
#include <stdio.h>
#include <stdarg.h>
char *ssprintf(const char *fmt, ...)
{
static char tmpbuf[100];
va_list argp;
va_start(argp, fmt);
vsnprintf(tmpbuf, 100, fmt, argp);
va_end(argp);
return tmpbuf;
}
int main()
{
char a = 'a';
char b[] = "bc";
printf("%sn", ssprintf("%c%s", a, b));
}
就像任何一个"wrap "printf
,这个ssprintf
函数必须声明一个va_list
,调用va_start
,然后调用一个&;printf函数的变体,在本例中为vsnprintf
.
和任何返回字符串的函数一样,分配返回的字符串也是有问题的。这个实现采用了使用static
局部数组的最快、最简单、但也是最俗气的方法。(通过使用snprintf,如果调用者试图打印更大的东西,它至少可以避免溢出固定大小的缓冲区。)
一个更好的方法是调用vsnprintf
两次,一次计算结果长度,一次实际构造它,然后使用malloc
动态分配一个足够大的缓冲区。在支持乌克兰的回答中详细介绍了这种技术。然而,对于这种技术,存在决定何时释放缓冲区的问题。
另一种可能是使用非标准但非常有用的asprintf
函数(或者在这种情况下您实际上使用vasprintf
),它会自动分配一个缓冲区并向其打印。
如前所述,在C中编写返回字符串的函数总是有问题,这是由于为返回字符串正确分配内存的子问题。关于这个问题可能有一个规范的SO问题,或者参见C常见问题列表,问题7.5b。
练习:对于如图所示的ssprintf
函数,如果在一行中调用它两次,并尝试像这样打印两个结果,会发生什么?为什么会发生这种情况?printf("%s%sn",
ssprintf("%c%d", a, 42),
ssprintf("%s%f", b, 56.75));
我可以创建一个字符串组合变量的各种值(作为字符串),并通过它作为参数而不分配它第一?
简短的回答是:不
C语言中的字符串(即以零结尾的字符数组)不是通过将所有单个字符传递给函数来传递给函数的。相反,传递的是指向字符串第一个字符的指针。因此,该字符串必须位于内存中的某个位置。
some_function("Hello world");
看起来好像"Hello world"传递而不存储在内存中。然而,编译器会自动处理它,以确保字符串(literal) "Hello World">
传递运行时构造的字符串时,必须先将其保存到内存中。
或者,作为一种选择,在显式地将其写入内存之前知道它将占用多少内存…
可能不是最好的解决方案,但你可以使用snprintf
两次。第一次没有实际存储结果,而只是获得所需的内存量。
char a = 'a';
char b[] = "bc";
int required = snprintf(NULL, 0, "%c%s", a, b); // Size is zero to suppress
// writing to buffer
char res[required + 1]; // Here a VLA but could be dynamic allocation instead
sprintf(res, required + 1, "%c%s", a, b);
printf("%s", res);
基于@SteveSummit (https://stackoverflow.com/a/75991030/4386427)的答案,你可以编写一个函数来创建字符串并将其存储在动态分配的内存中。比如:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char* dyn_sprintf(const char *fmt, ...)
{
va_list argp;
// Find size
va_start(argp, fmt);
int sz = vsnprintf(NULL, 0, fmt, argp);
va_end(argp);
// Dynamic allocation
char* buf = malloc(sz + 1); // +1 for termination character
if (buf == NULL) exit(1);
// Create the string
va_start(argp, fmt);
vsnprintf(buf, sz + 1, fmt, argp);
va_end(argp);
return buf;
}
int main()
{
char* str;
str= dyn_sprintf("Hello %s%d!", "world", 42);
puts(str);
free(str);
str= dyn_sprintf("%.20f-%d-%c-%s!", 3.14, 42, '@', "xyz");
puts(str);
free(str);
return 0;
}
输出:
Hello world42!
3.14000000000000012434-42-@-xyz!