2010-09-22 40 views
28

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

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

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

但在调用的sprintf()与NULL指针时,这immediatelly导致段错误。

因此,任何想法,解决方案或技巧?一个放在公共领域的sprintf()类似的解析器的小实现已经足够了,然后我可以自己完成。

非常感谢!

+1

谁给了你这个建议可能意味着你应该使用'snprintf',而不是'sprintf'。 – 2010-09-23 00:08:58

回答

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

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

asprintf()将是我选择的功能 - 但不幸的是,它的非标准和不便携性 - 差! – 2010-09-23 06:53:49

+1

@ sha-shamen - 你所要求的从定义上来说是非标准的,而不是便携式的。获取'asprintf'的源代码,并在需要时将其引入到项目中,或者独立重新实现它。 – bstpierre 2010-09-23 18:22:44

+11

我还没有听说过一个返回指针的'asprintf()'。 GNU和BSD(以及由gnulib和libstrl提供的)具有与等价的'printf()'调用相同的返回值,并将一个指针指向第一个参数。所以,char * s; int ret = asprintf(&s,“%s%d%s!”,a,c,b);'在'ret == -1'时出错。想知道,什么系统/库提供了一个'asprintf()',它返回一个像这个答案中的指针? – binki 2014-02-07 04:14:43

4

GLib库提供了一个g_strdup_printf函数,它正是你想要什么,如果链接到的GLib的是一种选择。从文档:

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

+0

你好,谢谢!但这只是glibc,我需要一个独立于平台的解决方案。所以也许最好自己做这个? – 2010-09-23 06:42:15

+3

GLib(GTK +的基础),而不是GNU C库(glibc)。但它相当于glibc的asprintf。 – 2013-10-16 16:46:40

+0

glib是独立平台 – Julius 2015-05-18 21:32:13

23

这是原来的答案from Stack Overflow。正如其他人所说,你需要snprintf而不是sprintf。确保snprintf的第二个参数是zero。这将阻止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); 
    snprintf(buffer, needed, "%s: %s (%d)", msg, strerror(errno), errno); 
    return buffer; 
} 
+3

不应该为1加上''needed'来解释终止空字符吗? – beldaz 2016-01-24 04:03:55

+2

没有发现第一行末尾的+1(它在可见区域之外):'size_t needed = snprintf(...)+ 1;' – user2421739 2017-02-10 18:29:58

9

如果你可以用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; 
} 

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

+1

您只能将'va_list'变量传递给一个功能。要在'vasprintf()'中使用'vsnprintf()'两次,你应该使用'va_copy()'。 – 2014-06-30 18:07:35

+0

@IliaK。现在是否正确? – 2014-07-01 06:42:42

+1

如果您在从'vasprintf()'返回之前添加'va_end(ap1)'(例如在调用'vsnprintf()'之后)。 – 2014-07-10 12:09:27