sprintf()具有自动内存分配?

the*_*men 35 c malloc printf

我正在寻找一个sprintf() - 类似于自动分配所需内存的函数的实现.所以我想说

char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s nice %05d string", a, b, c, d );
Run Code Online (Sandbox Code Playgroud)

和my_str检索保存此sprintf()结果的已分配内存的地址.

在另一个论坛中,我读到这可以像这样解决:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    char*   ret;
    char*   a = "Hello";
    char*   b = "World";
    int     c = 123;

    int     numbytes;

    numbytes = sprintf( (char*)NULL, "%s %d %s!", a, c, b );
    printf( "numbytes = %d", numbytes );

    ret = (char*)malloc( ( numbytes + 1 ) * sizeof( char ) );
    sprintf( ret, "%s %d %s!", a, c, b );

    printf( "ret = >%s<\n", ret );
    free( ret );

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是当调用带有NULL指针的sprintf()时,这会立即导致段错误.

那么任何想法,解决方案或提示?sprintf()的一个小实现 - 就像放置在公共域中的解析器一样,已经可以实现,然后我可以自己完成它.

非常感谢!

小智 36

这是Stack Overflow的原始答案.正如其他人所说,你需要的snprintf不是sprintf.确保第二个参数snprintfzero.这将阻止snprintf写入NULL第一个参数的字符串.

第二个参数是必需的,因为它告诉snprintf有足够的空间不可用于写入输出缓冲区.当有足够的空间不可用时,snprintf返回它将写入的字节数,有足够的空间可用.

在这里重现该链接的代码......

char* get_error_message(char const *msg) {
    size_t needed = snprintf(NULL, 0, "%s: %s (%d)", msg, strerror(errno), errno) + 1;
    char  *buffer = malloc(needed);
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}
Run Code Online (Sandbox Code Playgroud)

  • 难道你不应该在"需要"中添加1来解释终止空字符吗? (4认同)
  • 最初没有发现第一行末尾的+1(它在可见区域之外):`size_t needed = snprintf(...)+ 1;` (3认同)
  • 从技术上讲,这段代码是不安全的,因为“errno”可能在对“snprintf”的调用和对“sprintf”的调用之间发生变化,而后者没有防止缓冲区溢出的保护。您应该在两次调用中使用“snprintf”,并且应该在第一次调用之前将“errno”保存到本地变量中。 (3认同)
  • 我有点担心在这里传递 NULL 是否会调用未定义的行为,所以我检查了,并且可以确认它是 C 标准明确允许的 - 请参阅 /sf/answers/4035241871/。 (2认同)

Mik*_*iak 25

GNU和BSD有asprintf和vasprintf,旨在为您做到这一点.它将弄清楚如何为您分配内存,并在任何内存分配错误时返回null.

asprintf在分配字符串方面做了正确的事情 - 它首先测量大小,然后尝试使用malloc进行分配.如果失败,则返回null.除非你有自己的内存分配系统阻止使用malloc,否则asprintf是最好的工具.

代码看起来像:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    char*   ret;
    char*   a = "Hello";
    char*   b = "World";
    int     c = 123;

    ret = asprintf( "%s %d %s!", a, c, b );
    if (ret == NULL) {
        fprintf(stderr, "Error in asprintf\n");
        return 1;
    }

    printf( "ret = >%s<\n", ret );
    free( ret );

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 我没有听说过返回指针的`asprintf()`.GNU和BSD附带的(由gnulib和libstrl提供)具有与等效的`printf()`调用相同的返回值,并将指针作为第一个参数.所以,`char*s; int ret = asprintf(&s,"%s%d%s!",a,c,b);``ret == -1`时出错.只是想知道,什么系统/库提供了一个`asprintf()`,它返回一个像这个答案中的指针? (15认同)
  • asprintf()将是我选择的功能 - 但不幸的是,它非标准且不便携 - 糟糕! (7认同)

Pav*_*rda 13

如果您可以使用GNU/BSD扩展,则问题已经得到解答.您可以使用asprintf()(以及vasprintf()构建包装函数)并完成.

snprintf()vsnprintf()被POSIX强制,根据手册页,而后者可以用来建立的自己的简单版本asprintf()vasprintf().

int
vasprintf(char **strp, const char *fmt, va_list ap)
{
    va_list ap1;
    size_t size;
    char *buffer;

    va_copy(ap1, ap);
    size = vsnprintf(NULL, 0, fmt, ap1) + 1;
    va_end(ap1);
    buffer = calloc(1, size);

    if (!buffer)
        return -1;

    *strp = buffer;

    return vsnprintf(buffer, size, fmt, ap);
}

int
asprintf(char **strp, const char *fmt, ...)
{
    int error;
    va_list ap;

    va_start(ap, fmt);
    error = vasprintf(strp, fmt, ap);
    va_end(ap);

    return error;
}
Run Code Online (Sandbox Code Playgroud)

您可以执行一些预处理器魔术,并仅在不支持它们的系统上使用您的函数版本.


Jer*_*fin 10

  1. 如果可能,请使用snprintf- 它提供了一种简单的方法来衡量将要生成的数据大小,以便您可以分配空间.
  2. 如果你真的不能这样做,另一种可能性就是打印到临时文件fprintf来获取大小,分配内存,然后使用sprintf.snprintf肯定虽然优选的方法.


Pau*_*icz 5

GLib库库提供了一个g_strdup_printf功能,你想要做什么,如果链接到的GLib的是一种选择.从文档:

与标准C sprintf() 函数类似但更安全,因为它计算所需的最大空间并分配内存来保存结果.g_free()不再需要时,应释放返回的字符串.

  • GLib(GTK +的基础),而不是GNU C库(glibc).但它相当于glibc的asprintf. (3认同)