Zeiger als Parameter von Funktionen
-
Nathan schrieb:
Referenzen sind wie Zeiger
Ganz so einfach ist es auch nicht ...
-
Belli schrieb:
Nathan schrieb:
Referenzen sind wie Zeiger
Ganz so einfach ist es auch nicht ...
Oh Mist, hab ich übersehen.
Das ist natürlich Blödsinn.
-
Ich bin beruhigt, dass Du das noch mal bestätigst ...
-
Danke schonmal für die Antworten
!Ich arbeite mich gerade durch ein Grundlagenbuch durch. Dabei haben sich diese Fragen ergeben, besonders der Zeiger auf einen Zeiger. In einer Musterlösung zu einer Aufgabe wird dieser verwendet, aber vorher und nacher nicht richtig erklärt. Da steht nur, dass der doppelstern benötigt wird, damit die Änderung innerhalb der Funktion auf die Variable durschlägt. So oder so ähnlich stand es da. Den Abschnitt zu den Parametern (zwei seiten weiter vorne) habe ich mir gerade noch einmal durchgelesen.
Ok 1, 2 und 3 habe ich nun verstanden denke ich.
Beispiel 4 hat sich in meinem Kopf auch entknotet. Ich finde es aber immernoch verwirrend.
Daraus ergibt sich eine neue Frage die zum Teil ja schon beantwortet wurde: Wo brauchst man das bzw. wo finden die Beispiele Anwendung. Es führen ja alle zum selben Ergebnis, warum sollte man welche Variante verwenden und warum nicht.
LG
-
Ich arbeite mich gerade durch ein Grundlagenbuch durch. Dabei haben sich diese Fragen ergeben, besonders der Zeiger auf einen Zeiger. In einer Musterlösung zu einer Aufgabe wird dieser verwendet, aber vorher und nach[h]er nicht richtig erklärt.
oO
Ich glaube mittlerweile müssten wir mal eine Bücherverbrennung für Programmierfachliteratur organisieren.
Daraus ergibt sich eine neue Frage die zum Teil ja schon beantwortet wurde: Wo brauchst man das bzw. wo finden die Beispiele Anwendung. Es führen ja alle zum selben Ergebnis, warum sollte man welche Variante verwenden und warum nicht.
Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.
P.S.: call-by-value ist auch immer eine Möglichkeit
aber es kommt eben drauf an
-
mawilab schrieb:
Beispiel 4 hat sich in meinem Kopf auch entknotet. Ich finde es aber immernoch verwirrend.
void f(){ int value = 1; int* value_zeiger = &value; int** value_zeiger_zeiger = &value_zeiger; } /* +-----+ +--------------+ +---------------------+ |value| | value_zeiger | | value_zeiger_zeiger | +-----+ +--------------+ +---------------------+ | int | <------| int* | <----------| int** | +-----+ +--------------+ +---------------------+ | 1 | | &value | | &value_zeiger | +-----+ +--------------+ +---------------------+ */ void increment(int** ppi){ /* ppi ist eine Kopie des Funktionsarguments / z.B. bei increment(value_zeiger_zeiger): +-------------------+ | ppi | +-------------------+ | int** | +-------------------+ | &value_zeiger | +-------------------+ }Wofür man das braucht?
Jedesmal, wenn Du den Wert einer Variable (dritte Zeile in der Grafik oben) in einer Funktion ändern willst. Allerdings sind dafür Referenzen kompakter und weniger fehlerträchtigvoid bieg_zeiger_um(int*& p, int* pp){ p=pp; } void bieg_zeiger_um(int** p, int* pp){ if(p != nullptr) *p=pp; } void g(){ int a = 0; int b = 1; int* p = &a; bieg_zeiger_um(p, &b); // hin... bieg_zeiger_um(&p, &a); // und her... }Ich muss weg: die lokale Hausbrauerei wartet!

-
@Sone, Belli:
Mir ist schon klar, dass Referenzen nicht wie Zeiger sind.
Da die Verwendung in diesem Zweck ähnlich ist, wollte ich den TE nicht weiter verwirren.
-
Wofür man das braucht?
Jedesmal, wenn Du den Wert einer Variable (dritte Zeile in der Grafik oben) in einer Funktion ändern willst. Allerdings sind dafür Referenzen kompakter und weniger fehlerträchtigAber das funktioniert doch auch so wie im Beispiel 1 oder 2. Warum also so umständlich?
Schade, dass ich schon auf Seite 173 bin und noch nie die Bezeichnung "Rohe Zeiger" gehört habe, scheint wichtig zu sein. Ich werde mir merken Referenzen zu verwenden, welchen bislang zwei Seite gewidmet wurden. Naja es folgen ja noch ein paar Seiten evtl. wird ja noch einiges erklärt, sonst stelle ich mein Exemplar gern der erwähnten Verbrennung zur Verfügung!
LG
-
Kannste schon dyn. Speicherreservierung?
Wenn notwendig, dann das ganze in einen std::unique_ptr packen oder in einen std::auto_ptr, falls dein Compiler kein C++11 unterstützt.
-
Sone schrieb:
Rohe Zeiger wirst du in C++ fast nie finden. Gerade mehrfache Indirektion wird in C++ eigentlich gar nicht verwendet. Das ist eine C-Sache; mehrfache Indirektion wurde da für mehrdimensionale Arrays verwendet. In C++ gibt es dafür viel bessere Lösungen.
Warum soll so ein Schwachsinn (Doppelzeiger für Arrays) in C auf einmal akzeptabel sein?
-
Mehrdimensionale Arrays gehen nicht anders?!
-
mawilab schrieb:
Wofür man das braucht?
Jedesmal, wenn Du den Wert einer Variable (dritte Zeile in der Grafik oben) in einer Funktion ändern willst. Allerdings sind dafür Referenzen kompakter und weniger fehlerträchtigAber das funktioniert doch auch so wie im Beispiel 1 oder 2. Warum also so umständlich?
Die Beispiele sind natuerlich gekuenstelt. Aber der Groschen ist gefallen?!
Einfache Aufgabe:
Eine bestimmte Bakterienkultur verdoppelt sich jeden Zyklus.
Schreib mir doch bitte eine(zwei) Funktion, die die "population" jeweils verdoppelt, so dass das folgende Programm 16 ausgibt.#include <iostream> int main(){ int population=1; for(int i=0; i<4; ++i) /* hier */ std::cout << population << '\n'; }Du wirst merken, dass Du entweder Zeiger, oder Referenzen brauchst.
-
Ok, hier die erste Lösug ohne zeiger oder Referenz die 16 Aaugibt:
#include <cstdlib> #include <iostream> using namespace std; int verdoppel(int population) { return population * 2; } int main(int argc, char *argv[]) { int population=1; for(int i=0; i<4; ++i) population = verdoppel(population); std::cout << population << '\n'; system("PAUSE"); return EXIT_SUCCESS; }Bischen gemein, da ich es mit dem Rückgabewert gemacht habe
!Nun zu der zweiten Lösung:
#include <cstdlib> #include <iostream> using namespace std; void verdoppel(int *popZeiger) { *popZeiger = *popZeiger * 2; } int main(int argc, char *argv[]) { int population=1; int *popZeiger = 0; popZeiger = &population; for(int i=0; i<4; ++i) verdoppel(popZeiger); std::cout << population << '\n'; system("PAUSE"); return EXIT_SUCCESS; }Das ist eigtl. klar, die Frage ist warum man es so machen sollte:
#include <cstdlib> #include <iostream> using namespace std; void verdoppel(int **popZeiger) { **popZeiger = **popZeiger * 2; } int main(int argc, char *argv[]) { int population=1; int *popZeiger = 0; popZeiger = &population; for(int i=0; i<4; ++i) verdoppel(&popZeiger); std::cout << population << '\n'; system("PAUSE"); return EXIT_SUCCESS; }Also mit einem Zeiger auf einen Zeiger

LG
-
TyRoXx schrieb:
Sone schrieb:
Rohe Zeiger wirst du in C++ fast nie finden. Gerade mehrfache Indirektion wird in C++ eigentlich gar nicht verwendet. Das ist eine C-Sache; mehrfache Indirektion wurde da für mehrdimensionale Arrays verwendet. In C++ gibt es dafür viel bessere Lösungen.
Warum soll so ein Schwachsinn (Doppelzeiger für Arrays) in C auf einmal akzeptabel sein?
Na, für dynamische mehrdimensionale Arrays bleibt dir in C doch nichts übrig?
Sry, ich bin kein Cler
aber wie denn sonst?
-
mawilab schrieb:
Bischen gemein, da ich es mit dem Rückgabewert gemacht habe

Naja, gemein ist das nicht, und jeder hier würde die Funktion auch genau so schreiben. Das ist in Java so und in C++ nicht anders.
mawilab schrieb:
Nun zu der zweiten Lösung:
void verdoppel(int *popZeiger) { *popZeiger = *popZeiger * 2; }Ja. Das war eine Lösung, die ich mir vorgestellt habe: Du willst den Wert von
populationändern, also musst Du einen Zeiger aufpopulationbenutzen.mawilab schrieb:
Das ist eigtl. klar, die Frage ist warum man es so machen sollte:
void verdoppel(int **popZeiger) { **popZeiger = **popZeiger * 2; }Also mit einem Zeiger auf einen Zeiger

Gibt keinen Grund. Zeiger auf Zeiger sind eher selten. Wie Sone schon angemerkt hat kommen sie bei mehrdimensionalen (ragged) Arrays vor. Manchmal wird argv so deklariert:
int main(int argc, char** argv){ // argv[0] bis argv[argc-1] enthalten die Kommandozeilenparameter }Aber um beim Beispiel mit
verdoppelzu bleiben: Du brauchst keinenint**um einenintzu ändern - das ist einfach unnötig kompliziert. Siehe mein früheres Beispiel mitbieg_zeiger_um()- da war ein Zeiger auf Zeiger "nötig".Um die Referenz hast Du Dich gedrückt - dabei ist das noch "die natürlichste" Art,
verdoppelin C++ zu schreiben.
-
Doppelzeiger "braucht" man in C auch noch, wenn man eine Funktion hat, die einen Zeiger auf ein gültiges Objekt erzeugen soll.
err_code_t create_some_type_obj(some_type ** p) { some_type * zeiger_auf_gueltiges_some_type_objekt = malloc(...); *zeiger_auf_gueltiges_some_type_objekt = irgendwelche_daten; //ggf. mehr zeugs *p = zeiger_auf_gueltiges_some_type_objekt; } //... some_type * p = 0; err_code_t err = create_some_type_obj(&p); //hier wird ein some_type Objekt erzeugt //wenn bei create_some_type_obj alles gut gegangen ist, zeigt p nun nicht mehr auf 0, sondern auf ein gueltiges ObjektIch finde das zwar nicht schön, und würde sofern möglich, mit Rückgabewerten arbeiten, aber in vielen C-APIs sieht man sowas. In C++ braucht man das aber nicht mehr. Da kann man Referenzen benutzen.
-
Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.
Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.
-
Ethon schrieb:
Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.
Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.
Und wobei sind Dir da bis jetzt Probleme entstanden?
-
Vermutlich da nicht, abgesehen davon, dass man ein Ampersand mehr braucht und der DAU da aus Versehen einen nullptr übergeben kann.
Das Problem ist eher bei dynamischer Speicherallozierung, dass das u.U. Speicherlecks enstehen können.foo *f = new foo; f->bar(); delete foo;Sieht zwar gut aus, aber was machen, wenn bar() wirft?
foo *f = new foo; try { f->bar(); } catch(...) { delete f; throw; } delete f;Ein std::unique_ptr, erledigt das Löschen automatisch.
Anderer Fall:foo::foo() : bar1(new bar), bar2(new bar2) {}Was machen wenn die Allokierung von bar2 fehlschlägt?
bar1 verbleibt im Speicher.
Und noch viele änliche Situationen.
-
Tachyon schrieb:
Ethon schrieb:
Rohe Zeiger sollten gänzlich vermieden werden. Es heißt nicht, dass man in der Praxis keinen Gebrauch davon macht. Im Gegenteil. Aber man benutzt Referenzen eher als Standard, und Zeiger nur wenn man dazu gezwungen wird.
Finde ich nicht. Ich benutze Zeiger sehr gerne wenn ich verdeutlichen möchte dass nur eine Referenz und kein Wert gespeichert wird, weil das mit der C++-Referenzen-Syntax leicht untergehen kann.
Und wobei sind Dir da bis jetzt Probleme entstanden?
Naja, ich habe beispielsweise eine Klasse, die aus Performancegründen Strings nicht selbst speichert, die gehören in einen Stringpool.
Sieht bei mir so aus:
class Foo { private: std::string const& bar; public: Foo(std::string const* bar) : bar(*bar) { } };Den String per Referenz entgegenzunehmen könnte je nach Benutzer meines Codes zu dem Irrtum führen, hier würde ein String kopiert (was meistens auch zu erwarten ist).