Variable Anzahl Parameter ohne 'stdarg.h'



  • Hallo,
    ich wills mal wieder genau wissen. Ich will wissen wie z.B. printf auf die ihr übergebene Parameter zugreift. Dazu wollte ich selber eine Funktion schreiben, die eine variable Anzahl an Parametern aufnimmt. Ich komme aber irgendwie nicht an die übergebenen Parameter heran. Ich will dabei nicht diese va_-Makros benutzen. Ich habe mir diese jedoch angeschaut.

    Hier mein Quellcode:

    #include <stdio.h>
    void f1(char c1, char c2, ...)
    {
      char* charPtr = (char*)&c1;
      printf("%c\n",*charPtr);
      charPtr--;
      charPtr--;
      charPtr--;
      charPtr--;
      printf("%c\n",*charPtr);
      charPtr--;
      charPtr--;
      charPtr--;
      charPtr--;
      printf("%c\n",*charPtr);
    }
    int main()
    {
      f1('a','b','c');
      return 0;
    }
    

    Und hier die Ausgabe dazu:

    --------
    a
    b
    
    --------
    

    Verstehe ich das richtig, dass 'a' und 'b' in umgekehrter Reihenfolge auf den Stack gelegt wird (und 'c' eigentlich auch) und dann die Funktion aufgerufen wird? Aber wo ist dann der Parameter 'c' hin? Wo liegt der? Wie greife ich auf dieses "..." zu? Auf c2 kann ich schließlich auch zugreifen indem ich einen Zeiger auf c1 mache und diesen dann vier mal dekrementiere bis ich bei c2 angekommen bin. Wieso lande ich nicht beim dritten übergebenen Parameter wenn ich den Zeiger weitere vier mal dekrementiere?

    In stdarg.h hab ich unter anderem gelesen:

    #define	va_start(list, name) (void) (list = (void *)((char *)&...))
    

    Aber wenn ICH folgendes versuche:

    void f1(char c1, char c2, ...)
    {
      char* var = (char *)&...;
      printf("%c",*var);
    }
    

    bekomme ich folgenen Compile-Error (gcc Compiler)

    Fehler: expected expression before »...« token
    

    Vermutlich sehe ich den Wald vor lauter Bäumen nicht...?!

    MfG



  • ... ist kein gültiger Variablennamen. Im Definitionsteil der Funktion kann es jedoch stehen, um dem Compiler daruf hinzuweisen, dass es sich um eine Ellipse handelt.

    Ich habe während meine Diparbeit einen mikrokernel geschrieben, und hab mir stdarg sozu. nachimplementiert. Bei mir sieht es so aus:

    typedef void *va_list;
    
    #define va_start(l,t)   \
    do {\
        l = &t + 1; \
    } while(0)
    #define va_arg(l,t)     (*( (t *) ( (l = l + sizeof(t)) - sizeof(t) ) ))
    #define va_end(l)       (l = (void *) 0)
    /* defined in C99, pretty useless, but make them happy ;) */
    #define va_copy(d,s)    (d = s)
    

    es ist nicht portabel, hab für die ARM Architektur mit dem GNU GCC gemacht, doch unter x86 hat es auch funktioniert. Ob es mit anderen Compilern auch so geht, weiß ich nicht.

    Also eher so:

    void f1(char c1, char c2, ...) 
    { 
      char* var = &c2 + 1; 
      printf("%c",*var);
    }
    

    aber wie gesagt, das ist nicht portabel.



  • Darf man fragen, was du gegen stdarg.h hast? Diese Makros sind genau dazu da, solche unportablen Zeigerhackereien unter einer portablen Schnittstelle zu verstecken.



  • @supertux:
    So in der Art hab ich das jetzt auch gesehen als ich mir mal das Ergebnis vom Präprozessor anzeigen lies. Jetzt muss ich das nur noch verstehen was da passiert... 😞

    @Bashar:
    Ich hab nix dagegen, ich will nur verstehen wie das intern funktioniert und wie ich das selber machen könnte.

    Edit: Mal anders herum gefragt, was machen die Makros denn anders als ich?



  • Ich komme hier nicht weiter...

    Ich hab jetzt mal folgenden Code:

    #include <stdio.h>
    #include <stdarg.h>
    void f1(int anzahl, ...)
    {
       va_list parameterZg;
       __builtin_va_start(parameterZg, anzahl);
       printf("Die Parameter sind ");
       for (int i=0 ;i<anzahl;i++)
       {
          printf("%c ",__builtin_va_arg(parameterZg, int));
       }
       printf("\n");
        __builtin_va_end(parameterZg);
    }
    int main()
    {
      char a = 'a',b = 'b', c = 'c', d = 'd';
      f1(4,a,b,c,d);
      //f1('a','b','c');
      return 0;
    }
    

    Daraus macht der Präprozessor folgendes (Das include-Zeugs hab ich mal abgeschnitten):

    void f1(int anzahl, ...)
    {
       va_list parameterZg;
       __builtin_va_start(parameterZg, anzahl);
       printf("Die Parameter sind ");
       for (int i=0 ;i<anzahl;i++)
       {
          printf("%c ",__builtin_va_arg(parameterZg, int));
       }
       printf("\n");
        __builtin_va_end(parameterZg);
    # 25 "VarArgs.c"
    }
    int main()
    {
      char a = 'a',b = 'b', c = 'c', d = 'd';
      f1(4,a,b,c,d);
    
      return 0;
    }
    

    Man sieht, die Makros va_start, va_arg und va_end werden durch __builtin_va_start,__builtin_va_arg und __builtin_va_end ersetzt.

    Aber da komm ich nicht näher ran. "builtin" deutet wohl darauf hin dass da Hexerei im Spiel ist oder? Da tun sich Compiler-spezifische Abgründe auf. Weiß jemand ob und wo man nachlesen kann was zumindest der gcc aus __builtin_va_arg macht? Wenn ich die Ergebnisse aus meinen Quellcodes in meinem ersten Post richtig interpretiere, dann liegen diese variablen Parameter eben nicht dort auf dem Stack wo ich vermutet habe (die Vermutung war: vor den fest definierten Parametern) sondern irgendwo anders.

    Die Frage ist nur: WO?



  • SchlechterInformatiker schrieb:

    Aber da komm ich nicht näher ran. "builtin" deutet wohl darauf hin dass da Hexerei im Spiel ist oder?

    Ja. Aber diesmal nicht, um schrecklich gemeine Sachen zu machen, sondern wohl eher um darauf reagieren zu können, ob die Funktion stdcall, fastcall, pascal oder sowas ist.

    Da tun sich Compiler-spezifische Abgründe auf. Weiß jemand ob und wo man nachlesen kann was zumindest der gcc aus __builtin_va_arg macht?

    Im Sourcecode des gcc.

    Wenn ich die Ergebnisse aus meinen Quellcodes in meinem ersten Post richtig interpretiere, dann liegen diese variablen Parameter eben nicht dort auf dem Stack wo ich vermutet habe (die Vermutung war: vor den fest definierten Parametern) sondern irgendwo anders.
    Die Frage ist nur: WO?

    Vielleicht hilft sowas. http://de.wikipedia.org/wiki/Aufrufkonvention



  • Ja stimmt, der Quellcode ist ja open source. Danke für die Anregung. Da werd ich wohl ein Weilchen zu suchen haben... 😞


Anmelden zum Antworten