Übergabe einer variablen Zahl von Argumenten



  • Hallo!

    Ich habe, wie der Titel dieses Threads ja schon sagt, eine Frage zur Übergabe von variablen Anzahlen von Argumenten an Funktionen, die dies unterstützen.

    Ich habe etwas mit der Funktion execl experimentiert, welche ja eine variable Anzahl an Argumenten akzeptiert. Der Prototyp ist (zur Erinnerung):

    int execl( const char *path, const char *arg, ...);

    In "C in 21 Tagen" (http://wwwuser.gwdg.de/~kboehm/ebook/inhalt.html , nicht wundern, der Titel der Seite stimmt nicht) wird im Kapitel Prozesse und Signale als Aufgabe gestellt, die Funktion system() mithilfe von fork() und execl() zu realisieren. Das wollte ich mal probieren und war soweit auch erfolgreich, jedoch nur dann, wenn der übergebene Befehl keine Leerzeichen enthält.

    Tut er das doch (beispielsweise "/bin/ps aux"), so müssen die einzelnen Argumente einzeln übergeben werden, wie es mir scheint. Sollte ich damit falsch liegen, macht das nichts, denn das ändert nichts an meiner Frage. Das dient jetzt lediglich als Beispiel.

    Ich könnte jetzt den Befehl in die einzelnen Worte zerlegen und beispielsweise ein Array von Pointern auf die einzelnen Worte definieren. Diese Pointer müssten nun der Funktion execl() in der Form

    execl(arg1, arg2, arg3, ..., NULL)

    übergeben werden. Das wäre kein Problem, wenn von vornherein bekannt wäre, wieviele Worte der Befehl enthält. So jedoch geht das nicht.

    Meine Idee wäre nun, einfach execl() mit einer großen, festen Anzahl von Argumenten aufzurufen und die nicht benötigten Argumente auf NULL zu setzen. Das müsste, soweit ich das verstanden habe, funktionieren, da die variable Argumentenliste hier ohnehin mit NULL terminiert wird. Trotzdem finde ich diesen Ansatz besonders im Hinblick auf andere mögliche Anwendungsgebiete, bei denen vielleicht eine sehr große Maximalzahl an Argumenten übergeben werden muss, sehr unschön.

    Ich möchte betonen, dass mir klar ist, dass ich einfach eine eigene Funktion schreiben kann, die nur den Pointer auf ein erstes Argument und nicht jedes Argument einzeln by Value benötigt. execl() benötigt die char-Pointer aber nunmal by value und kann mit Pointern auf Pointer leider nichts anfangen.

    Beim Funktionsaufruf brauche ich also einen Funktionsstack, der aufeinanderfolgend alle char-Pointer enthält, das Ende wird durch NULL definiert. Es wäre in diesem besonderen Anwendungsfall also theoretisch sehr vorteilhaft, wenn man den an die Funktion übergebenen Stack in irgendeiner Form ähnlich einem memcopy-Aufruf selbst angeben könnte, o.ä.

    Gibt es sowas? Wie würdet ihr ein solches Problem angehen? Ich hoffe, dass überhaupt in etwa klar wurde, worum es mir geht. Ein weiteres Anwendungsbeispiel wäre etwa eine Funktion mit einer Variablen Anzahl von Integer-Werten, welche diese Werte sortiert, summiert, oder ihren Durchschnitt errechnet.

    Auch hier ist mir klar, dass man eine solche Funktion so implementieren würde, dass sie einen Pointer auf das erste Element eines Integer-Arrays erwartet, oder ähnlich. Aber die Annahme ist einfach, dass ich eine schon vorhandene Funktion nutzen möchte, welche leider einfach alle Werte bei Value erwartet.

    Vielen Dank, dass du diesen Roman überhaupt gelesen hast! 🙂
    Sollte diese Frage schon häufig besprochen worden sein, bitte ich um Entschuldigung, ich habe lange bei Google und in Foren danach gesucht und nichts gefunden. Eventuell, weil ich nicht wusste, wie man das nennt, was ich meine. Also bitte ich in diesem Fall um Stichwörter oder Links.

    Gruß, Volker



  • hmm,
    iss noch früh am morgen, aber ich denke,
    http://www.galileocomputing.de/openbook/c_von_a_bis_z/
    da wird ihnen in Kapitel 20 geholfen.
    Falls nicht, gibts hier viele nette fähige leute.

    mfg Flo



  • Hallo!

    Funktionen, die variable Argumentenlisten akzeptieren kenne ich und ich weiß, dass man sie auf die in dem besagten Kapitel beschriebene Weise implementieren kann. Es geht mir um die Frage, wie man einer solchen Funktion eine zur Kompilierzeit noch unbekannte Anzahl an Argumenten übergeben kann.

    Ich weiß, dass ich mit va_arg und Co eine Funktion schreiben kann, die variable Argumentenlisten akzeptiert.

    Ich will nochmal ein Beispiel nennen, um mein Problem klarer zu machen. Die printf()-Funktion verwendet variable Argumentenlisten, die man mit va_arg realisieren kann. Das ist mir klar.

    Wende ich printf() an, kann ich entweder 2 Integer-Werte ausgeben oder aber 4 oder soviele ich will, wenn ich das beim Programmieren schon weiß.
    Mein Problem ist aber, dass ich der Funktion eine Zahl von Variablen übergeben möchte, die auch während des Programmierens noch nicht bekannt ist. Dafür möchte ich diese Funktion jedoch nicht extra umschreiben. 😉 Mir ist klar, dass man sowas im Allgemeinen nicht häufig braucht, es geht lediglich um die Frage, ob sowas prinzipiell möglich ist.

    Ich hoffe, dass das jetzt etwas klarer wurde.

    Gruß, Volker



  • hi ! 🕶
    du kannst die argumente ja per zeiger übergeben

    // child.exe
    #include <stdio.h>
    int main( int argc, char* argv[] )
    {
    	int i;
    	if ( argc )
    	{
    		printf( "but i know: %d\n", argc );
    		for ( i=0; i<argc; i++ )
    		puts(argv[i]);
    	}
    }
    
    #include <process.h>
    
    int main()
    {
    	char* p = "dont know how much arguments i have";
    	_execl( "child.exe", p );
    }
    

    gruß
    e.p.



  • Ging das bei dir?

    Bei mir nicht, vielleicht habe ich aber auch etwas falsch gemacht. Jedenfalls ist execl immer fehlgeschlagen, wenn das Argument Whitespaces enthielt.

    Davon abgesehen funktioniert das leider nur im besonderen Fall der Charpointer. Wenn die jeweilige Funktion aber eine variable Anzahl von Integern by Value erwartet, wie geht's dann? Das ist jetzt wirklich nur noch ein Gedankenspiel und es würde mich auch wundern, aber es interessiert mich halt 😉

    Gruß, Volker



  • V0lle schrieb:

    Ging das bei dir?

    ja, funktioniert. allerdings unter windows. möglicher weise geht das unter linux nicht.

    V0lle schrieb:

    Wenn die jeweilige Funktion aber eine variable Anzahl von Integern by Value erwartet, wie geht's dann? Das ist jetzt wirklich nur noch ein Gedankenspiel und es würde mich auch wundern, aber es interessiert mich halt 😉

    man kann ja auch integerwerte in eine zeichenkette einpacken und wieder rauspulen.
    gruß,
    e.p.



  • Bööööses Linux, böse böse! 😉

    Man kann Integer in ne Zeichenkette packen, aber man kann ja die Zeichenkette nicht übergeben, weil in meinem Beispiel ja eine kommaseparierte Liste von Integern als Argument erwartet wird und kein Pointer auf den Anfang der Zeichenkette.

    Oder versteh ich schooon wieder etwas falsch? Also mein Prototyp ist jetzt beispielsweise:

    int berechne_Durchschnitt(int a1, ...);

    Diese Funktion soll beispielsweise den Durchschnitt der übergebenen positiven Integer-Werte ermitteln und erkennt das letzte Argument daran, dass es negativ ist.

    Jetzt könnte ich aufrufen "berechne_Durchschnitt(1,3,14,-1)" oder "berechne_Durchschnitt(17,1,235,1,66,2,2,26,2,26,3462,262,-1)".

    Wenn ich aber jetzt n dynamisches Integer-Array mit "int Zahlen[]" deklariere und das letzte Element mit -1 kenntlich mache, kann ich es ja trotzdem nicht übergeben, denn es werden ja Integer erwartet.

    "berechne_Durchschnitt(Zahlen)" geht dann also nicht, weil Zahlen ein Pointer ist und kein Integer. Eventuell steh ich etwas auf dem Schlauch, aber wenn du das mit der Zeichenkette anders gemeint hast, dann setz das doch vllt in meinem Beispiel um, wenn du magst, damit es auch für mich Trottel klar wird 😉

    Gruß, Volker



  • hi !
    ich habe nicht alles gelesen, was du geschrieben hast.
    aber für eine variable liste von parametern kannst du ach
    int execv(const char *path, char *const argv[]);
    benutzen
    🙂

    brauchst du das für einen bestimmten zweck oder ist es nur so, prizipielles interesse ?
    gruß
    d.v.



  • Danke für die Antwort, genau DAS habe ich soeben auch herausgefunden 😉

    Das ist nur prinzipielles Interesse, welches sich beim Benutzen dieser Funktion einfach ergeben hat. Die Frage, ob sowas prinzipiell auch irgendwie mit "execl()" oder meiner Beispielfunktion "berechne_Durchschnitt()" möglich wäre, bleibt daher trotzdem offen. 🙂 Meine Vermutung ist wie gesagt ohnehin, dass es nicht geht. 😉

    Gruß, Volker



  • Übrigens: Da mir eure sachlichen und relativ zügigen Antworten gefallen, habe ich mich entschlossen, euch auch in Zukunft unter Umständen häufiger zu nerven, falls sich Fragen ergeben. Und daher habe ich mich ma eben registriert.

    Gruß, Volker



  • übrigens: für dein Beispiel braucht man keine variable Parameterlist -.-

    #include <stdio.h>
    #include <malloc.h>
    
    int durchschnitt(int* pZahlen, int iAnzahl)
    {
        int Summe = 0, n;
        for (n = 0; n < iAnzahl; n++)
            Summe += pZahlen[n];
        return (Summe / iAnzahl);
    }
    
    int main(int argc, char** argv)
    {
        int iAnzahl, iDurchschnitt, n;
        int* pZahlen;
        printf("Wie viele Zahlen? ");
        scanf("%d", &iAnzahl);
        pZahlen = (int*)malloc(iAnzahl * sizeof(int));
        for (n = 0; n < iAnzahl; n++)
        {
            printf("> ");
            scanf("%d", &(pZahlen[n]));
        }
        iDurchschnitt = durchschnitt(pZahlen, iAnzahl);
        printf("Der Durchschnitt: %d\n", iDurchschnitt);
        free((void*)pZahlen);
        getchar();
        return 0;
    }
    


  • Hya ! 🕶

    V0lle85 schrieb:

    Übrigens: Da mir eure sachlichen und relativ zügigen Antworten gefallen, habe ich mich entschlossen, euch auch in Zukunft unter Umständen häufiger zu nerven, falls sich Fragen ergeben. Und daher habe ich mich ma eben registriert.

    Gruß, Volker

    Willkommen im Forum !



  • Hi!

    Proggingmania: Danke dir! 🙂

    DrakoXP: Auch dir danke ich für deine Antwort. Ich weiß, dass man mein Beispiel auch mit Übergabe by Reference realisieren kann. In meinem ersten Post schrieb ich auch, dass ich das weiß, dass mich die Frage jedoch einfach prinzipiell interessiert, ob das irgendwie funktionieren würde, falls mir mal eine höchstkomplizierte, schon vordefinierte Funktion begegnet, die mit variablen Argumentenlisten arbeitet und nicht mit Pointern, und die ich nicht selbst nochmal implementieren möchte.

    Könnte man in irgendeiner Weise den Argumentenstack manuell aufbauen und direkt übergeben, würde dies das natürlich ermöglichen, gleichzeitig aber natürlich sämtliche Überprüfungen durch den Compiler umgehen. Daher beantworte ich mir diese Frage jetzt einfach mit 'Nein'.

    Danke nochmal!

    Gruß, Volker



  • also im Normalfall muss feststehen, wie viele Parameter du der Funktion gibst!
    und das zur Compile-Time.

    das einige, was mir einfällt, wie das gänge,
    ist per Assembler:

    du pushst nacheinander die Parameter auf den Stack, callst dann deine Funtkion,
    die eine variable Parameterliste erwartet, und am Schluss machst du den Stack wieder sauber.



  • also, ich hab das jetzt mal ausprobiert mit inline-asm.
    das ganze läuft mit Visual C++, bei anderen Compilern sieht der inline-asm-Teil wahrscheinlich etwas anders aus.

    naja, hier is der Code:

    #include <stdio.h>
    #include <stdarg.h>
    #include <malloc.h>
    
    int __cdecl durchschnitt(int iAnzahl, ...)
    {
        unsigned int Summe = 0, n = 0;
        va_list pArgList;
        va_start(pArgList, iAnzahl);
        while (n < iAnzahl)
        {
            Summe += va_arg(pArgList, unsigned int);
            n++;
        }
        return (Summe / iAnzahl);
    }
    
    int main(int argc, char** argv)
    {
        unsigned int iAnzahl, iDurchschnitt, n, Extra, Buf;
        unsigned int* pZahlen;
        printf("Wie viele Zahlen? ");
        scanf("%d", &iAnzahl);
        pZahlen = (unsigned int*)malloc(iAnzahl * sizeof(unsigned int));
        for (n = 0; n < iAnzahl; n++)
        {
            printf("> ");
            scanf("%d", &(pZahlen[n]));
        }
        if (iAnzahl > 0)
        {
            __asm
            {
                push ecx;
                push eax;
                mov ecx, iAnzahl;
    push_loop:
                mov n, ecx;
                push eax;
                push ebx;
                push ecx;
                push edx;
            }
            Extra = pZahlen[n - 1];
            __asm
            {
                pop edx;
                pop ecx;
                pop ebx;
                pop eax;
                mov eax, Extra;
                push eax;
                dec ecx;
                cmp ecx, 0;
                jne push_loop;
                push iAnzahl;
                call durchschnitt;
                mov iDurchschnitt, eax;
                push eax;
                push ebx;
                push ecx;
                push edx;
            }
            Extra = (iAnzahl + 1) * 4;
            __asm
            {
                pop edx;
                pop ecx;
                pop ebx;
                pop eax;
                add esp, Extra;
                pop eax;
                pop ecx;
            }
        }
        printf("Der Durchschnitt: %d\n", (int)iDurchschnitt);
        free((void*)pZahlen);
        getchar();
        getchar();
        return 0;
    }
    


  • Hey! Du bist mein Held und Retter!

    Der Hinweis darauf, dass es mit inline-asm möglich ist, (womit ich mich noch nicht wirklich auseinander gesetzt habe,) hätte mir allein schon vollkommen gereicht.

    Dass du das Ganze auch noch umgesetzt hast setzt dem die Krone auf, vielen vielen Dank! 🙂 Das ist genau das, was ich gesucht habe.

    Das besonders Tolle daran ist, dass ich mich vorher noch nicht mit inline-asm auseinander gesetzt habe und mir das Beispiel erstmal grob gezeigt hat, wie das funktioniert.

    Vielen Dank nochmal!
    Gruß, Volker



  • Naja, wenn es dann unbedingt beliebig viele Parameter sein sollen, sollte man besser die Standard-Library von C benutzen und sich nicht so was mit inline assembler zusammenfrickeln 😉

    #include <stdio.h>
    #include <stdarg.h>
    
    int sumOfArguments(unsigned int numberOfArguments, ...)
    {
    	int sum = 0;
    	unsigned int i = 0;
    	va_list argumentList;
    	va_start(argumentList, numberOfArguments);
    
    	for (i = 0; i < numberOfArguments; ++i)
    		sum += va_arg(argumentList, int);
    
    	va_end(argumentList);
    
    	return sum;
    }
    
    int main()
    {
    	printf("%d\n", sumOfArguments(6, 2, 3, 4, 5, 6, 7));
    
    	return 0;
    }
    

    Gruß
    Don06



  • Hi Don.

    Danke für deine Antwort, aber eventuell hast du meine Frage nicht richtig verstanden. Dein Programm tut nicht das, worum es mir ging 😉

    Trotzdem danke.
    Meine Frage wurde jetzt in allen Belangen beantwortet! 🙂

    Gruß, Volker



  • Upps, du wolltest das zur Laufzeit bestimmen? Ok, sorry 😉



  • Die sinnvolle Loesung waere hier ein Array oder eine Liste zu verwenden. Assembler mag lustig zum rumspielen sein, aber produktiv ist es hier nicht.



  • Hallo.

    @Shade:

    Dazu müsste die Funktion allerdings wissen, wie sie mit einer Liste bzw. einem Array umzugehen hat. Das weiß die Funktion "durchschnitt(int numberOfArguments, ...)" aber nicht und das ist (für mein Verständnis) auch so beabsichtigt. Denn gerade das war ja meine Frage, wie man soetwas bei einer derart vorgegebenen Funktion realisieren kann.

    Wenn ich die Funktion durchschnitt() selbst implementieren müsste, ist mir vollkommen klar, dass ich hierfür Listen, o.ä. verwenden sollte.

    @Don:

    Kein Problem, jede gut gemeinte Antwort freut mich 😉

    Gruß, Volker


Anmelden zum Antworten