Undefinierte Parameteranzahl / Funktionsgröße auf Stack bestimmen



  • Hallo an alle,

    ich bin am verzweifeln. Kann mir jemand sagen, wie ich herausbekomme, wie viele Parameter
    an eine Funktion mit undefinierter Parameteranzahl übergeben wurden?
    Das ganze sieht beispielsweise so aus:

    int Summe( int x,... )
    {
    		int* p_int = &x;
        int sum = 0;
    
        for( int i = 0; i < PARAMETERANZAHL; i++ )
    		{
         		sum += *(p_int + i);
        }
    }
    
    Ich habs auch schon über Assembler versucht:
    int Summe( int x, int y )  // cdecl - call
    {
    [asm]     _asm
         {
              push ebp
              mov ebp, esp
              mov eax, [ebp+8]
              add eax, [ebp+12]
         }[/asm]
    }
    

    Die Parameter liegen ja an Stelle 8 und 12. Wie sieht das bei undefinierter Parameteranzahl aus? Ist dies eventl. dann nur ein Parameter [ebp+8] im Sinne eines dynamischen Arrays vom angegebenen Typ?

    Falls ja, wie komme ich an die größe dieses dynamischen Arrays?
    Die Assemblyausgabe vermerkt zumindest nur ein:

    _x$ = 8	; size = 4
    

    Bei der C-Funktion ...

    Bitte dringend um Hilfe. Danke im voraus!





  • Das geht nicht. Die Funktion muss das aus den Pflichtparametern irgendwie ermitteln, bei printf z.B. aus den Formatspecifiern.



  • Bashar schrieb:

    Das geht nicht. Die Funktion muss das aus den Pflichtparametern irgendwie ermitteln, bei printf z.B. aus den Formatspecifiern.

    Das MUSS ja irgendwie gehen. Die Information muss irgendwo auf dem Stack liegen.
    Ich gehe von einem Ansatz wie bei einem HugeString in TurboPascal aus. Dort liegt (im Gegensatz zum ShortString) die längen Information (des eigentlichen Pointers) genau in der Addresse VOR dem eigentlichen String.

    vadda schrieb:

    http://www.cplusplus.com/reference/clibrary/cstdarg/va_start/
    http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/
    http://www.cplusplus.com/reference/clibrary/cstdarg/va_end/
    http://www.cplusplus.com/reference/clibrary/cstdarg/va_list/

    Genau so möchte ich es nicht lösen. Wie gesagt, die Info muss irgendwo auf dem Stack bzw. direkt bei dem "array"(?) liegen ...

    In Delphi gibt es dafür ja auch High() und Low() die die größe eines (auch dynamischen!) Arrays ermitteln. Die greifen auch irgendwie auf den Stack zu.



  • FrEEzE2046 schrieb:

    Hallo an alle,

    ich bin am verzweifeln. Kann mir jemand sagen, wie ich herausbekomme, wie viele Parameter
    an eine Funktion mit undefinierter Parameteranzahl übergeben wurden?
    Das ganze sieht beispielsweise so aus:

    int Summe( int x,... )
    // ...
    

    Nee, eben so nicht. Das Ganze wird über die macros (keine Funktionen!!!) va_list, va_start, va_arg und va_end ausgelesen. Das ist hier ganz gut dargelegt. Dabei wird auch klar, daß zur Compiletime die Typisierung feststehen muß und die Anzahl der Parameter zur Laufzeit anders bestimmt werden muß (Stichworte: main-argc, printf- Formatstring). Oft nimmt man auch abschließende Nullen als letzten Parameter etc. Faktisch wird Dir ja nur ein Zeiger auf ein Brocken Framespeicher geliefert, wie das aufgeteilt ist, bleibt Sache des Programmierers.

    Anders geht's leider nicht ... 😞



  • FrEEzE2046 schrieb:

    Bashar schrieb:

    Das geht nicht. Die Funktion muss das aus den Pflichtparametern irgendwie ermitteln, bei printf z.B. aus den Formatspecifiern.

    Das MUSS ja irgendwie gehen.

    Warum?
    Der Aufbau eines HugeStrings in Turbopascal ist übrigens unabhängig davon, ob er an eine Funktion übergeben wird oder nicht.

    In C liegt die Information über die Anzahl der übergebenen Parameter eben NICHT auf dem Stack. Es würde auch nicht viel nützen, weil auch eine Information über die Größe der Parameter (char, int, long?) NICHT übergeben wird.



  • Belli schrieb:

    Warum?
    Der Aufbau eines HugeStrings in Turbopascal ist übrigens unabhängig davon, ob er an eine Funktion übergeben wird oder nicht.

    Das habe ich nie anders behauptet! Da hast du was falsch verstanden.

    Belli schrieb:

    In C liegt die Information über die Anzahl der übergebenen Parameter eben NICHT auf dem Stack. Es würde auch nicht viel nützen, weil auch eine Information über die Größe der Parameter (char, int, long?) NICHT übergeben wird.

    Sie liegt vlt. nicht auf dem Stack, dennoch muss sie irgendwo sein. Wie funktioniert denn sonst oben genanntes:

    int main( int argc, char* argv[] ) {}
    

    Wie kommt main denn an argc? Wenn dies ebenfalls mit den genannten Makros funktionert, dann bleibt die Frage, WIE diese Makros funktionieren. Das muss ja auch irgendwie an die Information kommen. Wenn diese aber "nirgendwo liegt", dann könnte auch das Makro nicht funktionieren ...



  • FrEEzE2046 schrieb:

    Das MUSS ja irgendwie gehen. Die Information muss irgendwo auf dem Stack liegen.

    Tut sie eben NICHT. Und wenn Du nur einen Char- Parameter da liegen hast, kannst Du 5000 doubles auslesen - natürlich 5000- mal Datenmüll.

    FrEEzE2046 schrieb:

    Ich gehe von einem Ansatz wie bei einem HugeString in TurboPascal aus.

    C != TurboPascal.

    FrEEzE2046 schrieb:

    In Delphi gibt es dafür ja auch High() und Low() die die größe eines (auch dynamischen!) Arrays ermitteln.

    C != Delphi.
    In C wird Dir nur die Adresse des Frameblocks weitergegeben, sonst nichts. Alles Weitere = Eigenimplementation.



  • FrEEzE2046 schrieb:

    Wie kommt main denn an argc? Wenn dies ebenfalls mit den genannten Makros funktionert, dann bleibt die Frage, WIE diese Makros funktionieren. Das muss ja auch irgendwie an die Information kommen. Wenn diese aber "nirgendwo liegt", dann könnte auch das Makro nicht funktionieren ...

    Du willst nicht verstehen?
    argc wird von Außen an main gegeben, damit main weiß, wieviele char * da zu lesen sind. Könnte main das selber feststellen, wäre argc unnötig!!!!!



  • FrEEzE2046 schrieb:

    Sie liegt vlt. nicht auf dem Stack, dennoch muss sie irgendwo sein.

    Ja, irgendwo ist sie ... implizit. Der Aufrufer und die Funktion müssen sich irgendwie einig werden, wie die Information zu der Funktion kommt. Bei printf geschieht das über den Formatstring, printf hat aber keine Möglichkeit zu prüfen, ob der Aufrufer lügt und mehr oder weniger Argumente übergibt als dort spezifiziert sind!

    Wie funktioniert denn sonst oben genanntes:

    int main( int argc, char* argv[] ) {}
    

    Wie kommt main denn an argc?

    Das ist doch was vollkommen anderes. main hat keine variable Parameterliste, außerdem wird main in argc die (Kommandozeilen-)Parameteranzahl, also die Länge eines Arrays, übergeben. Was hat das denn mit deiner Frage zu tun?



  • pointercrash() schrieb:

    argc wird von Außen an main gegeben, damit main weiß, wieviele char * da zu lesen sind. Könnte main das selber feststellen, wäre argc unnötig!!!!!

    Bashar schrieb:

    Das ist doch was vollkommen anderes. main hat keine variable Parameterliste, außerdem wird main in argc die (Kommandozeilen-)Parameteranzahl, also die Länge eines Arrays, übergeben. Was hat das denn mit deiner Frage zu tun?

    Die Frage ist doch einfach, WIE wird denn "von außen" argc an main gegeben. Wie kann ich mir das denn vorstellen?

    Was das mit meiner Frage zu tun hat? Natürlich ist char* argv[] etwas anderes als eine undefinierte Parameterliste, aber auch ein dynamisches array wäre vom Ansatz her ja eine genau so mögliche Implementierungsmöglichkeit. Beides läuft auf das selbe Ziel hinaus: Mehrere Parameter eines gleichen Typs übergeben und deren Anzahl herausfinden ... (ja, ich weiß, dass ein dyn. array auch nur 1 Parameter ist) ...



  • FrEEzE2046 schrieb:

    Wie funktioniert denn sonst oben genanntes:

    int main( int argc, char* argv[] ) {}
    

    Wie kommt main denn an argc?

    der startup-code ruft 'main' auf und übergibt den wert. das ist ja auch keine va-list.
    🙂



  • FrEEzE2046 schrieb:

    Die Frage ist doch einfach, WIE wird denn "von außen" argc an main gegeben. Wie kann ich mir das denn vorstellen?

    ist nicht vergleichbar und der mechanismus ist afaik nicht definiert. das betriebsystem wird wohl den kommandozeilen-string in einzelne teilstrings parsen und mitzählen, wie viele freie leerzeichen da drin sind. das ist dann dein argc.

    gib deiner funktion einfach einen parameter mit, wie viele argumente mitgeschickt wurden. die typen weißt du dann zwar immer noch nicht, aber die anzahl ist dann bekannt.



  • vadda schrieb:

    gib deiner funktion einfach einen parameter mit, wie viele argumente mitgeschickt wurden. die typen weißt du dann zwar immer noch nicht, aber die anzahl ist dann bekannt.

    Naja, der typ ist schon definiert. Sowohl bei undef. Parameterliste, als auch bei einem dyn. Array (wenn`s denn kein void* ist).

    Das mit dem Mitzählen der leeren Stellen von main ist eine logische Erklärung. Bei der Geschichte mit den undef. Parametern bleibt aber die Frage, wie genau das ganze denn letztendlich ausschaut.

    Kurz gefragt: Ist eine undef. Paramererliste im Sinne von int x,... im Prinzip (auf dem Stack) nichts anderes als ein array aus Integern?

    Zudem ist die Frage wie gesagt, wie genau va_list denn an den Wert kommt. Was genau macht das Makro denn?

    Ich will nicht kleinkariert sein und ich verstehe und weiß auch was ihr hier alle gesagt habt (übrigens danke für die vielen Reaktionen).
    Es ist lediglich Wissensdurst, es interessiert mich einfach. Ich möchte wissen, wie sieht denn int x,... auf dem Stack aus (ist es ein Pointer?) und was macht va_list.

    Wenn mir da jemand helfen könnte, wäre mir sehr geholfen ... (intelligenter Satz).

    Das Delphi/TurboPascal/ObjectPascal kein C/C++ ist, ist mir auch klar. Aber wenn Borland es auf die Reihe bekommt eine Funktion zu implementieren, die auch die Größe von dyn. Arrays ermittelt, wird dies jawohl auch in C/C++ und in Assembler sowieso möglich sein.



  • [quote="FrEEzE2046"]

    vadda schrieb:

    Naja, der typ ist schon definiert. Sowohl bei undef. Parameterliste, als auch bei einem dyn. Array (wenn`s denn kein void* ist).

    Das mit dem Mitzählen der leeren Stellen von main ist eine logische Erklärung. Bei der Geschichte mit den undef. Parametern bleibt aber die Frage, wie genau das ganze denn letztendlich ausschaut.

    Kurz gefragt: Ist eine undef. Paramererliste im Sinne von int x,... im Prinzip (auf dem Stack) nichts anderes als ein array aus Integern?

    Nein, der Typ ist nicht definiert.
    Eine Funktion, die so deklariert ist:

    void funk(int x, ...);

    kennt nur den Typ des ersten Parameters. Die weiteren übergebenen Parameter können alle einen unterschiedlichen Typ haben.



  • Belli schrieb:

    Nein, der Typ ist nicht definiert.
    Eine Funktion, die so deklariert ist:

    void funk(int x, ...);

    kennt nur den Typ des ersten Parameters. Die weiteren übergebenen Parameter können alle einen unterschiedlichen Typ haben.

    Darum geht es mir zwar eigentlich nicht, aber dass was du da sagst ist mir absolut neu. Ich habe es an dieser Stelle auch gleich mal ausprobiert:

    void funk(int x, ...) {}
    

    bringt bei mir nach Aufruf

    int main() 
    { 
         double d = 2.3;
         funk( 3, d, "Hallo" );
    
         return 0;
    }
    

    folgende Meldungen:
    error C2664: 'TestSumme': Konvertierung des Parameters 3 von 'const char [6]' in 'int' nicht möglic 122
    Warnung 1 warning C4244: 'Argument': Konvertierung von 'double' in 'int', möglicher Datenverlust 122

    Demnach muss wohl doch alles int sein, so wie ich sagte ...



  • FrEEzE2046 schrieb:

    Aber wenn Borland es auf die Reihe bekommt eine Funktion zu implementieren, die auch die Größe von dyn. Arrays ermittelt, wird dies jawohl auch in C/C++ und in Assembler sowieso möglich sein.

    geht ja auch, aber du musst es eben selbst programmieren. in C eingebaut ist sowas nicht (wie z.b. "hello".length() in Java o.ä.)
    🙂



  • +fricky schrieb:

    geht ja auch, aber du musst es eben selbst programmieren. in C eingebaut ist sowas nicht (wie z.b. "hello".length() in Java o.ä.)
    🙂

    Genau, daher ja der Thread. Hättest du vielleicht einen Ansatz?
    Es geht mir ja gar nicht darum eine vorhandene Funktion / Makro zu benutzen, ich würde es auch selber programmieren, auch in assembler. Aber mir fehlt einfach der Ansatz ...
    vlt. hab ich mich im bisherigen Thread-Verlauf undeutlich ausgedrückt 😞

    Ich mache da jetzt schon seit Tagen dran rum. Es fehlt aber einfach das Wissen, wie das Ganze zu realisieren ist.



  • FrEEzE2046 schrieb:

    Demnach muss wohl doch alles int sein, so wie ich sagte ...

    void funk(int x, ...) { }
    
    int main()
    {
    	double d = 2.0;
    	funk(1, d, "drei");
    }
    

    VS2008 schrieb:

    1>va_test - 0 error(s), 0 warning(s)

    hätte mich auch gewundert.



  • FrEEzE2046 schrieb:

    Genau, daher ja der Thread. Hättest du vielleicht einen Ansatz?

    naja, entweder du gibst der va_list eine ende-kennung als letzten wert mit (z.b. 0, -1, oder so), oder der erste ist die anzahl.

    int summe (int x, ...)
    {
      va_list vl;
      int sum = 0;
    
      va_start (vl, x);
    
      while (x--)
        sum += va_arg (vl, int);
    
      return sum;
    }
    
    int main()
    {
      // beispiel: 4 werte, 1,2,3,-6 addieren
      printf ("%d\n", summe (4, 1,2,3,-6)); 
    }
    

    🙂


Anmelden zum Antworten