Moin zusammen,
Zeiger. Vor allem in C sind sie ja ein oft genutztes Mittel und stehen
auch charakterisierend für die Vorteile von C. Obwohl ich mich jetzt
länger damit beschäftige, gibt es immer noch einige Unklarheiten.
Wenn mich z.B. jemand fragt, warum Zeiger denn nun so mächtig sind, kann
ich weder eine flüssige, noch eine klare Antwort darauf abgeben.
Also, Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher
und man kann mit ihnen viel Chaos anstellen - jemand verglich dies mal
mit dem "Zauberstab" aus Harry Potter. Man kann gutes damit bewirken,
aber auch viel Unheil anrichten.
Wenn man z.B. mittels eines Pointers auf die Adresse einer deklarierten
Variablen zeigt und diese ändert, bspw. durch ein Inkrement, dann ändert
sich ja indirekt die Adresse dieser Variablen.
Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise
unangetastet lassen sollte.
Stimmt das soweit? Kann man das ergänzen? Das ist doch nicht alles,
oder? Weshalb sind Zeiger so ein mächtiger Werkzeug (in C)?
Danke Leute :)
Nikola Tesla schrieb:> stehen> auch charakterisierend für die Vorteile von C
Wirklich? Ich denke eher nicht. ;->
Nikola Tesla schrieb:> Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise> unangetastet lassen sollte.
Das ist das Hauptmerkmal von C pointern (und besonders mit cast
operatoren). ;->
Nikola Tesla schrieb:> Weshalb sind Zeiger so ein mächtiger Werkzeug (in C)?
Zeiger sind gerne datentechnisch kleiner (z.B. 4 oder 8 Byte) als die
zugehörigen Datenobjekte auf die der Zeiger zeigt (zum Beispiel ein
Array oder eine verkettete Liste von ... was auch immer)
Daher ist es günstiger (Rechenleistung, Geschwindigkeit) Zeiger zu
"bewegen" anstelle echte Datenobjekte. Z.B. 2 Objekte vertauschen:
a = wert x
b = Wert y
Vertausche (a,b) =
hv := a,
a := b,
b := hv
wenn a, b und hv jeweils ein Zeiger wären, müssten nur 3 x 4 = 12 Bytes
bewegt werden.
wenn a, b und hv jeweils das echte Datenobjekt (z.B. ein String von 400
Zeichen) wären, müssten jeweils 3 x 400 = 1200 Bytes bewegt werden.
(Mithin also die 100-fache Datenmenge welche bewegt wird)
und bei 1 Million derartige Operationen, welche angenommen 1 µ-Sekunde
pro byte braucht sind das dann .... (rechne es selber aus)
Die notwendigen Speicherschutz-Mechanismen (damit da nicht auf
unerlaubte Speicherbereiche zugegriffen werden kann ist halt was
anderes, und muss halt als "Mehraufwand" berücksichtigt werden.
Wegstaben V. schrieb:> Zeiger sind gerne datentechnisch kleiner (z.B. 4 oder 8 Byte) als die> zugehörigen Datenobjekte auf die der Zeiger zeigt
Das ist aber nicht C spezifisch und gilt generell für pointer.
Wegstaben V. schrieb:> Die notwendigen Speicherschutz-Mechanismen (damit da nicht auf> unerlaubte Speicherbereiche zugegriffen werden kann ist halt was> anderes
un in C nicht bekannt. ;-<
Nikola Tesla schrieb:> Zeiger. Vor allem in C sind sie ja ein oft genutztes Mittel und stehen> auch charakterisierend für die Vorteile von C.
Diese Aussage hat keinen objektiv überprüfbaren Wahrheitsgehalt. Es ist
eine Meinung, keine Tatsache. Wer sollte das auch entscheiden? Natürlich
ist das eine perfekte Ausgangsposition für sinnlose Diskussionen ...
Nikola Tesla schrieb:> Also, Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher> und man kann mit ihnen viel Chaos anstellen - jemand verglich dies mal> mit dem "Zauberstab" aus Harry Potter. Man kann gutes damit bewirken,> aber auch viel Unheil anrichten.
Das gleiche könnte man über Schraubenzieher, Bügeleisen oder partielle
Differentialgleichungen sagen. Weißt du, warum das so ist? Weil man mit
Potters Zauberstab (das muss Code für irgendwas sein ...) so ziemlich
alles machen kann, daher kann man alles, mit dem man "etwas" machen
kann, mit dem Zauberstab vergleichen.
Der Vergleich ist sinnlos, weil völlig nichtssagend.
Nikola Tesla schrieb:> Weshalb sind Zeiger so ein mächtiger Werkzeug (in C)?
Sie entsprechen der Art, wie die Hardware funktioniert. Das erhöht die
Chance, dass das Programm einigermaßen performant sein wird.
Der ganze Rest ist subjektiv: Vielleicht mag man Zeiger als Konstrukt
einer Hochsprache, vielleicht mag man sie nicht. Schon die Frage, ob
Zeiger "mächtig" sind, kann man kontrovers diskutieren: Offensichtlich
geht es ja auch ohne. Wie mächtig kann das Konzept dann schon sein?
Nikola Tesla schrieb:> Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise> unangetastet lassen sollte.
..., was man sinngemäß auch über Schraubenzieher, Bügeleisen und
partielle Differentialgleichungen sagen könnte. "Man kann etwas damit
machen, was man normalerweise nicht machen sollte" trifft mal wieder auf
praktisch alles zu.
Zeiger sind genau dann gefährlich, wenn es auch so ziemlich alle anderen
Sprachkonstrukte sind: Wenn sie ungeprüft direkt auf die Hardware
durchgreifen. Irgendjemand muss also aufpassen, heute ist das regelmäßig
das Betriebssystem.
Nikola Tesla schrieb:> Wenn mich z.B. jemand fragt, warum Zeiger denn nun so mächtig sind, kann> ich weder eine flüssige, noch eine klare Antwort darauf abgeben.
Weniger anschauliche Vergleiche suchen, dafür mehr harte Fakten, klar
getrennt von Bewertungen und Meinungen. Langweilig, aber wirksam.
Nikola Tesla schrieb:> Weshalb sind Zeiger so ein mächtiger Werkzeug (in C)?
Da kann man so viele verschiedene Antworten drauf geben. Ich wähle mal
"Weil man ohne Zeiger keine Funktionen aufrufen und keine Arrays
benutzen kann."
Nikola Tesla schrieb:> Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise> unangetastet lassen sollte.
Dann ist es wahrscheinlich kein C mehr.
Nikola Tesla schrieb:> Also, Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher
Sie zeigen auf eine bestimmte Speicherstelle. Dazu enthalten sie die
Adresse dieser Speicherstelle. Ob die im dynamischen Speicher liegt oder
nicht, ist davon unabhängig. Dynamischer Speicher ist also keine
Voraussetzung für die Verwendung von Zeigern, aber umgekehert sind
Zeiger eine Voraussetzung für dynamischen Speicher. Die Adresse eines
dynamisch allokierten Objekts ergibt sich ja erst zur Laufzeit, daher
braucht man etwas, wo man diese Adresse speichern und über das man das
Objekt dann auch nutzen kann.
> Wenn man z.B. mittels eines Pointers auf die Adresse einer deklarierten> Variablen zeigt und diese ändert, bspw. durch ein Inkrement, dann ändert> sich ja indirekt die Adresse dieser Variablen.
Nein, die Adresse einer Variablen ist fix und kann nicht geändert
werden. Der Zeiger zeigt dann einfach nicht mehr auf die Variable.
> Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise> unangetastet lassen sollte.
Ob man das kann, sagt C nicht. Es sagt, wenn man es versucht, ist das
Verhalten des Programms undefiniert.
Es gab in vielen Hochsprachen (vor und neben C) das Konzept von
Pointern. Sobald dynamischer Speicher ins Spiel kommt, braucht man
einen.
Das war auch schon in den 60ern bei Algol und Simula der Fall. Auch
Pascal und dessen Nachfolger haben Pointer.
Fehlerhafte Zuweisungen, Zugriffe auf gelöschten Speicher, auch das habe
ich mit den anderen Sprachen geschafft.
Nikola Tesla schrieb:> Weshalb sind Zeiger so ein mächtiger Werkzeug (in C)?
Weil C die Zeiger offen trägt und nicht hinter irgendwelchen anderen
Konstrukten versteckt.
Weil C keine anderen Werkzeuge hat.
Nikola Tesla schrieb:> Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher.
Nein. Zwar möglich aber weder zwingend noch überwiegend.
Nikola Tesla schrieb:> Wenn man z.B. mittels eines Pointers auf die Adresse einer deklarierten> Variablen zeigt und diese ändert, bspw. durch ein Inkrement, dann ändert> sich ja indirekt die Adresse dieser Variablen.
Nein. Das ergibt so wenig Sinn, dass man diese Aussage nicht Mal
korrigieren könnte.
> Damit kann man dann z.B. auf Speicher zugreifen, den man normalerweise> unangetastet lassen sollte.
Du solltest lieber die Aufgabe Posten oder was Du nicht verstanden hast.
Oder was Du schon an C kennst.
Nikola Tesla schrieb:> Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher
Nö, die können auf jede beliebige Adresse zeigen. Dass kann auch
Hardware oder anders sein.
Mach dir einfach mal klar wie eine CPU im allgemeinen so funktioniert
und wie die mit den Adressen umgeht dann wird dir auch klarer was die
bewirken.
Und wenn du mal versuchst in Asembler zu programieren hantierst du noch
viel mehr mit Adressen.
Und damit ist auch der Fluch und Segen zugleich von C erklärt, C ist
halt noch sehr Assember nah und somit CPU nah und erlaubt den direkten
zugriff auf alle Adressen ohne Netz und doppelten Boden (so feinheiten
wie Kernel-Mode /User-Mode, MMU usw. moderner CPU-Architekturen mal
außen vor gelassen).
Sprachen wie z.B. Pascal oder JAva bauen da einfach eine paar Schichten
Watte drum herum damit der Programierer eben nicht mehr direkt auf die
Adressen zugreifen kann/muss/darf und wandeln das halt in den
Zwichenschichten entsprechend um. Aber auch da steht am ende eine
Addresse mit der die CPU arbeiten kann. Nur der Programierer sieht diese
nicht mehr so direkt und kann somit einige Fehler weniger machen - aber
das kostet auch alles Rechenzeit.
Nikola Tesla schrieb:> Wenn man z.B. mittels eines Pointers auf die Adresse einer deklarierten> Variablen zeigt und diese ändert, bspw. durch ein Inkrement, dann ändert> sich ja indirekt die Adresse dieser Variablen.
Nein.
Wenn man einen Zeiger ändert, dann ändert man nicht die Adresse einer
Variablen. Aber der Zeiger zeigt danach nicht mehr auf die Variable,
sondern daneben.
Nikola Tesla schrieb:> Also, Pointer zeigen auf eine bestimmte Adresse im dynamischen Speicher
Im µC Bereich kannst dein Pointer auch auf Flash Adressen zeigen lassen
oder einfach irgendwo auf eine RAM Adresse...dieser Speicher muss ja
nicht "dynamisch" mit malloc allokiert sein (falls du das mit dynamisch
meinst).
Nikola Tesla schrieb:> Wenn man z.B. mittels eines Pointers auf die Adresse einer deklarierten> Variablen zeigt und diese ändert, bspw. durch ein Inkrement, dann ändert> sich ja indirekt die Adresse dieser Variablen.
Das wäre ja schlimm, wenn das so wäre!!!
Was du meinst ist, du kannst den Pointer inkrementieren, dann zeigt er
auf die nächste Adresse.
Schau mal:
Ausgabe oben im Bild.
1
intmain(void)
2
{
3
uint8_tfoo[3]={1,2,3};
4
uint8_t*p;
5
6
printf("foo[0] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[0],foo[0]);
7
printf("foo[1] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[1],foo[1]);
8
printf("foo[2] liegt auf Adr.: 0x%08X und hat den Wert: %d\n\n",&foo[2],foo[2]);
9
10
11
/* Pointer auf foo[0] zeigen lassen und Wert um 1 erhöhen */
12
p=&foo[0];
13
(*p)++;// Oder: *p = *p + 1;
14
15
printf("foo[0] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[0],foo[0]);
16
printf("foo[1] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[1],foo[1]);
17
printf("foo[2] liegt auf Adr.: 0x%08X und hat den Wert: %d\n\n",&foo[2],foo[2]);
18
19
20
/* Zeiger eine Adresse weiter und Wert auf 0 setzen */
21
p++;
22
*p=0;
23
24
printf("foo[0] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[0],foo[0]);
25
printf("foo[1] liegt auf Adr.: 0x%08X und hat den Wert: %d\n",&foo[1],foo[1]);
26
printf("foo[2] liegt auf Adr.: 0x%08X und hat den Wert: %d\n\n",&foo[2],foo[2]);
27
28
29
return0;
30
}
An deiner Stelle würde ich mir solche kleinen Programme erstellen und
einfach mal damit "rumspielen" dann erkennst/lernst du am besten, da du
siehst was passiert.
Adam P. schrieb:> printf("foo[0] liegt auf Adr.: 0x%08X und hat den Wert: %d\n", &foo[0],> foo[0]);
Der Formatspecifier bei printf für einen Pointer ist %p
Ein Zeiger muss nicht mal auf einen gültigen Speicher zeigen. Wenn der
Adressbereich sehr groß ist, dann liegt nicht überall Speicher oder
Hardware.
Immerhin sind Zeiger in C typisiert, d.h. wenn ein Zeiger auf ein int
deklariert wurde, dann meckert der Compiler wenn ich diesem die Adresse
auf ein float zuweisen möchte. Das kann man erwingen, das gehört aber zu
den 'gefährlichen' Operationen in C.
Und Zeiger können auch auf Funktionen, also ausführbahren Code zeigen.
Auch gefährlich, aber sehr praktisch.
Dirk B. schrieb:> Adam P. schrieb:>> printf("foo[0] liegt auf Adr.: 0x%08X und hat den Wert: %d\n", &foo[0],>> foo[0]);>> Der Formatspecifier bei printf für einen Pointer ist %p
Ja ist mir beim schreiben auch aufgefallen.
Aber wann wurde der eingeführt?
Zeiger sind in C das was in Assembler bzw. dem Prozessor Adressregister
und indirekte Adressierung sind.
In anderen Programmiersprachen versteckt man das komplett hinter
Variablendeklarationen, Array-Indizierung und vollautomatischer
Speicherverwaltung, so dass man keinen Unfug damit machen kann.
Der Nachteil anderer höherer Sprachen ist natürlich, dass man überhaupt
keinen Unfug (= Tricks, wenn man genau weiß was man tut) damit machen
[i]kann[/i].
Deshalb ist C im Embedded-Bereich und bei Betriebssytemkernels so
beliebt. Weil man da aus Performancegründen oft mit verschiedenen Tricks
arbeiten muß, die C eben nicht verbietet.
mh schrieb:> Adam P. schrieb:>> Ja ist mir beim schreiben auch aufgefallen.>> Aber wann wurde der eingeführt?>> Steht schon im Anso-C
Ja habs grad in C89 gefunden:
"The %p conversion specifier was added to C89 for pointer conversion
since the size of a pointer is not necessarily the same as the size of
any integer type. Because an implementation may support more than one
size of pointer, the corresponding argument is expected to be a pointer
to void."
Mh, ist mir noch nie aufgefallen :)
Adam P. schrieb:> Mh, ist mir noch nie aufgefallen :)
size_t kennst du aber.
Auch dass es dafür einen Modifier für printf gibt?
Und den hh Modifier bei scanf?
Und dass %lf bei printf nicht vorgesehen ist (war).
Dirk B. schrieb:> size_t kennst du aber.
Ja klar.
Dirk B. schrieb:> Auch dass es dafür einen Modifier für printf gibt?> Und den hh Modifier bei scanf?> Und dass %lf bei printf nicht vorgesehen ist (war).
Nutze printf und scanf eigentlich nie.