Fragen zu Argumentlisten



  • Hallo ANSI C- Freaks,

    nach einiger Zeit der Beschäftigung mit Argumentlisten, frage ich mich immer noch, was das va_end() überhaupt bewirken soll:

    static void myprintf(char far *string, ...)
    {
       va_list argzeiger;
       va_start(argzeiger,string);
       vprintf(string,argzeiger);
       va_end(argzeiger);	// kann man weglassen, funzt immer noch
    }
    

    Ob ich das jetzt einmal oder eine millionmal aufrufe, es macht keinen Unterschied, ob ich va_end auskommentiere oder lasse. 😕

    Dann heißt es weiter in den Eskimo- FAQs (http://www.eskimo.com/~scs/C-faq/q15.12.html), daß man keine Argumentlisten durch Funktionen weiterreichen kann 😞 - stimmt das wirklich?

    Das impliziert natürlich auch, daß man Argumentlisten schlecht in structs "aufheben" kann, um sie später zu bearbeiten, oder täusche ich mich da? 🙄

    Bin echt gespannt, was ihr so schreibt, weil ich das alles probiert habe, aber nie erwünschte Ergebnisse erhalte ... 😡

    Die üblichen Grüße und Danksagungen ... 😃



  • pointercrash() schrieb:

    va_end(argzeiger);	// kann man weglassen, funzt immer noch
    

    sollte aber nicht fehlen. Dir erklären warum, kann ich nicht, weil ich nicht weiß, wie die macros aussehen. Aus den man pages

    The va_end() macro is used to clean up; it invalidates ap for use (unless va_start() or va_copy() is invoked again).

    Siehe stdarg.h(3)



  • supertux schrieb:

    sollte aber nicht fehlen. Dir erklären warum, kann ich nicht, weil ich nicht weiß, wie die macros aussehen. Aus den man pages

    Gut, dann schreib' ich's halt hin. Verstehe bloß nicht, daß es überhaupt keinen Code abwirft - in der ausführbaren Datei schlägt sich das in Null tracebarem Assembler nieder. Aber wenn's die man- pages glücklich macht, soll man sie glücklich machen 🙄

    Wo genau müßte dann aber der allgemeinen Meinung nach va_end() überhaupt hin 😕 . Hab's mal verschoben - ohne sichtbare Effekte.

    Da bleiben aber immer noch die zwei Restfragen:
    Man kann die Argumente nicht mit ( , ...) an Unterfunktionen weitergeben, leuchtet auch grob ein, aber den va_list- ap müßte man ja weitergeben können, oder 😕

    ... denn dann müßte man ihn problemlos in einer Struct "einparken" können, um die Argumentliste dann bearbeiten zu lassen, wenn es meinem Programm paßt 😉

    Wie gesagt, der Compiler schluckt es problemlos, nur kommt hinterher Käse raus 😞

    Zu abstrakt? Werde morgen Code zusammenstellen, wenn sich keiner rührt 😃



  • pointercrash() schrieb:

    Aber wenn's die man- pages glücklich macht,

    du musst nur ANSI C glücklich machen 😉 - vielleicht wird dein Programm ja mal portiert



  • Vertexwahn schrieb:

    pointercrash() schrieb:

    Aber wenn's die man- pages glücklich macht,

    du musst nur ANSI C glücklich machen 😉 - vielleicht wird dein Programm ja mal portiert

    Gut, dann wird eben ANSI-C befriedigt 😃
    Nur, wohin muß va_end() dann hin? Generell, nachdem die Argument- Liste fertig bearbeitet ist ?

    Gilt das auch, wenn ich nur den va_list- Pointer in eine Unterfunktion reiche?

    Fragen über Fragen ... 😕



  • pointercrash() schrieb:

    Gut, dann schreib' ich's halt hin. Verstehe bloß nicht, daß es überhaupt keinen Code abwirft - in der ausführbaren Datei schlägt sich das in Null tracebarem Assembler nieder. Aber wenn's die man- pages glücklich macht, soll man sie glücklich machen 🙄

    sorry, das ist Blödsinn. Die Man pages dokumentieren, was Funktionen und Programme tun, du musst ihnen keine Freude machen, aber wenn du Fehler einbaust, auch wenn sie darauf hinweisen, hast du nur Müll programmiert. Du musst nicht den Man Pages glückglich machen, sondern die Nutzer deiner Sofware und Menschen wollen keinen Müll benutzen.

    pointercrash() schrieb:

    Wo genau müßte dann aber der allgemeinen Meinung nach va_end() überhaupt hin 😕 . Hab's mal verschoben - ohne sichtbare Effekte.

    Am Ende der Funktion, wo argzeiger sich befindet? Auf jeden Fall, wenn du die va_* Makros nicht mehr brauchst.

    pointercrash() schrieb:

    Wie gesagt, der Compiler schluckt es problemlos, nur kommt hinterher Käse raus 😞

    Nirgends steht es, dass der Compiler ohne va_end den Code nicht runterschlucken wird, sondern, dass wenn du das Programm ausführst und auf va_end verzichtet hast, werden Variablen nicht mehr freigegeben, die wichtig sind, um die Ellipsen zu speichern, so dass es glatt läuft. Wer va_end nicht benutzt, sollte sich nicht wundern, dass "hinterher Käse raus" kommt und dass hinterher gesagt wird, dass man doch va_end benutzt, um die Man pages zu befriedigen 😕

    va_end ist wichtig, damit der einige Sachen einfach richtig abgeräumt werden und damit die va_* Makros das nächste Mal richtig laufen und daran soll man sich haöten. Wer damit nicht leben kann, soll eben eine andere Programmierschprache wählen.

    Nur, wohin muß va_end() dann hin? Generell, nachdem die Argument- Liste fertig bearbeitet ist?

    ja

    Gilt das auch, wenn ich nur den va_list- Pointer in eine Unterfunktion reiche?

    würde ich nicht tun.



  • supertux schrieb:

    ... hast du nur Müll programmiert. Du musst nicht den Man Pages glückglich machen, sondern die Nutzer deiner Sofware und Menschen wollen keinen Müll benutzen.

    Äh, mußt ja nicht gleich pampig werden. Letztendlich will ich ja nicht nur ein funktionierendes Programm haben, sondern auch wissen, warum das so sein muß.

    supertux schrieb:

    Nirgends steht es, dass der Compiler ohne va_end den Code nicht runterschlucken wird, sondern, dass wenn du das Programm ausführst und auf va_end verzichtet hast, werden Variablen nicht mehr freigegeben, die wichtig sind, um die Ellipsen zu speichern, so dass es glatt läuft. Wer va_end nicht benutzt, sollte sich nicht wundern, dass "hinterher Käse raus" kommt und dass hinterher gesagt wird, dass man doch va_end benutzt, um die Man pages zu befriedigen :confused
    va_end ist wichtig, damit der einige Sachen einfach richtig abgeräumt werden und damit die va_* Makros das nächste Mal richtig laufen und daran soll man sich haöten. Wer damit nicht leben kann, soll eben eine andere Programmierschprache wählen.

    Meinst Du, daß das der richtige Ton ist, einen Sachverhalt zu vermitteln? 😡
    Bei va_start werden (im Assembler- Debugger sichtbar) ein paar Bytes bewegt, bei va_end passiert gar nichts und mich hätte interessiert, was sich da eigentlich rühren soll, wenn man es den Empfehlungen gemäß einsetzt, damit ich auch verstehen kann, was bei meinem Prog schiefläuft.

    Nur, wohin muß va_end() dann hin? Generell, nachdem die Argument- Liste fertig bearbeitet ist?

    supertux schrieb:

    ja

    Das ist wenigstens eine konkrete Aussage ohne Gemecker ... 😉

    Gilt das auch, wenn ich nur den va_list- Pointer in eine Unterfunktion reiche?

    supertux schrieb:

    würde ich nicht tun.

    Hab' ich aber getan, und etwas erhalten, was augenscheinlich tut 😃 - mehr im nächsten Posting.



  • So,

    nachdem ich ein wenig herumgebosselt habe, hatte ich dann doch noch etwas Lauffähiges. :p

    /**** Include of LIB files ***********************************/
    
    #include <stdarg.h>
    #include    <stdlib.h>
    #include    <stdio.h>
    #include "lcd.h"
    
    /*************************************************************/
    /************** internal structs *****************************/
    /*************************************************************/
    struct printvalue // Eintragformat für Einzelwerte
    {
     char far *formatstring;
     va_list argumente;
    };
    /*************************************************************/
    /************** all Prototypes *******************************/
    /*************************************************************/
    struct printvalue far * storevalue(char *format,...);
    int printlater(struct printvalue far *);
    static void myprintf(char far *string, ...);
    void main(void);
    
    /*************************************************************/
    /************** Implementation *******************************/
    /*************************************************************/
    
    // Funktion zum Speichern Formatstring, Argumentpointer
    struct printvalue  far * storevalue(char far *format,...)
    {
     va_list argzeiger;
     struct printvalue far * new_value = malloc(sizeof(struct printvalue));
     va_start(argzeiger,format);
     if (new_value)  // Speicher erhalten
     {
      new_value->formatstring = format;
      new_value->argumente = argzeiger;
     }
     va_end(argzeiger);
     return new_value;
    }
    /*************************************************************/
    
    // Funktion zur späteren Ausgabe
    int printlater(struct printvalue far *what_to_print)
    {
     int len;
    // char far *format; // kann kein near/far -casting beim Dereferenzieren
    // va_list argzeiger;
    
    // format = (what_to_print->formatstring); // Casting- Umweg
    // va_start(argzeiger,format);
     len = vprintf(what_to_print->formatstring,what_to_print->argumente);
    // va_end(argzeiger);
     return len;
    }
    
    /*************************************************************/
    
    // Funktion zur sofortigen Ausgabe
    void myprintf(char far *string, ...)
    {
       va_list argzeiger;
       va_start(argzeiger,string);
       vprintf(string,argzeiger);
       va_end(argzeiger);
    }
    
    /*************************************************************/
    /************** Main *****************************************/
    /*************************************************************/
    
    void   main()  // ja, ich weiß schon, int main wäre korrekt
    {
     struct printvalue far * zwei_args;
     struct printvalue far * drei_args;
    
    lcd_init();
    
         // Ausgaben für später speichern
    zwei_args = storevalue("%i %i ",1,2);
    drei_args = storevalue("%i %i %i",3,4,5);
    
       // do something else
    lcd_cursor_pos(3,3);
    myprintf("Direkttest %i",1);
    lcd_cursor_pos(3,4);
    printlater(zwei_args);
    printlater(drei_args);  // Ausgabe 1 2 3 4 5
    
    while (1)
    }
    

    Stoßt euch nicht an den "far"- modifyern, mein memory model braucht sie.

    Jetzt würde mich nur noch interessieren, warum es funktioniert - ein paar Fragen habe ich also schon noch:

    1. Zu Funktion storevalue(): Was macht das Makro va_start(argzeiger,format) eigentlich wirklich? Da im Code außer Wegspeichern nichts passiert, müßte ja nach dessen Ausführung die ganze Liste von Argumenten bereits feststehen. Denn es werden ja nur der Formatstring und der Argumentlistenpointer übergeben. Wie sieht der Argumentlistenpointer eigentlich aus? Wird er zur Laufzeit oder beim Compilieren angelegt?

    2. Zu Funktion printlater(): Läuft unabhängig davon, ob ich den va_* - Kram im Code habe. Sollte er aus ANSI-C- Kompatibilitätsgründen mit hinein und wenn ja, warum? Oder sollte er sogar überhaupt nicht da stehen (ist nämlich eher mein Verdacht), weil ja keine Argumentliste mehr einzulesen ist, sondern nur noch Pointer durchgeschoben werden müssen?

    3. Wie sieht es mit der Gültigkeit des Argumentpointers aus? Ich gehe davon aus, daß zur Ausführungszeit von printlater() alle Variablen der Liste vorhanden sein sollten, damit kein Datenschrott ausgegeben wird. Es dürfte sich also empfehlen, keine Variablen aus dem lokalen Stack einer Funktion zu übergeben - ein Irrtum? Welche weiteren Einschränkungen muß ich beachten?

    Besonderen Dank für konstruktive Antworten und ich weiß eh', daß es int main(int) heißen sollte 😉



  • pointercrash() schrieb:

    supertux schrieb:

    ... hast du nur Müll programmiert. Du musst nicht den Man Pages glückglich machen, sondern die Nutzer deiner Sofware und Menschen wollen keinen Müll benutzen.

    Äh, mußt ja nicht gleich pampig werden. Letztendlich will ich ja nicht nur ein funktionierendes Programm haben, sondern auch wissen, warum das so sein muß.

    das hab ich mehrmals gesagt, weil va_end die Variablen und Speicher aufräumt, damit va_start das nächste Mal auch richtig startet. Wenn du genauer wissen willst, schau dir den Code von stdarg.h oder mach's hardcore mäßig und untersuch den Code von gibc. 🙂

    pointercrash() schrieb:

    supertux schrieb:

    würde ich nicht tun.

    Hab' ich aber getan, und etwas erhalten, was augenscheinlich tut 😃 - mehr im nächsten Posting.

    ich würde es nicht tun, weil dir va_* Makros sind und da weißt du nicht, wie sie die Argumente behandeln, es könnte auch sos ein, dass der Code nachher nicht mehr kompilierbar ist.



  • supertux schrieb:

    das hab ich mehrmals gesagt, weil va_end die Variablen und Speicher aufräumt, damit va_start das nächste Mal auch richtig startet. Wenn du genauer wissen willst, schau dir den Code von stdarg.h oder mach's hardcore mäßig und untersuch den Code von gibc. 🙂
    ...
    ich würde es nicht tun, weil dir va_* Makros sind und da weißt du nicht, wie sie die Argumente behandeln, es könnte auch sos ein, dass der Code nachher nicht mehr kompilierbar ist.

    Hab' ich mir jetzt angesehen - va_end setzt das PP-Argument AP auf 0, va_start setzt es aber sowieso neu. Störend wird Einstreuen von va_end also nur, wenn man einzeln auf die Argumente zugreifen will. Damit wird auch klar, daß das alles beim Compilieren passiert, man sich die Initialisierung einer Argumentliste beim Handling eines gespeicherten Argumentpointers sparen soll und nur die Gültigkeit der übergebenen Variablen zu beachten ist.

    Gut, jetzt haben wir's ja 😃

    Danke zwar, aber die Müll- Sprüche wären nicht nötig gewesen ... 🙄


Anmelden zum Antworten