Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 来自另一个函数的动态格式化字符串_C_String_Dynamic_Char - Fatal编程技术网

C 来自另一个函数的动态格式化字符串

C 来自另一个函数的动态格式化字符串,c,string,dynamic,char,C,String,Dynamic,Char,我有一个函数a,它的主体是这样的 char* A (const char* arg) { char ret[8192]; B(ret); return strdup(ret); } 函数B看起来像这样(为了简洁起见,在迭代逻辑上使用了一些伪代码) 所以本质上我有一个函数a,它给另一个函数B一个字符串缓冲区,让B输入格式化数据。现在,只要从迭代返回的总数据不超过8196(只是一个“足够大”的值的猜测),这就可以了,但我认为如果我可以动态地这样做,并且不必担心缓冲区填满的情

我有一个函数a,它的主体是这样的

char* A (const char* arg) {
    char ret[8192];
    B(ret);
    return strdup(ret);
}
函数B看起来像这样(为了简洁起见,在迭代逻辑上使用了一些伪代码)


所以本质上我有一个函数a,它给另一个函数B一个字符串缓冲区,让B输入格式化数据。现在,只要从迭代返回的总数据不超过8196(只是一个“足够大”的值的猜测),这就可以了,但我认为如果我可以动态地这样做,并且不必担心缓冲区填满的情况,这会更好。在函数a仍然必须调用函数B,并且B的签名可以更改,但a的签名不能更改的约束下,如何以相当有效的方式实现这一点?

除了分配问题之外,您还可以在此处覆盖相同的字符串:

    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;
}
#包括
#包括
#包括
#包括
结构附加{
char*str;/*字符串*/
字符串的大小\u t len;/*长度*/
大小\u t大小;/*分配的大小*/
};
无效附加(结构附加*app,常量字符*fmt,…)
{
va_列表参数;
尺寸透镜;
va_启动(arg、fmt);
len=vsnprintf(NULL,0,fmt,arg);
va_端(arg);
而(应用程序->镜头+镜头+1>=应用程序->大小){
应用->大小=应用->大小?应用->大小*2:0x100;
应用->str=realloc(应用->str,应用->大小);
//检查并处理错误
}
va_启动(arg、fmt);
len=vsnprintf(应用->str+app->len,app->size-app->len,fmt,arg);
va_端(arg);
app->len+=len;
}
int main(int argc,字符**argv)
{
结构append_t app={NULL};
对于(int i=1;i1)追加(&app,“,”);
追加(&app,“%s”,argv[i]);
}
if(app.str)put(app.str);
免费(app.str);
返回0;
}
注意事项:

  • 代码使用的事实是
    realloc(NULL,size)
    的行为类似于
    malloc(size)
    。追加器必须初始化为全部零
  • vsnprintf
    snprintf
    的一个变体,它采用
    va_列表
    而不是可变参数。
    v…printf
    函数允许您编写自己的
    printf
    类函数。您不能将可变参数传递给其他函数,您必须使用
    标题中的
    va\u…
    宏创建
    va\u列表
  • 大多数编译器可以检测标准
    printf
    函数的打印格式和参数之间的不匹配。如果您想从这些函数检查中获益,可以使用适当的GCC属性
    ((格式(printf,2,3))
    或SAL注释
    \u printf\u format\u string\u

在您的示例中,
A
将创建appender并将其传递给
B
,然后返回其
.str
。您也可以从
B
返回appender,并从
A
返回其
。str
A

我建议以下版本的
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
  • 您可能希望使用while循环中的重复代码创建一个新函数
  • 在您的B版本中,每次调用
    snprintf
    ,缓冲区都会被覆盖。在这里,写入位置(
    pos
    )被更新为append(null终止
    char
    覆盖)
  • snprintf
    with
    NULL
    参数将返回所需的缓冲区大小,而不在任何地方打印任何内容
  • 调用
    realloc
  • 如果
    ret
    不是
    NULL
    ,则可以确定缓冲区足够大。因此,simple
    sprintf
    用于实际打印
  • 记住释放缓冲区
  • 我还没有亲自测试代码

  • 与您当前的问题无关,但您正在释放由
    A
    函数分配的内存?要使用动态内存(
    malloc
    ),您需要事先知道与您的问题更相关的大小(使用
    strlen(“文字”)+strlen(值)+1
    ):“使用零bufsz和缓冲区的空指针调用snprintf有助于确定包含输出的必要缓冲区大小“,下面是一个示例。是的,A中的内存已被释放。我想关键问题是,在迭代完成之前,我不知道结果字符串的大小,但是在执行迭代时,我希望获取数据并适当格式化它(所以在我完成迭代之前,我不会有一个合适大小的缓冲区来处理我的迭代代码),所以问题是我把数据放在哪里?我总是可以迭代两次,但出于明显的原因,我想避免这样做
    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);
        }
    }