来自另一个函数

时间:2016-02-18 10:25:31

标签: c string dynamic char

我有一个像这样的身体的功能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不能?

2 个答案:

答案 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初始化为全零。
  • vsnprintfsnprintf的变体,它采用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)

我建议使用以下版本的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. 每次拨打snprintf时,都会覆盖您的B缓冲区版本。写入位置(pos)更新为追加(null终止char覆盖)
  5. 带有snprintf参数的
  6. NULL将返回所需的缓冲区大小,而无需在任何地方打印任何内容
  7. 您可能需要在致电ret后检查NULL是否为realloc
  8. 如果ret不是NULL,则确定缓冲区足够大。因此,简单的sprintf用于实际打印。
  9. 请记住free缓冲区!
  10. 我自己没有测试过代码