Ich suche ein sprintf()-ähnliche Implementierung einer Funktion, die automatisch den erforderlichen Speicher zuweist. Also möchte ich sagen
char *my_str = dynamic_sprintf("Hello %s, this is a %.*s nice %05d string", a, b, c, d);
und my_str erhält die Adresse eines zugewiesenen Speicherblocks, der das Ergebnis davon enthält sprintf().
In einem anderen Forum habe ich gelesen, dass man das so lösen kann:
#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;
}
Dies führt jedoch sofort zu einem Segfault, wenn die sprintf() mit dem Nullzeiger aufgerufen wird.
Also irgendwelche Ideen, Lösungen oder Tipps? Eine kleine Implementierung von a sprintf()-ähnlicher Parser, der in die Public Domain gestellt wird, würde schon ausreichen, dann könnte ich es selbst erledigen.
Danke vielmals!
Wer auch immer Ihnen diesen Rat gegeben hat, meinte wahrscheinlich, dass Sie es verwenden sollten snprintfnicht sprintf.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
23. September 2010 um 0:08 Uhr
Mögliches Duplikat von Verwenden von snprintf zur Vermeidung von Pufferüberläufen
– Benutzer9645477
16. April 2018 um 4:10 Uhr
Tarun
Hier ist die ursprüngliche Antwort von Stack Overflow. Wie andere bereits erwähnt haben, müssen Sie snprintf nicht sprintf. Stellen Sie sicher, dass das zweite Argument zu snprintf ist zero. Das wird verhindern snprintf vom Schreiben bis zum NULL Zeichenfolge, die das erste Argument ist.
Das zweite Argument wird benötigt, weil es sagt snprintf dass nicht genügend Platz vorhanden ist, um in den Ausgabepuffer zu schreiben. Wenn nicht genügend Platz vorhanden ist snprintf gibt die Anzahl der Bytes zurück, die es geschrieben hätte, wenn genügend Speicherplatz vorhanden gewesen wäre.
Solltest du nicht 1 hinzufügen needed um das abschließende Nullzeichen zu berücksichtigen?
– beldaz
24. Januar 2016 um 4:03 Uhr
Habe das +1 am Ende der ersten Zeile zuerst nicht entdeckt (es war außerhalb des sichtbaren Bereichs): size_t needed = snprintf(...) + 1;
– Benutzer2421739
10. Februar 2017 um 18:29 Uhr
Ich war etwas besorgt darüber, ob das Übergeben von NULL hier undefiniertes Verhalten hervorrief, also habe ich es überprüft und kann bestätigen, dass es vom C-Standard ausdrücklich erlaubt ist – siehe stackoverflow.com/a/57646312/1709587.
– Mark Amery
25. August 2019 um 13:28 Uhr
Technisch gesehen ist dieser Code unsicher, weil errno könnte sich zwischen den Anrufen ändern snprintf und der Aufruf an sprintf, die nicht gegen Pufferüberlauf geschützt ist. Du solltest benutzen snprintf für beide Anrufe und Sie sollten sparen errno vor dem ersten Aufruf in eine lokale Variable.
– chqrlie
13. Oktober 2019 um 17:28 Uhr
Mike Axiak
GNU und BSD haben asprintf und vasprintf die dafür entwickelt wurden, genau das für Sie zu tun. Es wird herausfinden, wie der Speicher für Sie zugewiesen wird, und bei jedem Speicherzuweisungsfehler null zurückgeben.
asprintf macht das Richtige in Bezug auf die Zuweisung von Zeichenfolgen – es misst zuerst die Größe und versucht dann mit der Zuweisung malloc. Andernfalls wird null zurückgegeben. Es sei denn, Sie haben ein eigenes Speicherzuweisungssystem, das die Verwendung von ausschließt malloc, asprintf ist das beste Werkzeug für den Job.
Der Code würde wie folgt aussehen:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char* ret;
char* a = "Hello";
char* b = "World";
int c = 123;
int err = asprintf(&ret, "%s %d %s!", a, c, b );
if (err == -1) {
fprintf(stderr, "Error in asprintf\n");
return 1;
}
printf("ret = >%s<\n", ret);
free(ret);
return 0;
}
asprintf() wäre die Funktion meiner Wahl – aber leider kein Standard und nicht portabel – schlecht!
– der Schamane
23. September 2010 um 6:53 Uhr
@the-shamen – was Sie verlangen, ist per Definition kein Standard und nicht portabel. Holen Sie sich die Quelle für asprintf und ziehen Sie es bei Bedarf in Ihr Projekt oder implementieren Sie es unabhängig neu.
– bstpierre
23. September 2010 um 18:22 Uhr
Ich habe noch nichts von einer gehört asprintf() das gibt einen Zeiger zurück. Diejenige, die mit GNU und BSD geliefert wird (und von gnulib und libstrl bereitgestellt wird), hat den gleichen Rückgabewert wie das Äquivalent printf() call und nimmt als erstes Argument einen Zeiger auf einen Zeiger. So, char *s; int ret = asprintf(&s, "%s %d %s!", a, c, b); mit Fehler an ret == -1. Ich frage mich nur, welche Systeme / Bibliotheken eine bieten asprintf() was gibt einen Zeiger wie in dieser Antwort zurück?
– binki
7. Februar 2014 um 4:14 Uhr
Pavel Simerda
Wenn Sie mit GNU/BSD-Erweiterungen leben können, ist die Frage bereits beantwortet. Sie können verwenden asprintf() (und vasprintf() zum Erstellen von Wrapper-Funktionen) und fertig.
Aber snprintf() und vsnprintf() werden laut der Manpage von POSIX vorgeschrieben, und letztere kann verwendet werden, um Ihre eigene einfache Version von zu erstellen asprintf() und vasprintf().
int
vasprintf(char **strp, const char *fmt, va_list ap)
{
va_list ap1;
int len;
char *buffer;
int res;
va_copy(ap1, ap);
len = vsnprintf(NULL, 0, fmt, ap1);
if (len < 0)
return len;
va_end(ap1);
buffer = malloc(len + 1);
if (!buffer)
return -1;
res = vsnprintf(buffer, len + 1, fmt, ap);
if (res < 0)
free(buffer);
else
*strp = buffer;
return res;
}
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;
}
Sie können etwas Präprozessor-Magie anwenden und Ihre Versionen von Funktionen nur auf Systemen verwenden, die sie nicht unterstützen.
Du kannst nur bestehen va_list Variable zu einer Funktion. Benutzen vsnprintf() doppelt so viel wie drin vasprintf() du solltest benutzen va_copy().
– Ilia K.
30. Juni 2014 um 18:07 Uhr
Es wird sein, wenn Sie hinzufügen va_end(ap1) vor der Rückkehr von vasprintf() (z. B. direkt nach einem Anruf bei vsnprintf()).
– Ilia K.
10. Juli 2014 um 12:09 Uhr
Sie haben vergessen, das strp am Ende von vasprintf zu setzen
– 5andr0
12. Juli 2015 um 13:14 Uhr
Es gibt eine Fehlerbehandlung im Code (er behandelt Fehler von calloc), aber es explodiert immer noch, wenn vsnprintf fehlschlagen: Die Größenberechnung würde umbrechen, wenn weniger als -1 als Fehlercode für die erste zurückgegeben wird vsnprintf. Wenn der zweite Aufruf von vsnprintf fehlschlägt, ist der Puffer durchgesickert.
– Benutzer2421739
10. Februar 2017 um 18:34 Uhr
@ user2421739 Ich habe das behoben, da diese Antwort ziemlich alt ist.
– Marco Bonelli
15. Juli 2021 um 2:11 Uhr
Wenn möglich verwenden snprintf — Es bietet eine einfache Möglichkeit, die Größe der erzeugten Daten zu messen, damit Sie Speicherplatz zuweisen können.
Wenn du Ja wirklich kann das nicht, eine andere Möglichkeit ist das Drucken in eine temporäre Datei mit fprintf Um die Größe zu erhalten, weisen Sie den Speicher zu und verwenden Sie dann sprintf. snprintf ist bestimmt jedoch die bevorzugte Methode.
Das GLib Bibliothek bietet a g_strdup_printf Funktion, die genau das tut, was Sie wollen, wenn das Verlinken gegen GLib eine Option ist. Aus der Dokumentation:
Ähnlich wie beim Standard-C sprintf()
Funktion, aber sicherer, da sie den maximal erforderlichen Speicherplatz berechnet und Speicher zuweist, um das Ergebnis zu halten. Die zurückgegebene Zeichenfolge sollte mit freigegeben werden g_free() wenn nicht mehr benötigt.
Hallo danke! Aber das ist nur glibc, ich brauche eine plattformunabhängige Lösung. Also vielleicht ist es besser, dies selbst zu tun?
– der Schamane
23. September 2010 um 6:42 Uhr
GLib (die Basis von GTK+), nicht die GNU C Library (glibc). Aber es ist äquivalent zu asprintf von glibc.
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) ist zumindest unter Linux und macOS verfügbar und das schon seit einigen Jahren. Das Gegenteil von open_memstream(3) ist fmemopen(3), das den Inhalt eines Puffers zum Lesen verfügbar macht.
Wenn Sie nur einen einzigen sprintf(3) wollen, dann könnte das weit verbreitete, aber nicht standardmäßige asprintf(3) das sein, was Sie wollen.
Hallo danke! Aber das ist nur glibc, ich brauche eine plattformunabhängige Lösung. Also vielleicht ist es besser, dies selbst zu tun?
– der Schamane
23. September 2010 um 6:42 Uhr
GLib (die Basis von GTK+), nicht die GNU C Library (glibc). Aber es ist äquivalent zu asprintf von glibc.
– Pavel Simerda
16. Oktober 2013 um 16:46 Uhr
glib ist plattformunabhängig
– Jules GM
18. Mai 2015 um 21:32 Uhr
Jose Miguel Parra Tomás
/* 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);
}
Willkommen bei Stapelüberlauf. Während Ihr Code möglicherweise die Antwort auf die Frage liefert, fügen Sie bitte Kontext hinzu, damit andere eine Vorstellung davon haben, was er tut und warum er vorhanden ist.
– Das Ö
11. Oktober 2019 um 11:56 Uhr
14101600cookie-checksprintf() mit automatischer Speicherzuweisung?yes
Wer auch immer Ihnen diesen Rat gegeben hat, meinte wahrscheinlich, dass Sie es verwenden sollten
snprintf
nichtsprintf
.– R.. GitHub HÖR AUF, EIS ZU HELFEN
23. September 2010 um 0:08 Uhr
Mögliches Duplikat von Verwenden von snprintf zur Vermeidung von Pufferüberläufen
– Benutzer9645477
16. April 2018 um 4:10 Uhr