我有一个像这样的身体的功能A
char* A (const char* arg) {
char ret[8192];
B(ret);
return strdup(ret);
}
函数B看起来像这样(为简洁起见,迭代逻辑上有一些伪代码)
void B(char* ret) {
char retString[8192];
while(ITERATIONS_LEFT) {
snprintf(returnString, 8192, "\n Format %s\n\n", IT_VALUE);
snprintf(returnString, 8192, "\n Val %s\n\n", IT_VALUE_2);
}
strcpy(ret, returnString);
}
所以基本上我有一个函数A给另一个函数B一个字符串缓冲区,供B输入格式化数据。现在这个工作正常,只要从迭代返回的总数据不超过8196(只是猜测一个足够大的'值),但我认为如果我可以动态地这样做而不是更好不得不担心我的缓冲区填充的情况。如何以一种相当有效的方式实现这一点,功能A必须仍然可以调用函数B的约束,并且B的签名可以改变,但A不能?
答案 0 :(得分:1)
除了你的分配问题,你在这里覆盖相同的字符串:
snprintf(returnString, 8192, "\n Format %s\n\n", IT_VALUE);
snprintf(returnString, 8192, "\n Val %s\n\n", IT_VALUE_2);
你可以通过一种“appender”来解决这个问题,它通过将Joachim Pileborg建议的大小0传递给snprintf
来确定所需的长度,从而根据需要重新分配内存:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
struct append_t {
char *str; /* string */
size_t len; /* length of string */
size_t size; /* allocated size */
};
void append(struct append_t *app, const char *fmt, ...)
{
va_list arg;
size_t len;
va_start(arg, fmt);
len = vsnprintf(NULL, 0, fmt, arg);
va_end(arg);
while (app->len + len + 1 >= app->size) {
app->size = app->size ? app->size * 2 : 0x100;
app->str = realloc(app->str, app->size);
// Check and handle error
}
va_start(arg, fmt);
len = vsnprintf(app->str + app->len, app->size - app->len, fmt, arg);
va_end(arg);
app->len += len;
}
int main(int argc, char **argv)
{
struct append_t app = {NULL};
for (int i = 1; i < argc; i++) {
if (i > 1) append(&app, ", ");
append(&app, "'%s'", argv[i]);
}
if (app.str) puts(app.str);
free(app.str);
return 0;
}
注意事项:
realloc(NULL, size)
表现得像malloc(size)
的事实。必须将appender初始化为全零。vsnprintf
是snprintf
的变体,它采用va_list
而不是可变参数。 v...printf
函数允许您编写自己的printf
函数。您无法将可变参数传递给其他函数,您必须使用va_list
标题中的va_...
宏创建<stdarg.h>
。printf
函数的打印格式和参数之间的不匹配。如果您希望通过这些功能检查获益,则可以使用相应的GCC属性((format(printf, 2, 3))
或SAL注释_Printf_format_string_
。在您的示例中,A
会创建appender并将其传递给B
,然后返回其.str
。您还可以从B
返回一个appender,并从.str
返回A
。
答案 1 :(得分:1)
我建议使用以下版本的A
和B
。
char* A (const char* arg) {
int size = 8192;
char *ret = malloc(size);
B(ret, size);
return ret;
}
void B(char* ret, int size) {
int pos = 0, required;
while(ITERATIONS_LEFT) {
required = snprintf(NULL, 0, "\n Format %s\n\n", IT_VALUE);
if (pos + required >= size) {
size *= 2;
ret = realloc(ret, size);
}
pos += sprintf(ret + pos, "\n Format %s\n\n", IT_VALUE);
required = snprintf(NULL, 0, "\n Val %s\n\n", IT_VALUE_2);
if (pos + required >= size) {
size *= 2;
ret = realloc(ret, size);
}
pos += sprintf(ret + pos, "\n Val %s\n\n", IT_VALUE_2);
}
}
请注意:
strdup
或strcpy
)snprintf
时,都会覆盖您的B缓冲区版本。写入位置(pos
)更新为追加(null终止char
覆盖)snprintf
参数的NULL
将返回所需的缓冲区大小,而无需在任何地方打印任何内容ret
后检查NULL
是否为realloc
ret
不是NULL
,则确定缓冲区足够大。因此,简单的sprintf
用于实际打印。free
缓冲区!