warum zur Hölle mault mich der Compiler voll?
Wollte schnell mal was probieren und nun verstehe ich die Welt nicht mehr.
Runtergebrochen bleibt folgender Code übrig.
Es soll doch nur der Wert 7654 in die Adresse geschrieben werden auf die ptr zeigt.
In CodeBlocks (das vom Buch) gibts keine Fehler, ebenfalls C++17.
Problem 1 (ist dir evtl noch gar nicht aufgefallen)
Du hast zwar einen Pointer definiert, aber nicht das reserviert, worauf er zeigt.
Problem 2 (ist dir gerade auf die Füße gekracht)
Außerhalb von Funktionen/Methoden existiert keine Möglichkeit der Zuweisung.
(Zuweisungen sind nur in Anweisungsblöcken erlaubt/sinnvoll.)
ich weiß ungefähr was du mir sagen möchtest, verstehe es jedoch noch nicht ganz.
Im setup Block kann ich den Wert dem Zeiger zuweisen.
Bezieht sich das Problem nur auf Zeiger?
Denn ich kann doch jede beliebige Variable außerhalb eines Blocks initialisieren.
Nur einen Zeiger demzufolge nicht?
int a = 7654; // okay
int *p = 7654; // Fehler
zu deinem (1)
Der deklarierte Zeiger zeigt auf eine Adresse die für einen int Wert reserviert ist.
Die Adresse auf die er zeigt kann mir egal sein, muss ich nicht unbedingt wissen.
Aus meiner Sicht ist die Adresse für den Zeiger reserviert.
Noch eine Korrektur. In CodeBlocks klappt das natürlich auch nicht. Hier meckert der Compiler ebenfalls. Hatte das in geistiger Umnachtung "dummwerweise" in den main Block geschrieben und nicht außerhalb des Blocks.
Denn ich kann doch jede beliebige Variable außerhalb eines Blocks initialisieren.
Initialisieren ja. Weil das direkt mit der Definition gekoppelt ist. Das ist aber was anderes als reine reguläre Zuweisung. Du solltest inzwischen schon gemerkt haben dass Initialisierung eine Art Sonderfall ist und eigene Regeln hat
Du kannst global eine Zeiger-Variable definieren und dieser dann in einem Block etwas zuweisen
int *p = 7654; // Fehler
Das ist (theoretisch) ein Zeiger der auf die Adresse 7654 zeigt
Einfach einen Zeiger anlegen reserviert noch keinen Speicher für den Inhalt, sondern nur für den Zeiger.
int a; // Speicher für ein int namens a reservieren
int *ptr = &a; // Speicher für einen Zeiger reservieren und diesen auf a zeigen lassen
void setup() {
*ptr = 7654; // in a steht jetzt 7654
}
Die Zeigerinitialisierung ist kein Fehler!
Aber dennoch tut es nicht das was du beabsichtigst.
Ist somit logisch falsch.
Es wirft allerdings eine Warnung, welche man auch beachten muss.
Du initialisierst den Zeiger so dass er auf die Adresse 7654 zeigt.
Und das sagt dir auch die Warnung.
int *ptr = nullptr; // Initialisierung
*ptr = 7654; // Zuweisung
Hier die Alternativschreibweise für Initialisierungen:
int a {7654}; // Initialisierung
int *ptr {nullptr}; // Initialisierung
int *p {(int*)7654}; // Initialisierung
int *p {(int*)7654}; // richtet den Pointer auf die Adresse 7654
zu deinem (1)
Der deklarierte Zeiger zeigt auf eine Adresse die für einen int Wert reserviert ist.
Die Adresse auf die er zeigt kann mir egal sein, muss ich nicht unbedingt wissen.
Aus meiner Sicht ist die Adresse für den Zeiger reserviert.
Naja...
Du richtest den Zeiger auf einen Bereich, worüber du keine Ahnung hast was da ist.
Und das ist dir dann auch noch egal :o
Somit nagelst du dir eine Frikadelle ans Knie. 8)
das ist ganz schön viel auf einmal. Danke für die Aufmerksamkeit. Nochmal langsam zum mitmeißeln.
Irgendwas passt noch nicht zusammen. Jedenfalls bei mir.
Ihr sagt alle das hier ist eine Zuweisung einer Adresse! nicht eines Wertes?
int *p = 7654
Dann verstehe ich ehrlich gesagt nichts mehr.
Warum? Passt mal auf ... ich habe das Buch "Der C++ Programmierer" (aktuelle 5. Auflage) vor mir liegen.
Hier wird auf Seite 202 mit *ip = 100 dem Zeiger der Wert! 100 zugewiesen.
Es wird nicht gesagt das man dem Zeiger die Adresse 100 zuweist.
Auf Seite 203 wird ip = &i dem Zeiger ip die Adresse der Variablen i zugewiesen. Sodass der Zeiger den Wert von i hat. Genau das kann ich nachvollziehen und auch nicht erst seit heute.
Allerdings wird auf der gleichen Seite direkt darunter mit
i = 99
*ip =99
*ip2 = 99
gesagt das wäre alles das gleiche, also alle hätten den Wert 99.
Irgendwas kann jetzt nicht stimmen. Also entweder wird mit
*ip = 99 der Wert zugewiesen oder die wir ihr sagt die Adresse.
Irgendwas stimmt mit dem Dereferenzierer nicht?
Die Seiten lösche ich wieder wenn alles geklärt ist.
int *p; // Das ist eine Deklaration eines Zeigers auf ein int.
int *p = 7654; // Das ist auch eine Delaration eines Zeigers auf ein int und die Zuweisung von 7654 an diesen Zeiger.
Der * ist hier in beiden Fällen oben lediglich die Kennzeichnung als Zeiger.
int a;
p = &a; // Zuweisung der Adresse von a an p
*p = 1234; // hier kommt die Dereferenzierung
Weil es keine Deklaration ist, macht * hier die Dereferenzierung.
Leider wird für beides das gleiche Symbol benutzt, das kann zu Verwirrungen führen. Man muss also genau hinschauen, ob es eine Deklaration int *p oder eine Dereferenzierung *p ist.
Hier wird auf Seite 202 mit *ip = 100 dem Zeiger der Wert! 100 zugewiesen.
Es wird nicht gesagt das man dem Zeiger die Adresse 100 zuweist.
Das ist eine Zuweisung!
Hier wird der Wert 100 an die Speicherstelle geschrieben, auf welche der Zeiger zeigt.
Ihr sagt alle das hier ist eine Zuweisung einer Adresse! nicht eines Wertes?
int *p = 7654
Nein, sagen wir nicht.
Wir sagen, das ist eine Initialisierung des Zeigers.
Der Zeiger wird mit dem Wert 7654 initialisiert
Danach zeigt der Zeiger auf die Adresse 7654.
int *p; // Zeiger definieren
int *p = (int*)7654; // Zeiger definieren und initialisieren
int *p {(int*)7654}; // Zeiger definieren und initialisieren
p = (int*)4567; // ab jetzt zeigt der Zeiger auf eine neue Adresse
*p = 100; // der Wert 100 wird an die neue Adresse geschrieben
Definitionen mit Initialisierungen dürfen sowohl innerhalb, als auch außerhalb von Anweisungsblöcken auftauchen
Zuweisungen dürfen nur in Anweisungsblöcken auftauchen.
Tipp: Unterscheide streng zwischen Initialisierung und Zuweisung.
Auch wenn diese durch das = ähnlich aussehen, ist es dennoch etwas grundverschiedenes
Genau so hier:
int wert = 2 * 3; // hier ist der * der Multiplikationsoperator
int *p; // hier ist der * Teil des Datentype, es wird ein Zeiger definiert
*p = 100; // hier ist der * der Zeigerdereferenzierungsoperator
wir kommen dem schon näher. Die Formulierungen sind das Schwierigste.
Ist folgende Behauptung richtig?
"Bei einer Initialisierung eines Zeigers kann man diesem nur eine Adresse zuweisen, keinen Wert."
Jetzt habe ich weiter rumgespielt. Sind die Kommentare richtig?
Was nicht stimmt ist die Ausgabe der 345. Hier erhalte ich Sonderzeichen. ?
Sollte eigentlich funktionieren, da ich endlos den Inhalt der Adresse ändern kann solange alles int bleibt.
Und warum kann ich mir die Adresse des Zeigers nicht anzeigen lassen? Das muss doch auch gehen.
int var = 3456; // Initialisierung
int *p = (int*)7654; // Initialisierung (Adresszuweisung)
void setup (void)
{
Serial.begin(9600);
Serial.println(*p); // gibt mir den Wert von der Adresse 7654 worauf 'p' zeigt
int *ptr = nullptr; // sichere Initialisierung
Serial.println(*ptr); // gibt mir den Wert von der Adresse worauf 'ptr' zufällig zeigt, nicht die Adresse selbst
// Dereferenzierer, * auf der linken Seite vom =
*ptr = 123; // ändert den Wert auf den 'ptr' zeigt auf 123
Serial.println(*ptr);
*ptr = 345; // ändert den Wert auf den 'ptr' zeigt auf 345
Serial.println(*ptr);
ptr = &var; // ändert die Adresse worauf 'ptr' zeigt auf die Adresse von 'var'
Serial.println(*ptr); // und zeigt damit den Wert von 'var' an
Serial.println(&ptr); // falsch, soll Adresse anzeigen auf die 'ptr' zeigt
}
void loop (void)
{ }
Das hatten wir Dir schon am Anfang gesagt: Dein Zeiger zeigt ins Blaue.
Dort wo er hin zeigt, ist kein Speicher für einen int reserviert.
int *ptr = nullptr;
*ptr = 123; // Du schreibst an eine beliebige Stelle im Speicher 123. Das kann zufällig funktionieren, muss aber nicht.
// richtiger Weg:
int *ptr = nullptr;
int zahl; // Platz für einen int reservieren
ptr = &zahl; // ptr auf diesen int zeigen lassen
*ptr = 123; // jetzt kannst Du problemlos den Inhalt ändern
Für Zeiger gibt es keine Print-Routine. Da auf den kleinen Arduinos Zeiger aber in einem (unsigned ? sollte es sein, aber ist es das auch?) int gehalten werden, kannst Du sie Dir mit einem Typecast anzeigen lassen.
int *ptr = nullptr; // sichere Initialisierung
*ptr = 123; // ändert den Wert auf den 'ptr' zeigt auf 123 <<<---- ptr zeigt ins blaue
Ich mache:
int *ptr = nullptr;
int zahl; // Platz für einen int reservieren <<<------
ptr = &zahl; // ptr auf diesen int zeigen lassen <<<------
*ptr = 123; // jetzt kannst Du problemlos den Inhalt ändern
Die Wolken lichten sich. Ich formuliere es anders. Da ich den Zeiger mit nullptr initialisiere zeigt er ins Nichts.
An diese Adresse irgendeinen Wert schreiben geht schief. Korrekt?
Deswegen weist du erst die Adresse irgendeiner Variablen zu.
Ab dann kann man den Wert über den Zeiger der Variablen ändern. Korrekt?
Wenn ja, hab ichs langsam geschnallt.
Genau so, aber auch wenn Du den Zeiger nicht mit nullptr initialisiert hättest, wäre es so.
Die Initialisierung mit nullptr macht man, um im Ablauf prüfen zu können, ob der Zeiger auf etwas sinnvolles zeigt.
if (ptr != nullptr) {
// Du kannst mit dem Pointer arbeiten
}
jetzt ist mir vieles klarer, was ich bisher mit Zeigern wohl eher zufällig durch abgucken hinbekommen habe.
Alleine mit dem Buch habe ich das nie verstanden. Ich Danke euch allen für die Geduld mit mir. Der Tag war erfolgreich.
Zum Abschluss noch ein geänderter Sketch wo hoffentlich auch andere nachvollziehen können was mit dem Zeiger passiert.
/ https://forum.arduino.cc/index.php?topic=616496.0
int var = 3456; // Initialisierung
// Initialisierung mit Adresszuweisung, sollte man nicht machen,
// man weiß nicht was in der Adresse steht und den Inhalt zufällig und ungewollt ändern
int *n = (int*)7654;
int *p = nullptr; // sichere Initialisierung, zeigt ins Nichts
void setup (void)
{
Serial.begin(9600);
Serial.print(F("Inhalt var: "));
Serial.println(var);
int *ptr = nullptr; // sichere Initialisierung
Serial.print(F("zufaelliger Inhalt *ptr: "));
Serial.println(*ptr); // gibt mir den Wert von der Adresse worauf 'ptr' zufällig zeigt, nicht die Adresse selbst
Serial.println();
ptr = &var; // ändert die Adresse worauf 'ptr' zeigt auf die Adresse von 'var'
Serial.println(F("Adresszuweisung &var an ptr. "));
Serial.print(F("Inhalt *ptr: "));
Serial.println(*ptr); // und zeigt damit den Wert von 'var' an
// Dereferenzierer, * auf der linken Seite vom =
Serial.println(F("Wertzuweisung '123' an Zeigeradresse von *ptr"));
*ptr = 123; // ändert den Wert auf den 'ptr' zeigt auf 123
Serial.print(F("Inhalt *ptr: "));
Serial.println(*ptr);
Serial.print(F("Inhalt var: "));
Serial.println(var);
Serial.println(F("Wertzuweisung '345' an Zeigeradresse von *ptr"));
*ptr = 345; // ändert den Wert auf den 'ptr' zeigt auf 345
Serial.print(F("Inhalt *ptr: "));
Serial.println(*ptr);
Serial.print(F("Inhalt var: "));
Serial.println(var);
}
void loop (void)
{ }
Doc_Arduino:
Schade das man die Zeigeradresse nicht ausgeben kann, dann könnte man das im Terminal noch genauer nachvollziehen was mit den unterschiedlichen Zuweisungen passiert. Wäre zum debuggen hilfreich.
Das ist heute nicht Dein Tag?
Das hatte ich Dir in #12 geschrieben, wie das gehen sollte:
Da ich den Zeiger mit nullptr initialisiere zeigt er ins Nichts.
An diese Adresse irgendeinen Wert schreiben geht schief. Korrekt?
Nicht ganz!
nullptr entspricht NULL welches im Endeffekt der numerischen 0 entspricht.
Dein Pointer zeigt also auf die Adresse 0x0000
Wenn da Speicher ist, kannst du da auch rein schreiben.
Es ist also nicht korrekt, dass das automatisch schief geht!
Es geht nur schief, wenn es nicht das ist was du willst.
Manchmal kann es also sogar Sinn machen einen Zeiger mit 0 zu initialisieren und ihn dann auch so zu benutzen.
Selten, aber manchmal schon!
Zumindest hat der Compiler keine Einwände und folgt einem solchen Auftrag ohne Murren.