sprintf () z automatycznym przydzielaniem pamięci?


Szukam sprintf () - podobnej implementacji funkcji, która automatycznie przydziela niezbędną pamięć. Więc co chcę powiedzieć
char* my_str = dynamic_sprintf( "Hello %s, this is a %.*s nice d string", a, b, c, d );

a my_str pobiera adres przydzielonej pamięci, która zawiera wynik tego sprintf ().
Na innym forum przeczytałem, że można to rozwiązać w ten sposób:
#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;
}

Ale to natychmiast prowadzi do segfaulta, gdy wywoływana jest sprintf () ze wskaźnikiem NULL.
Masz więc pomysły, rozwiązania lub wskazówki? Wystarczyłaby mała implementacja parsera sprintf (), która jest publicznie dostępna, wtedy mógłbym to zrobić sam.
Wielkie dzięki!
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto oryginalna odpowiedź

z przepełnienia stosu
https://coderoad.ru/1775403/... Jak wspominali inni, potrzebujesz
snprintf
, a nie
sprintf
. Upewnij się, że drugi argument dla
snprintf
to
zero
. Zapobiegnie to zapisywaniu
snprintf
w ciągu
NULL
, który jest pierwszym argumentem.
Drugi argument jest potrzebny, ponieważ informuje
snprintf
, że nie ma wystarczającej ilości miejsca na zapis do bufora wyjściowego. Jeśli nie ma wystarczającej ilości miejsca,
snprintf
zwraca liczbę bajtów, które zostałyby zapisane, gdyby było wystarczająco dużo miejsca.
Odtwarzam tutaj kod z tego linku ...
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;
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

GNU i BSD mają

asprintf
http://linux.die.net/man/3/asprintf
i vasprintf, które są do tego przeznaczone. Dowie się, jak przydzielić Ci pamięć i zwróci wartość null przy każdym błędzie alokacji pamięci.
asprintf postępuje właściwie w odniesieniu do alokacji linii - najpierw mierzy rozmiar, a następnie próbuje przydzielić za pomocą malloc. W przeciwnym razie zwraca null. Jeśli nie masz własnego alokatora pamięci, który eliminuje użycie malloc, asprintf jest najlepszym narzędziem do tego zadania.
Kod będzie wyglądał następująco:
#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;
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli możesz żyć z rozszerzeniem GNU/BSD, to odpowiedź na to pytanie została już udzielona. Możesz użyć
asprintf ()
(i
vasprintf ()
do zbudowania funkcji opakowujących) i zostanie to zrobione.
Ale
snprintf ()
i
vsnprintf ()
są zalecane przez POSIX zgodnie ze stroną podręcznika, a ta ostatnia może być użyta do stworzenia własnej prostej wersji
asprintf ()
i
vasprintf ()
. [/code]
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;
}

Możesz użyć magicznych preprocesorów i używać swoich wersji funkcji tylko w systemach, które ich nie obsługują.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

  • Jeśli to możliwe, użyj
    snprintf
    http://libslack.org/manpages/snprintf.3.html- zapewnia to łatwy sposób pomiaru rozmiaru pobieranych danych, aby można było przydzielić miejsce.
  • Jeśli ty naprawdę nie mogę tego zrobić, inną możliwością jest wydrukowanie do pliku tymczasowego za pomocą
    fprintf
    , aby uzyskać rozmiar, przydzielić pamięć, a następnie użyć sprintf. Jednak
    snprintf
    Zdecydowanie jest metodą preferowaną.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Biblioteka GLib udostępnia tę funkcję
g_strdup_printf
http://library.gnome.org/devel ... rintf
który robi dokładnie to, co chcesz, jeśli linkowanie do GLib jest opcją. Z dokumentacji:

Podobna do standardowej funkcji C
sprintf ()
, ale od niej bezpieczniejsza
oblicza maksymalną wymaganą przestrzeń
i przydziela pamięć do przechowywania
wynik. Zwrócony ciąg powinien być
zwolniony przez
g_free ()
, gdy już nie ma
niezbędny.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

POSIX.1 (aka IEEE 1003.1-2008) zapewnia 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) jest dostępny przynajmniej na Linuksie i MacOS i istnieje od kilku lat. Odwrotnością open_memstream (3) jest fmemopen (3), dzięki czemu zawartość bufora jest czytelna.
Jeśli potrzebujesz tylko jednego sprintf (3), to szeroko stosowany, ale niestandardowy asprintf (3) może być dokładnie tym, czego potrzebujesz.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

/* casprintf print to allocated or reallocated stringchar *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 *resu<
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);
}

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się