Wenn du mit Zeigern arbeitest musst du dir im Klaren darüber sein, dass es immer zwei Speicherbereiche zu unterscheiden gibt. Der eine ist der Speicherbereich in dem die eigentlichen Daten stehen, die andere Speicherzelle ist der Zeiger, dessen Wert die Adresse des anderen enthält.
Wenn du jetzt eine Variable von einem einfachen Basistypen wie int, char oder double etc. hast und über den Zeiger den Wert ändern willst, muss der Compiler unterscheiden können ob du den Wert des Zeigers oder den Wert auf den den er zeigt ändern möchtest.
Code:
int aval = 5;
int main(void) {
int *ptr;
ptr = &aval;
printf("Der Wert an der Adresse %p ist %d\n", ptr, *ptr);
*ptr = 7;
printf("Der Wert an der Adresse %p ist %d\n", ptr, *ptr);
return 0;
}
Für einfache Typen ist das schlicht. "ptr = &aval" weisst dem Zeiger die Adresse von aval zu, während " *ptr = 7 " den Zeiger dereferenziert und in den Speicher dieser Adresse den Wert 7 hineinschreibt. Zwei gänzlich unterschiedliche Dinge.
Bei Strukturen kommt jetzt hinzu, dass der Compiler Offsets für die Elemente der Struktur berechnen muss, damit er den korrekten Speicherbereich liesst bzw. beschreibt.
Bei einer Variablen weiss der Compiler zum Übersetzungszeitpunkt wo der Start der Variablen ist und kann diese Offsets absolut oder relativ berechnen.
Code:
#include <stdio.h>
struct vec2d {
double x, y;
};
struct vec2d ein_vec;
int main(void) {
ein_vec.x = 3.0;
ein_vec.y = 7.0;
printf("Adressen von ein_vec=%p, ein_vec.x=%p, ein_vec.y%p\n", &ein_vec, &ein_vec.x, &ein_vec.y);
return 0;
}
Verwende ich einen Zeiger auf eine Variable einer Struktur, dann kennt der Compiler diesen Startpunkt nicht und ich brauche einen Mechanismus ihm diesen mitteilen zu können. Der "->" sagt also aus: in der Zeigervariable findest du die Startadresse, nimm die und berechne von da an. Während bei Variablen der Compiler die Verantwortung für den Start hat, habe ich als Programmierer die Verantwortung bei Zeigern den richtigen Startpunkt dem Compiler mitzuteilen.
Code:
#include <stdio.h>
struct vec2d {
double x, y;
};
struct vec2d ein_vec;
int main(void) {
struct vec2d *vptr;
vptr = &ein_vec;
vptr->x = 3.0;
vptr->y = 7.0;
printf("Adressen von vptr=%p, ein_vec=%p\n", vptr, &ein_vec);
/* Fehler gibt SEGFAULT */
vptr = 0;
vptr->x = 3.0;
vptr->y = 7.0;
return 0;
}
An die Adresse 0x00000000 darf man nichts schreiben.
Falls dir die Erklärungen nicht so ganz einleuchten, solltest du dann vielleicht doch nochmal in ein gutes C-Buch reinschauen und dort die Erklärungen über Zeiger durchgehen. Mir fehlt im Erklären die Übung. Nichts desto trotzt sind Zeiger ein wichtiges und mächtiges Werkzeug in der C-Programmierung.
Warum ich die überhaupt benutzen wollte hatte einen bestimmten Grund:
Bei Threads muss man immer aufpassen, dass man sich keine Wettbewerbsbedingungen einfängt, sprich ein Thread in Daten eines anderen hineinschreibt. Obwohl ich erstmal globale Variablen als Speicherort gewählt hatte, werden die einzelnen PIDs dadurch isoliert, dass in den Threads immer nur auf eine Variable über den zugehörigen Zeiger zugegriffen wird (das wäre selbst so, wenn ich alle in einem Feld anordnen würde.). Diese Zeiger liegen auf dem Thread lokalem Stack, sind nach Aussen nicht sichtbar.
Das war die Idee.
Gruss
botty
Lesezeichen