【问题标题】:Length of each string argument每个字符串参数的长度
【发布时间】:2020-04-03 12:02:18
【问题描述】:

最近我开始对stdarg.h 函数有了一些了解,因为我想拥有类似于printf 的东西,但我不想将其写入控制台,而是想将其作为字符串返回。

到目前为止,这是我想出的:

char *write(const char *format, ...)
{
    // init
    va_list arg;
    char *string;

    va_start (arg, format);
    vsprintf (string, format, arg);

    // done
    va_end (arg);
    return string;
}

现在的问题是,string 没有保留内存,因此我需要帮助来解决此功能,因为我还没有找到任何解决方案。

提前致谢

【问题讨论】:

  • 一种选择是将vsnprintf() 与 n=0 一起使用,因为这将返回已写入的字符数(实际上不写入任何内容)。然后你可以根据它分配一个缓冲区,然后再次调用 vsnprintf,但使用正确的 n
  • 然后您可以根据它分配一个缓冲区,:请注意,您需要为 NUL 终止符分配一个比 vsnprintf 返回的缓冲区更多的内容。
  • 这就是为什么我写“基于”@Jabberwocky ;-)。我希望 OP 在盲目实施任何东西之前实际阅读 vsnprintf 文档。但是,是的,那里有一个陷阱,因为 n 应该是缓冲区的大小(包括 '\0'),而返回值是没有 '\0' 的字符数
  • @FelixG 这是人们在这里询问的常见问题之一
  • @klutt 他想要的函数返回指向生成的字符串的指针

标签: c arguments args


【解决方案1】:

使用snprintf(NULL, 0 检查您需要多长时间的缓冲区。然后分配内存。然后打印到字符串。

char *my_write(const char *format, ...) {
    va_list va;
    va_start(va, format);

    // remember to have a separate va_list for each v*print function
    va_list va2;
    va_copy(va2, va);
    const int len = vsnprintf(NULL, 0, format, va2);
    va_end(va2);

    char *string = malloc((len + 1) * sizeof(*string));
    if (string != NULL) {
       vsprintf(string, format, va);
    }
    va_end(va);

    return string;
}

正如 @IanAbbott 在 cmets 中所建议的,您可以调用 va_start 两次,这似乎很好地简化了代码:

char *my_write(const char *format, ...) {
    va_list va;

    va_start(va, format);
    const int len = vsnprintf(NULL, 0, format, va);
    va_end(va);

    char *string = malloc((len + 1) * sizeof(*string));
    if (string == NULL) {
       return NULL;
    }

    va_start(va, format);
    vsprintf(string, format, va);
    va_end(va);

    return string;
}

在带有 glibc 的平台上,您还可以使用 vasprintf。请注意,write 名称已被 posix write() function 使用,我建议使用其他名称。有了vasprintf GNU 扩展,它就变成了:

#define _GNU_SOURCE
#include <stdio.h>
char *write2(const char *format, ...) {
    va_list va;
    va_start(va, format);
    char *string;
    const int err = vasprintf(&string, format, va);
    va_end(va);
    if (err == -1) {
          return NULL;
    }
    return string;
}

【讨论】:

  • 为什么会这样?:/* 记得为每个 v*print 函数有一个单独的 va_list */ 我根据 FelixG 的回答 [ 重新编写了我的函数,只需添加一个 int length = vsnprintf(string, 0 , 格式, arg);然后 malloc 使用 length+1] 的字符串,看起来它工作得很好。只是对我最容易忽略的那些小细节感到好奇
  • 因为 v*printf 函数会消耗 va_list,所以你不能只是在之后重用它。我的评论中没有提到 va_copy ,但绝对是必要的,所以这篇文章确实是正确和完整的答案
  • v*printf 调用之一中使用va_list 后,您应该只使用va_end 它。你不能用它做任何其他事情。所以要使用va_list 两次,你必须va_copy 它。 seems like it works just fine 这是未定义的行为。
  • va_start(va, format);va_end(va); 之后,可以通过再次调用va_start(va, format); 来重复使用va 变量,因此不需要va2 和@此代码中的 987654341@。
  • @chux 我的意思是这个序列类似于va_start(va, format);vsnprintf(NULL, format, va);va_end(va);va_start(va, format);vsprintf(string, format, va);va_end(va);
猜你喜欢
  • 1970-01-01
  • 2017-11-19
  • 2019-05-11
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
  • 1970-01-01
相关资源
最近更新 更多