Funktion mit variabler Argumentliste
-
Ich habe mich mit va_args nie beschäftigt, und werde es nicht tun.
Aber ich denke mal das double bedeutet, dass dieses Element als Double interpretiert werden soll.
-
also, va_arg liefert dir ja den nächsten parameter zurück, und der zweite wert gibt an von welchem typ dieser parameter ist (bspw. double). Beispiel
int funktion (int p, ...) { va_list a; va_start(a, p); va_arg(a, int); return a+p; (va_end(a);) } // aufruf: funktion(2, 4); // rückgabewert 6
übrigens: der erste parameter von va_start gibt nicht die anzahl der variablen an, die man noch haben möchte, sondern den letzten festen parameter der funktion
-
Interessantes Thema. Hab da eben mal nach gesucht und das hier gefunden.
http://www.cplusplus.com/reference/clibrary/cstdarg/
das dürfte alle Fragen beantworten.
-
Auf
va_list
& co sollte man eher verzichten. Nimm stattdessen lieber einen Proxy mit überladenem Operator oder gerade bei einem Durchschnitt wäre die Übergabe von Iteratoren eine gute Idee.Wieso dein Programm nicht funktioniert:
va_list
& co arbeiten direkt auf dem rohen Stack. Die übergebenen Parameter müssen 1:1 den Parametern entsprechenen, welche übergeben wurden, sonst hat das undefinierte Folgen. Zudem müssen die Parameter reine PODs sein.Durchschnitt(3,4,6,8); // <- du übergibst int Zahlen //... va_arg(Argumente,double); // <- und liest double aus, undefiniert, kann fatal sein!
Grüssli
-
Grundsätzlich brauchst du starg.h nicht. Du musst nur wissen wie deine Variablen auf den Stack gelegt werden und wie du durch sie "durchwandern" kannst.
Für dein Durchschnitt-Beispiel ist es eigentlich ganz einfach. Du holst dir die Adresse der int-Variable, springst dann auf die Speicherstelle dahinter, weil dort ja deine double Variablen anfangen und durchläufst diese mit einem double Zeiger. Ganz simpel, oder?
#include <stdarg.h> #include <iostream> using namespace std; double Durchschnitt (int Anzahl,...) { int* pint; double* pdouble; pint = &Anzahl; pdouble =(double*) (((char*)pint) + sizeof(int)); for (int i=0; i < Anzahl; ++i) cout << *pdouble++ << endl; return 0.0; } int main (void) { Durchschnitt (4, 1.2, 2.4, 3.6, 5.5); }
Schwierig wird es wenn Variablen unterschiedlichen Typs auf den Stack kommen können. Die Makros aus starg.h helfen etwas beim "Zeigerhüpfen", aber die eigentliche Arbeit musst du trotzdem selbst leisten.
P.S.: Bitte kein Blabla über "das heißt nicht Stack" oder "man verwendet doch keine C-Casts", ist ja nur schnell hingeschmiert.
-
Hallo,
vielen Dank schonmal für die Antworten. Da bin ich gleich schon ein Stück weiter.
Das mit den Operatoren wusste ich nicht, doch habe ich mein Programm nochmals abgeändert:double Durchschnitt (int Anzahl,...) { va_list Argumente; //Variable vom Typ va_list wird angelegt va_start (Argumente, Anzahl); //Mittels diesem Array wird die Parameteranzahl an das Array "Argumente" übergeben. Der Parameter Anzahl definiert die Arraygröße float sum = 0.0; for (int i=0; i<Anzahl; i++) { sum += va_arg(Argumente,float); //liefert jeweils ein Element zurück } va_end(Argumente); //löscht das Array --> gibt Speicher wieder frei return sum/Anzahl; } ... cout << Durchschnitt(3,4.4,6.7,8.9);
Nun sind auch die übergebenen Zahlen vom gleichen Typ, doch es kommt ne Zahl raus die kleiner als -3 Millionen ist! Es funzt also immer noch nicht!
Wie soll ich denn mit überladenen Operatoren so ein Problem bewerkstelligen? Das Hauptziel war es ja eine Funktion zu deklarieren mit variablen Parametern.
Vielen Dank für eure Hilfe
lg, freakC++Ps: @Frager: deinen Post habe ich zu spät gesehen. WErd ihn mir jetzt angucken.
-
freakC++ schrieb:
...
double Durchschnitt (int Anzahl,...) { va_list Argumente; //Variable vom Typ va_list wird angelegt va_start (Argumente, Anzahl); //Mittels diesem Array wird die Parameteranzahl an das Array "Argumente" übergeben. Der Parameter Anzahl definiert die Arraygröße float sum = 0.0; for (int i=0; i<Anzahl; i++) { sum += va_arg(Argumente,float); //liefert jeweils ein Element zurück } va_end(Argumente); //löscht das Array --> gibt Speicher wieder frei return sum/Anzahl; } ... cout << Durchschnitt(3,4.4,6.7,8.9);
...
Da hast Du genau das Problem, das Dravare angesprochen hat.
Du übergibst hier (cout << Durchschnitt(3,4.4,6.7,8.9);
) double-Werte, und versuchst in Deiner Funktion, diese als float zu interpretieren. Das geht dann schief.
-
Deswegen sage ich ja, lass die Finger davon. Erst recht, wenn du dich in C++ noch nicht so auskennst.
va_list
ist EXTREM fehleranfällig!Durchschnitt(3,4.4,6.7,8.9);
Hier ürbigbst du:
int, double, double, doubleAuslesen tust du:
int, float, float, floatDas kann nicht gut gehen
Lass einfach die Finger davon. In C++ wird sowas sowieso nie eingesetzt. Mach zum Beispiel besser sowas:#include <iterator> template<typename IterT> typename std::iterator_traits<IterT>::value_type average(IterT first, IterT last) { typedef typename std::iterator_traits<IterT>::value_type ValueType; unsigned int count = 0; ValueType result = ValueType(); for(; first != last; ++first, ++count) { result += *first; } return result / count; } // Verwendung, z.b. mit deinem Beispiel: double arr[] = { 4.4, 6.7, 8.9 }; double result = average(arr, arr + 3);
Grüssli
-
Dravere schrieb:
Auf
va_list
& co sollte man eher verzichten. Nimm stattdessen lieber einen Proxy mit überladenem Operator oder gerade bei einem Durchschnitt wäre die Übergabe von Iteratoren eine gute Idee.Wieso dein Programm nicht funktioniert:
va_list
& co arbeiten direkt auf dem rohen Stack. Die übergebenen Parameter müssen 1:1 den Parametern entsprechenen, welche übergeben wurden, sonst hat das undefinierte Folgen. Zudem müssen die Parameter reine PODs sein.Durchschnitt(3,4,6,8); // <- du übergibst int Zahlen //... va_arg(Argumente,double); // <- und liest double aus, undefiniert, kann fatal sein!
Grüssli
Kurze Frage, was meinst du mit Proxy, könntest du mir bitte dazu mal nen Link geben, hab leider noch Code rumfliegen mit dieser va_list und würde die gern austauschen.
Mfg Marco
-
http://de.wikipedia.org/wiki/Stellvertreter_(Entwurfsmuster)
Und als Code, hmmm, mal sehen:
#include <iostream> // Der Proxy: template<typename ValueT> class AvarageParams { // Attributes // private: ValueT m_value; unsigned int m_counter; // Constructors // public: AvarageParams(ValueT const& init) : m_value(init) , m_counter(1) { } // Operators // public: operator ValueT() const { return m_value / m_counter; } AvarageParams& operator +(ValueT const& value) { m_value += value; ++m_counter; return *this; } }; template<typename ValueT> AvarageParams<ValueT> avarage(ValueT const& init) { return AvarageParams<ValueT>(init); } int main() { double result = avarage(10.0) + 20.0 + 40.0; std::cout << result << std::endl; std::cin.get(); return 0; }
Irgendsowas in der Art
Edit: Ich glaube, dies ist ein perfektes Beispiel für eine schlechte Anwendung der Operator Überladung. Ist mir gerade aufgefallen, als ich den Code aus dem Testprojekt löschen wollte.
Aber es sollte ja darum gehen, die Verwendung von Proxies zu zeigenGrüssli
-
anstatt so einen batzen zu schreiben benutz ich lieber va_list
-
player424 schrieb:
anstatt so einen batzen zu schreiben benutz ich lieber va_list
Schreibfaulheit kommt meistens auch zusammen mit Fehleranfälligkeit
Und ich würde ja sowieso zur Lösung raten, welche ich als erstes geschrieben habe. Das ist schliesslich nichts anderes als die Funktion selber, nur ein wenig erweitert, damit sie mit Iteratoren funktioniert. Dadurch hast du eine höhere Flexibilität.
Grüssli
-
player424 schrieb:
anstatt so einen batzen zu schreiben benutz ich lieber va_list
Es geht beim Programmieren aber nicht darum, mit möglichst wenig Code möglichst viel Kot zu produzieren...
-
Hallo zusammen,
natürlich kann es sein, dass dieser Stil entweder nicht gut ist oder nicht gebräuchlich. In meinem Buch wird er jedoch behandelt und wenn ich das Kaptiel "Funktionen mit variabler Parameterliste" nicht interessant fände, dann würde ich mich wahrscheinlich auch nicht mehr damit beschäftigen.Vielen Dank für den vielen "Ersatzcode", doch möchte ich einmal mein Programm zum Laufen bekommen. Ich habe nämlich gelesen, dass die Standartfunktionen wie printf genauso mit diesen Makros funktionieren (und das soll was heißen)
Ich bin auch blöd....jetzt habt ihr mir tausendmal gesagt, dass ich gleiche Typen einsetzten soll und ich vergesse den Rückgabewert zu ändern. Also das Programm sieht jetzt so aus:
float Durchschnitt (float Anzahl,...) { va_list Argumente; va_start (Argumente, Anzahl); float sum = 0.0; for (float i=0; i<Anzahl; i++) { sum += va_arg(Argumente,float); } va_end(Argumente); return sum/Anzahl; } void __fastcall TForm1::FormCreate(TObject *Sender) { cout << Durchschnitt(3,4.4,6.7,8.9);
Der erste Parameter ist pflicht und gibt die Anzahl der Parameter an. Daher muss dieser doch eine ganze Zahl sein (so wirds auch in meinem Buch beschrieben). Ich kann schließlich nicht 3,4 Parameter haben.
Dennoch funktioniert das Programm nicht. Es kommt nicht der Durchschnitt heraus, sondern die Zahl -35791394,6666667!! Keine Ahnung, was der Compiler da anstellt. Dies ist aber kein zufälliger Wert, d.h. er erscheint immer wieder!!
Was mache ich noch falsch??
Vielen Dank für eure Hilfe
lg, freakC++PS: Nehmt es mir nicht Übel, dass ich dieses Programm zu Ende stellen möchte!!
-
freakC++ schrieb:
Vielen Dank für den vielen "Ersatzcode", doch möchte ich einmal mein Programm zum Laufen bekommen. Ich habe nämlich gelesen, dass die Standartfunktionen wie printf genauso mit diesen Makros funktionieren (und das soll was heißen)
printf
ist eine C Funktion. In C hat man da kaum andere Möglichkeiten. In C++ ist die variable Argumentliste vonprintf
einer der Gründe, wieso manprintf
nicht benutzt. Ausgaben & Eingaben werden in C++ über die Streams geregelt. Auf C Funktionen probiert man in C++ so oft wie möglich zu verzichten.freakC++ schrieb:
Was mache ich noch falsch??
Immer noch das gleiche. Du übergibst doubles und liest floats aus.
4.3 // -> double 4.3f // -> float
Ändere einfach diese Zeile um:
sum += va_arg(Argumente,float);
zu:
sum += va_arg(Argumente,double);
Der Typ des Rückgabewerts und die Typen, welche du sonst in der Funktion benutzt sind scheiss egal. Du musst einfach aus der variablen Parameterliste doubles auslesen, wenn sich dort doubles befinden. Du musst immer den Typ auslesen, welcher sich dort drin befindet. Deshalb gibt man bei
printf
ja auch an, von welchem Typ der jeweilige Parameter ist, mit den Codes im String drin.Übringes, was benutzt du eigentlich für ein Buch?
Grüssli
-
freakC++ schrieb:
Hallo zusammen,
natürlich kann es sein, dass dieser Stil entweder nicht gut ist oder nicht gebräuchlich.In C++ nicht gut und deshalb nicht gebräuchlich. Eines der wichtigsten Dinge in C++ ist die Typsicherheit und die Fähigeit des Compilers, diese durchzusetzen und damit schon zur Compielzeit eine große Menge an Fehlern abzufangen. va_args & Co setzen im Gegensatz dazu auf Pointergehüpfe und mehr oder weniger willkürliches Umhercasten von Pointern und Uminterpretieren von Speicherbereichen, weshalb wie oben schon gesagt wurde das alles auch nur für PODs definiert ist. Der Compiler kann dir dabei keine Unterstützung geben, muss für bare Münze nehmen was du ihm diktierst und produziert daraus irgendwelchen Code, der funktionieren kann oder auch nicht. Um wirklich sicher zu sein dass das was du getippt hast in jedem Fall auch tut was du erwartest, müsstest du streng genommen bei jedem Funktionsaufruf schauen ob du die einzelnen Argumente auch richtig interpretierst (z.B. der float/double-Fehler). Da könntest du es fast jedesmal von Hand hintippen.
freakC++ schrieb:
In meinem Buch wird er jedoch behandelt und wenn ich das Kaptiel "Funktionen mit variabler Parameterliste" nicht interessant fände, dann würde ich mich wahrscheinlich auch nicht mehr damit beschäftigen.
Steht auf deinem Buch zufälligerweise "C/C++"? Falls ja, bist du gerade im C-Teil. Falls nicht: gibts am Anfang zu dem Abschnitt "variable Argumenteliste" einen fetten Disclaimer "möglchst nicht in C++ benutzen"? Falls auch das nicht zutrifft hat das Buch bestenfalls noch Heizwert, denn dann ist entweder das Buch oder der Kenntnisstand des Autors noch aus den Anfängen von C++ wo es eher als "C mit Klassen" benutzt wurde.
-
Hallo zusammen,
also ich benutze das Buch "C++ Objektorientierte Programmierung" von Alexander Nieman und Stefan Heitsiek!
Ich finde es recht kompliziert geschrieben, doch da ich schon ein paar Grundlagen habe (auch wenn es manchmal nicht
so scheint), komm ich bis jetzt ganz gut damit klar! Jedoch wird auch andauerndvoid main (void) { ... }
geschrieben, was auch nicht mehr den Standarts entspricht. Es handelt sich also um ein reines C++ Buch. Es ist die zweite Auflage
und ist im Jahr 2008 erschienen (also noch neu).Ich habe mir vorgenommen, verschiedene Einsteigerbücher von verschiedenen Autoren durchzuarbeiten, um ein möglichst breit
gefächertes Wissen abzubekommen. Nach diesem Buch wollte ich mir dann einen richtig schönen Wälzer anschaffen, der sich dann
auch sehr viel tiefer eingeht (ich will ja nicht immer solch primitive Programme schreiben, sondern auch welche die jemandem
etwas nützen)Ihr könnt euch das Buch ja mal anschauen:
http://www.amazon.de/C-Objektorientierte-Programmierung-Alexander-Niemann/dp/3826673743
Vielen Dank für eure Hilfe
lg, freakC++
-
Hi,
also ich bin doch ziemlich erstaunt, welche Lösungen hier angeboten werden ... immerhin ist doch gerade "Durchschnitt" eine Funktion, die locker mit einer Parameterliste mit
a) variabler Länge aber
b) festem Typ
auskommt ... und für so etwas hat Gott doch den std::vector erfunden. (oder meinetwegen ein Array).Gruß,
Simon2.
-
freakC++ schrieb:
geschrieben, was auch nicht mehr den Standarts entspricht. Es handelt sich also um ein reines C++ Buch. Es ist die zweite Auflage
und ist im Jahr 2008 erschienen (also noch neu).Ufff, dann schmeiss das Ding weg oder verwende es zum Anfeuern. Ich meine man muss nur diese zwei Rezensionen lesen:
http://www.amazon.de/product-reviews/3826673743/ref=cm_cr_dp_hist_1/279-9692699-1433456?ie=UTF8&showViewpoints=0&filterBy=addOneStarWer sein Buch überarbeitet und dann immer noch solch fatale Fehler drin hat, der hat einfach nichts mitbekommen, wie sich C++ entwickelt hat. Der ist noch auf einem uralten Stand. Wir empfehlen hier hauptsächlich die folgenden zwei:
- C++ Primer
- Thinking in C++ 1 & 2 (auch als gratis Download erhältlich)Gibt noch ein paar weitere in den FAQs.
Falls du dich schon ein wenig auskennst, wäre die Lektüre von "Die C++ Programmiersprache" von Bjarne Stroustrup auch eine Überlegung wert.Simon2 schrieb:
... und für so etwas hat Gott doch den std::vector erfunden. (oder meinetwegen ein Array).
Und deswegen habe ich die Lösung mit den Iteratoren vorgeschlagen, welche mit allen Containern, welche das Iteratoren-Konzept ünterstützen, auskommt.
Grüssli
-
freakC++ schrieb:
ich benutze das Buch "C++ Objektorientierte Programmierung" von Alexander Nieman und Stefan Heitsiek [...]
andauerndvoid main (void) { ... }
[...]
reines C++ Buch. Es ist die zweite Auflage
und ist im Jahr 2008 erschienen (also noch neu).Ein 2008er Buch mit void main (void) - das war meines Wissens nichtmal in C90 legal.
Ein ganz klarer Fall für den Schredder. Schmeiß das bloß weg! Wer weiß was für anderen Scheiß die einem verkaufen wollen, der nicht so ganz offensichtlich ist.
Simon2 schrieb:
und für so etwas hat Gott doch den std::vector erfunden. (oder meinetwegen ein Array).
Oder ganz generisch Iterator-Ranges (wurden aber auch am Anfang schon vorgeschlagen)
/edit: gna, war wieder einer schneller