sprintf() mit automatischer Speicherzuweisung?

Lesezeit: 8 Minuten

Benutzeravatar von the-shamen
der Schamane

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

Benutzeravatar von Tarun
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.

Den Code von diesem Link hier reproduzieren …

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

  • 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

Benutzeravatar von Mike Axiak
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

Benutzeravatar von Pavel Šimerda
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

  1. 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.
  2. 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.

    – Pavel Simerda

    16. Oktober 2013 um 16:46 Uhr

  • glib ist plattformunabhängig

    – Jules GM

    18. Mai 2015 um 21:32 Uhr

Benutzeravatar von John Haxby
John Haxby

POSIX.1 (alias IEEE 1003.1-2008) bietet 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) 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

Benutzeravatar von jose miguel parra tomas
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

1410160cookie-checksprintf() mit automatischer Speicherzuweisung?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy