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

时间:2010-09-22 23:35:56

标签: c malloc printf

我正在搜索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;
}

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

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

非常感谢!

7 个答案:

答案 0 :(得分:31)

以下是原始答案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);
    sprintf(buffer, "%s: %s (%d)", msg, strerror(errno), errno);
    return buffer;
}

答案 1 :(得分: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;
}

答案 2 :(得分:12)

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

但根据联机帮助页,POSIX要求snprintf()vsnprintf(),后者可用于构建您自己的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;
}

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

答案 3 :(得分:10)

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

答案 4 :(得分:5)

GLib库提供g_strdup_printf函数,如果可以选择链接GLib,则可以完全按照您的要求执行操作。来自文档:

  

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

答案 5 :(得分:1)

POSIX.1(又名IEEE 1003.1-2008)提供open_memstream:

char *ptr;
size_t size;
FILE *f = open_memstream(&ptr, &size);
fprintf(f, "lots of stuff here\n");
fclose(f);
write(1, ptr, size); /* for example */
free(ptr);

open_memstream(3)至少在Linux和macOS上可用,并且已经存在了很多年。 open_memstream(3)的反义词是fmemopen(3),它使缓冲区的内容可供读取。

如果您只想要一个sprintf(3),那么您可能想要的是广泛实现但非标准的asprintf(3)。

答案 6 :(得分:0)

/*  casprintf print to allocated or reallocated string

char *aux = NULL;
casprintf(&aux,"first line\n");
casprintf(&aux,"seconde line\n");
printf(aux);
free(aux);
*/
int vcasprintf(char **strp,const char *fmt,va_list ap)
{
  int ret;
  char *strp1;
  char *result;
  if (*strp==NULL)
     return vasprintf(strp,fmt,ap);

  ret=vasprintf(&strp1,fmt,ap); // ret = strlen(strp1) or -1
  if (ret == -1 ) return ret;
  if (ret==0) {free(strp1);return strlen(*strp);}

  size_t len = strlen(*strp);
  *strp=realloc(*strp,len + ret +1);
  memcpy((*strp)+len,strp1,ret+1);
  free(strp1);
  return(len+ret);
}

int casprintf(char **strp, const char *fmt, ...)
{
 int ret;
 va_list ap;
 va_start(ap,fmt);
 ret =vcasprintf(strp,fmt,ap);
 va_end(ap);
 return(ret);
}