sprintf() with automatic memory allocation?

Viewed 48642

I'm searching for a sprintf()-like implementation of a function that automatically allocates required memory. So I want to say

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

and my_str receives the address of an allocated block of memory that holds the result of this sprintf().

In another forum, I read that this can be solved like this:

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

But this immediately results in a segfault when the sprintf() with the null pointer is invoked.

So any idea, solution or tips? A small implementation of a sprintf()-like parser that is placed in the public domain would already be enough, then I could get it myself done.

Thanks a lot!

8 Answers

POSIX.1 (aka IEEE 1003.1-2008) provides 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) is available on at least Linux and macOS and has been for some years. The converse of open_memstream(3) is fmemopen(3) which makes the contents of a buffer available for reading.

If you just want a single sprintf(3) then the widely-implemented but non-standard asprintf(3) might be what you want.

/*  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);
}

My version of https://stackoverflow.com/a/10388547/666907 – a bit more universal [note I'm a C++ noob, use at your own risk :P]:

char* myFormat(char const *format...) {
  va_list args;
  va_start(args, format);
  
  size_t needed = 1 + vsnprintf(NULL, 0, format, args);

  // they say to cast in C++, so I cast…
  // https://stackoverflow.com/a/5099675/666907
  char* buffer = (char*) malloc(needed);

  vsprintf(buffer, format, args);

  return buffer;
}

Serial.println(myFormat("Foo %s: %d", "bar", 456)); // Foo bar: 456
Related