Zeiger als Parameter von Funktionen
-
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).
-
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 das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.
-
Sone 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 das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.
Welche Nachteile bitte?
-
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).
Hä? Was schert dich was der Benutzer denken könnte oder nicht?
-
Naja, ich nutze immer noch rohe Zeiger.
Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.
-
Ethon schrieb:
Sone 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 das kompensiert die Nachteile, die Zeiger mit sich bringen? Glaube ich nicht.
Welche Nachteile bitte?
Beispielsweise die hohe Fehleranfälligkeit durch falsche Initialisierung.
Bei einem so trivialen Fall wie deinem macht es allerdings wohl keinen Unterschied.
P.S.: Was, wenn durch deinen Code der Eindruck entsteht, deine Klasse führt im Destruktordeleteauf dem Zeiger aus (managt also den Speicher, der dynamisch allokiert sein sollte)?Nathan schrieb:
Naja, ich nutze immer noch rohe Zeiger.
Wenn ich bspw. eine Klasse habe, die einen änderbaren, ab und zu auch mal ungültigen Verweis auf eine andere Klasse braucht.Ja, da wirst du zu sowas gezwungen. Das ist in Ordnung.