【问题标题】:Dynamic formatted string from another function来自另一个函数的动态格式化字符串
【发布时间】:2016-05-30 11:04:53
【问题描述】:

我有一个像这样的身体的函数 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 不能更改的约束下,我如何以相当有效的方式实现这一点?

【问题讨论】:

  • 与您当前的问题无关,但您正在释放A函数分配的内存?
  • 要使用动态内存 (malloc),您需要事先知道大小(使用 strlen("LITERALS") + strlen(values) + 1 计算)
  • 更多与您的问题相关,来自this snprintf (and family) reference:“调用带有零 bufsz 和缓冲区空指针的 snprintf 对于确定包含输出所需的缓冲区大小很有用”,示例如下。
  • 是的,A 中的内存已被释放。我想关键问题是在迭代完成之前我不会知道结果字符串的大小,但是在执行迭代时我希望抓取数据并适当地格式化它(所以我不会有一个缓冲区合适的大小来处理我的迭代代码,直到我完成迭代),所以问题是我将这些数据放在哪里?我总是可以迭代两次,但出于明显的原因我想避免这种情况

标签: c string dynamic char


【解决方案1】:

除了你的分配问题,你在这里覆盖了相同的字符串:

    snprintf(returnString, 8192, "\n Format %s\n\n", IT_VALUE);
    snprintf(returnString, 8192, "\n Val %s\n\n", IT_VALUE_2);

您可以使用一种“附加器”来解决这个问题,该“附加器”通过将 0 的大小传递给 snprintf 来确定所需的长度,根据需要重新分配内存,正如 Joachim Pileborg 建议的那样:

#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 必须初始化为全零。
  • vsnprintfsnprintf 的变体,它采用 va_list 而不是可变参数。 v...printf 函数允许您编写自己的 printf 类似函数。您不能将可变参数传递给其他函数,您必须使用 &lt;stdarg.h&gt; 标头中的 va_... 宏创建一个 va_list
  • 大多数编译器可以检测到标准printf 函数的打印格式和参数之间的不匹配。如果您希望从这些检查功能中受益,您可以使用适当的 GCC 属性 ((format(printf, 2, 3)) 或 SAL 注释 _Printf_format_string_

在您的示例中,A 将创建附加程序并将其传递给B,然后返回其.str。你也可以从B返回一个appender,然后从A返回它的.str

【讨论】:

    【解决方案2】:

    我建议AB的以下版本。

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

    注意:

    1. 如果不够,缓冲区大小会加倍。这在大多数情况下都很有效。
    2. 复制被最小化(没有strdupstrcpy
    3. 您可能希望使用 while 循环中的重复代码创建一个新函数
    4. 在您的 B 版本中,每次调用 snprintf 时都会覆盖缓冲区。此处写入位置 (pos) 更新为 append (null terminating char overwritten)
    5. snprintfNULL 参数将返回所需的缓冲区大小,而不在任何地方打印任何内容
    6. 您可能想在调用realloc 后检查ret 是否不是NULL
    7. 如果ret 不是NULL,则可以确定缓冲区足够大。因此简单的sprintf 用于实际打印。
    8. 记得free缓冲区!
    9. 我自己没有测试过代码

    【讨论】:

      猜你喜欢
      • 2015-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      相关资源
      最近更新 更多