sprintf (Memory allocation und Parameterliste)



  • Warum castest Du den Rückgabewert von malloc() ?



  • Hallo,

    danke, der Cast ist nicht nötig!
    Habt ihr irgendeine Idee, wie ich die Argumente in der Array an sprintf übergeben kann?



  • monad schrieb:

    Ich glaube nicht, dass vsprintf mir da weiterhelfen wird.

    Warum nicht?
    Kennst du die Funktionsweise denn überhaupt?

    Mit GNU99 geht sogar
    http://ideone.com/Oo58K



  • Hallo Wutz,

    vielen Dank für deine Antwort. Ich habe dein Beispiel noch ein wenig modifiziert, da vasprintf schlechter auf andere Plattformen portiert werden kann.
    Ein (kleines) Problem habe ich mit deiner Lösung noch. Der Compiler moniert den Typen des übergebenen Arrays. Diese Warnung lässt sich jedoch mit einem einfachen Cast nach __gnuc_va_list ausschalten. Gibt es in diesem Fall eine saubere Lösung?

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        char *translation; 
        unsigned int chars_needed; 
        int args[] = {1,2,3,4};
        char msg[] = "Args:%d,%d,%d,%d\n";
    
        chars_needed = vsnprintf(NULL, 0, msg, (__gnuc_va_list) args); 
        translation = (char *) malloc(chars_needed+1); 
        vsprintf(translation, msg, (__gnuc_va_list) args);
    
        puts(translation);
        free(translation);
    
        return 0;
    }
    


  • monad schrieb:

    Swordfish schrieb:

    Warum castest Du den Rückgabewert von malloc() ?

    danke, der Cast ist nicht nötig!

    Warum castest Du den Rückgabewert von malloc() ?



  • Swordfish schrieb:

    monad schrieb:

    Swordfish schrieb:

    Warum castest Du den Rückgabewert von malloc() ?

    danke, der Cast ist nicht nötig!

    Warum castest Du den Rückgabewert von malloc() ?

    Weil ich das obige Coding kopiert und ergänzt habe...



  • #include <stdio.h> 
    #include <stdlib.h> 
    
    int main(void) 
    { 
        char *translation; 
        unsigned int chars_needed; 
        int args[] = {1,2,3,4}; 
        char msg[] = "Args:%d,%d,%d,%d\n"; 
    
        chars_needed = vsnprintf(NULL, 0, msg, (__gnuc_va_list) args); 
        translation = malloc(chars_needed+1); 
        vsprintf(translation, msg, (__gnuc_va_list) args); 
    
        puts(translation); 
        free(translation); 
    
        return 0; 
    }
    


  • Da hat Wutz dir einen Wolf gebaut. va_list muss keinesfalls so funktionieren, wie er es annimmt - und tut es auch nicht überall. Unter x86-64 beispielsweise:

    $ cat foo.c
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
      char *x;
      vasprintf(&x,"%f%f%f%f%f",(double[]){3.,4.,5.,6.,7.});
      puts(x);
      free(x);
      return 0;
    }
    $ gcc foo.c
    $ ./a.out 
    Speicherzugriffsfehler
    

    Mit vsnprintf ergibt sich genau das selbe Problem. Die Gründe dafür hängen mit Calling-Conventions zusammen und sind einigermaßen esoterisch; es geht dabei vor allem um Fälle, in denen Argumente in Registern herumgereicht werden.

    Wenn dein Format-String ausreichend einfach ist (also etwa "%f %f %f %f ..."), kannst du die Längen mit snprintf einzeln ausrechnen und zusammenzählen. Zusammensetzen wirst du ihn von Hand müssen, was aber insofern ein geringes Problem darstellen sollte, als dass du für die vsprintf-Variante, wenn sie denn funktioniert hätte, auf die gleiche Weise einen Format-String zusammensetzen hättest müssen.



  • Hallo seldon,

    ich hatte ja schon meine Zweifel, ob das so einfach geht. Zwar funktioniert der Ansatz bei mir, jedoch glaube ich nicht, dass ein String-Array "ausreichend einfach" ist. Da der Code sich in Grenzen hält, möchte ich ihn gerne vollständig posten:

    message.h:

    #ifndef _message_h
    #define _message_h
    
    struct { 
        size_t index;
        char *de; 
        char *en; 
    } i18n[] = 
    { 
    {0,  "Test %s,%s" , "Test %s,%s"}
    };
    #endif
    

    i18n.h:

    #ifndef _i18n_h
    #define _i18n_h
    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    enum Language {DE, EN};
    
    char *c_render(enum Language, size_t, char **); 
    
    #endif
    

    i18n.c

    #include "i18n.h"
    #include "message.h"
    
    char *get_translation(enum Language lang, char *msg, char **args)
    {
        char *translation;
        size_t chars_needed;
    
        chars_needed = vsnprintf(NULL, 0, msg, (__gnuc_va_list) args);
    
        if (chars_needed > 0)
        {
            translation = malloc(chars_needed+1);
            vsprintf(translation, msg, (__gnuc_va_list) args);
            return translation;   
        }
        else return msg;
    }
    
    char *c_render(enum Language lang, size_t ix, char **args)
    {
        size_t entries = sizeof(i18n)/sizeof(i18n[0]);
    
        if (ix >= entries) return strcpy(malloc(1), "");
    
        switch (lang)
        {
            case DE: return get_translation(lang, i18n[ix].de, args);
            case EN: return get_translation(lang, i18n[ix].en, args); 
            default: return get_translation(lang, i18n[ix].en, args); 
        }
    }
    

    Den Code habe ich nur geschrieben, weil der Haskell Compiler nicht besonders gut mit vielen Strings umgehen kann (vgl. http://www.haskell.org/haskellwiki/Internationalization_of_Haskell_programs). Deswegen habe ich mir ein kleines FFI-Modul gebastelt, das auf diesen C-Code zugreift. Ist es nicht möglich sprintf mehrfach anzuwenden und so schrittweise jeden Formalparameter zu ersetzen, d.h. das Ergebnis jedes Aufrufs von sprintf dient als Eingabe des nächsten Aufrufs in einer Schleife? Wie sollte ich den obigen Code bzgl. vsprintf umgestalten, damit er (möglichst plattformübergreifend) korrekt ist?

    Viele Grüße!



  • Nochmals hallo,

    Vielleicht sollte ich noch erwähnen, dass das Programm soweit fehlerfrei auf meinen Rechner läuft und auch problemlos in das entsprechende Haskell-Programm eingebunden werden konnte. Vielleicht könntest Du, seldon, es nochmals auf deiner Plattform testen. Ich denke CStrings sind weniger kritisch, da im Gegensatz zu double, die Länge auf allen (verbreiteten) Plattformen auf 1 Byte fixiert ist (kann mich auch irren).

    Viele Grüße!



  • Ich kann das bei mir nicht mal kompilieren; er frisst den Cast von char** zu __gnuc_va_list nicht.

    i18n.c: In Funktion »get_translation«:
    i18n.c:9:44: Fehler: Typkonvertierung gibt Feldtyp an
    i18n.c:14:36: Fehler: Typkonvertierung gibt Feldtyp an
    

    Die Breite der Typen sollte dabei ziemlich gleichgültig sein; du willst ja keine Daten zwischen verschiedenen Maschinen hin- und herschieben. Es ist nur stumpf so, dass va_list kein einfaches Array sein muss und es auch nicht überall ist. Grundsätzlich: wenn du eine va_list auf andere Weise als mit den Standardmakros verarbeitest (oder versuchst, selbst eine zusammenzusetzen), bist du auf hoher See verloren. Ich kann dir nur dringend raten, diesen Ansatz schnell wieder zu verwerfen; das kriegst du nie im Leben irgendwohin portiert.



  • Hallo seldon,

    wahrscheinlich ist es das beste, wenn ich gleich den String per Hand konstruiere und das Formatzeichen "%s" durch die jeweiligen Argumente ersetze.

    Viele Grüße



  • Wenn du nicht auf C festgelegt bist, sondern C++ auch in Ordnung geht, wäre Boost.Format einen Blick wert. Du könntest da ja ein paar extern "C"-Funktionen drumherumschreiben.


Anmelden zum Antworten