Void-Zeiger
-
Hallo, ich habe mal wieder eine Frage. Ich habe mich jetzt mit Zeigern bekannt gemacht, es auch ohne größere Probleme verstanden. Nur eine einzige Sache ist mir bei den Void-Zeigern nicht 100% klar:
#include <stdio.h> #include <stdlib.h> int main(void) { void *void_ptr; int wert = 10; void_ptr = (int *)&wert; *(int *)void_ptr = 100; printf("%d\n", wert); getchar(); return EXIT_SUCCESS; }
Am Ende des Programms wird ein weiterer Zeiger benötigt um den gecasteten Void-Zeiger zu dereferenzieren. Ist dieser deshalb notwendig weil ich beim vorherigen casten des Void-Zeigers
void_ptr = (int *)&wert;
den Void-Zeiger in Wirklichkeit auf einen Int-Zeiger habe zeigen lassen der dann
auf die Adresse von wert zeigt ?Schonmal danke für die hoffentlich erleuchtende Antwort
-
... void_ptr = (int *)&wert; // hier muss kein cast hin, es geht auch: void_ptr = &wert; *(int *)void_ptr = 100; // hier muss ein cast hin, weil ein void* keinen typ hat und der compiler deshalb nicht weiss, wie er die 100 speichern soll. ...
-
void_ptr = (int *)&wert;
dort kannst du das (int
weglassen, bzw solltest es sogar, weil der Compiler daraus
void_ptr = (void *)(int *)&wert;
machen müsste, anstatt direkt
void_ptr = (void *)&wert;
In der Zeile wird einfach der Pointer void_ptr auf die Adresse von der Variable "wert" gesetzt.
In der Zeile *(int *)void_ptr = 100; passiert folgendes:
Da du einen void Pointer nicht dereferenzieren kannst, also auf seinen Wert zugreifen kannst, musst du casten. Daher castet du einmal nach (int
und dereferenzierst dann mit dem *. Auf diese Weise kannst du auf den Wert an der Speicherstelle zugreifen und diesen auf 100 setzen. *void_ptr würde nicht funktionieren.
Ich hoffe, dass dir das etwas hilfreich war.
-
Ok Leute. Das ist alles das ich wissen musste. Danke euch vielmals.
ten schrieb:
... void_ptr = (int *)&wert; // hier muss kein cast hin, es geht auch: void_ptr = &wert; ...
Das Stand so auch in dem Buch (das ein Cast in C nicht notwendig ist). Allerdings wurde auch gesagt das man an dieser Stelle ruhig casten dürfe. In C++ scheint ein Cast an dieser Stelle auch erforderlich zu sein. Da ich möglicherweise später C++ lernen möchte kann es deshalb nicht schaden sich das casten gleich anzugewöhnen.
-
Brezel schrieb:
In C++ scheint ein Cast an dieser Stelle auch erforderlich zu sein. Da ich möglicherweise später C++ lernen möchte kann es deshalb nicht schaden sich das casten gleich anzugewöhnen.
in c++ macht man dafür 'static_cast<void*>(&wert)'
c++ ist ziemlich broken...
-
Brezel schrieb:
Das Stand so auch in dem Buch (das ein Cast in C nicht notwendig ist). Allerdings wurde auch gesagt das man an dieser Stelle ruhig casten dürfe. In C++ scheint ein Cast an dieser Stelle auch erforderlich zu sein.
Nein. Auch in C++ wird implizit nach void* umgewandelt. Umgekehrt gilt das allerdings nicht, dh eine implizite Umwandlung von void* in einen Zeiger eines anderen Typs wird nicht durchgeführt. ZB
int wert = 10; void* void_ptr = &wert; int* int_ptr = void_ptr;
Dies ist in C problemlos möglich, in C++ jedoch nicht. Dort bedarf es eines Casts in der letzten Zeile.
int* int_ptr = static_cast<int*>(void_ptr);
Das hat aber nichts damit zu tun, dass C++ "broken" ist. Das ist vollkommener Käse. Sondern das Typsystem von C++ ist einfach strikter, um mögliche Fehlerquellen zu reduzieren.
-
groovemaster schrieb:
...Sondern das Typsystem von C++ ist einfach strikter, um mögliche Fehlerquellen zu reduzieren.
wo siehst du in
int x = 10; void *p = &x;
eine fehlerquelle? ein
*p = ...;
oder sowas geht ja nicht.
-
Wie er sagte, die Umwandlung von T* nach void* ist in C++ genauso erlaubt wie in C. Aber die umgekehrte Umwandlung (void* nach T*) ist in C++ nur mit einem expliziten Cast erlaubt (in C ist
void*pv=...;int*pi=pv;
zulässig, in C++ nicht).PS: Der Cast in
void_ptr = (int*)&wert;
ist übrigens nicht nur unnötig, sondern sogar sinnlos - wenn du schon der Meinung bist, casten zu müssen, dann bitte in den gewünschten Zieltyp (&wert ist bereits ein int*).
-
CStoll schrieb:
Aber die umgekehrte Umwandlung (void* nach T*) ist in C++ nur mit einem expliziten Cast erlaubt (in C ist
void*pv=...;int*pi=pv;
zulässig, in C++ nicht).also hat c++ keinen generischen pointer (void*), bzw. der funktioniert ja nur in eine richtung
so ein mist. würde mir echt fehlen...
-
ten schrieb:
wo siehst du in
int x = 10; void *p = &x;
eine fehlerquelle? ein
*p = ...;
oder sowas geht ja nicht.
Denk mal ein bisschen weiter. Du kannst zB folgendes machen:
void foo(long* x) { *x = 100; //... } foo(p); // undefiniertes Verhalten, falls sizeof(int) < sizeof(long)
In C++ kannst du dir mit sowas zwar immer noch in den Fuss schiessen, du musst dafür aber auch mehr machen. Es ist nicht einfach mal so "aus Versehen" möglich. Sprich, du musst explizit Hand anlegen und casten.
-
ten schrieb:
CStoll schrieb:
Aber die umgekehrte Umwandlung (void* nach T*) ist in C++ nur mit einem expliziten Cast erlaubt (in C ist
void*pv=...;int*pi=pv;
zulässig, in C++ nicht).also hat c++ keinen generischen pointer (void*), bzw. der funktioniert ja nur in eine richtung
so ein mist. würde mir echt fehlen...
Doch, es gibt einen void* - mit allen Vor- und Nachteilen. Allerdings kannst du den nicht mehr implizit in einen "normalen" Zeiger umwandeln lassen (die explizite Umwandlung (aka Cast) ist trotzdem möglich, nur ist die etwas auffälliger im Quelltext).
-
ten schrieb:
also hat c++ keinen generischen pointer (void*), bzw. der funktioniert ja nur in eine richtung
so ein mist. würde mir echt fehlen...
Wenn es um generische Programmierung geht, verwendet man in C++ Templates. Nur heisst der Zeiger dann T* oder was auch immer. void* fehlt keinem C++ Programmierer. Ansonsten kann man mit void* in C++ alles das machen, was man auch in C machen kann. Lediglich die Typisierung ist nicht so einfach auszuhebeln.
-
groovemaster schrieb:
Lediglich die Typisierung ist nicht so einfach auszuhebeln.
na ich weiss nicht...
wenn schon typesicherheit, dann wenigstens konsequent. void* funzen nur in die eine richtung - unschön - doch irgendwie verständlich. aber 'nen leichten schock hat mir c++ mal damit verpasst:int *a = (int*)1; void *b = (void*)2; if (a == b) { ... }
das compiliert!
noch nicht mal ein warning erscheint (beim msvc++)!
so ein code ist ganz offensichtlich ein programmierfehler, c++ juckt's aber nicht.
von wegen 'typsicherheit' - c++ ist einfach nur kaputt...
-
Das einzige Problem von C++ ist sein Anspruch, halbwegs kompatibel zu C zu sein. Und da erklärt auch, daß solche Konstruktionen akzeptiert werden. Allerdings ist es auch (afaik) Absicht, daß du mit expliziten Casts das Typsystem durcheinanderbringen kannst - wer einen Wert in einen bestimmten Typ castet, weiß schon, was er da tut (und wenn nicht, ist er selber dran schuld).
(btw, in deinem Code liefert das
if(a==b)
das Ergebnis eines ganz normalen Zeiger-Vergleichs - und da int* in void* umgewandelt werden darf, ist der Vergleich absolut legal)
-
ten schrieb:
na ich weiss nicht...
wenn schon typesicherheit, dann wenigstens konsequent. void* funzen nur in die eine richtung - unschön - doch irgendwie verständlich. aber 'nen leichten schock hat mir c++ mal damit verpasst:int *a = (int*)1; void *b = (void*)2; if (a == b) { ... }
das compiliert!
noch nicht mal ein warning erscheint (beim msvc++)!Und warum sollte das nicht kompilieren? Wie schon erwähnt, der Compiler kann einen Zeiger implizit nach void* umwandeln.
ten schrieb:
so ein code ist ganz offensichtlich ein programmierfehler
Tatsächlich? Woran siehst du das?
ten schrieb:
c++ juckt's aber nicht.
In C ist das übrigens genauso legal.
Niemand sagt, dass C++ absolute Typsicherheit hat. Wenn eine Sprache soweit low-level geht, wo zB Zeiger verwendet werden können, ist das vermutlich auch kaum machbar. Nur verhält sich C++ hier etwas strikter gegenüber C. Von kaputt kann keine Rede sein. Ende der Geschichte.
-
CStoll schrieb:
Wie er sagte, die Umwandlung von T* nach void* ist in C++ genauso erlaubt wie in C. Aber die umgekehrte Umwandlung (void* nach T*) ist in C++ nur mit einem expliziten Cast erlaubt (in C ist
void*pv=...;int*pi=pv;
zulässig, in C++ nicht).PS: Der Cast in
void_ptr = (int*)&wert;
ist übrigens nicht nur unnötig, sondern sogar sinnlos - wenn du schon der Meinung bist, casten zu müssen, dann bitte in den gewünschten Zieltyp (&wert ist bereits ein int*).Ja, mir kam das ganze auch irgendwie verwirrend vor, darum die Fragen.
Auf jeden Fall nochmal Dank an euch alle, habt mir sehr geholfen.
-
CStoll schrieb:
Das einzige Problem von C++ ist sein Anspruch, halbwegs kompatibel zu C zu sein. Und da erklärt auch, daß solche Konstruktionen akzeptiert werden. Allerdings ist es auch (afaik) Absicht, daß du mit expliziten Casts das Typsystem durcheinanderbringen kannst...
mir geht's um den vergleich und nicht um die casts.
aber du sprichst es ja an: dieses 'halbwegs kompatibel' sein ist das problem.
entweder: "wir erweitern C um objektorientierte features, bleiben aber zu C 100%ig kompatibel"
oder: "wir machen eine komplett neue, objektorientierte programmiersprache, die auf C syntax basiert, verzichten aber sonst auf jegliche kompatiblität zu C"
beides wäre akzeptabel.
nicht aber: "wir versuchen 90% kompatibilität hinzubekommen, rsikieren dabei eine inkonsistente sprachdefinition und viel mehr undefiniertes verhalten als C sowieso schon hat"
'struppi & friends' haben einfach nicht zuende gedachtgroovemaster schrieb:
Ende der Geschichte.
hast ja recht. genug geflamed, sonst macht TactX noch zu...
-
feigling schrieb:
void_ptr = (int *)&wert;
dort kannst du das (int
weglassen, bzw solltest es sogar, weil der Compiler daraus
void_ptr = (void *)(int *)&wert;
machen müsste, anstatt direkt
void_ptr = (void *)&wert;
In der Zeile wird einfach der Pointer void_ptr auf die Adresse von der Variable "wert" gesetzt.
In der Zeile *(int *)void_ptr = 100; passiert folgendes:
Da du einen void Pointer nicht dereferenzieren kannst, also auf seinen Wert zugreifen kannst, musst du casten. Daher castet du einmal nach (int
und dereferenzierst dann mit dem *. Auf diese Weise kannst du auf den Wert an der Speicherstelle zugreifen und diesen auf 100 setzen. *void_ptr würde nicht funktionieren.
Ich hoffe, dass dir das etwas hilfreich war.
Auch nochmal danke für Deinen Beitrag. Hat viel gebracht. Habe noch mal eine Frage: Wo kann man im einzelnen nachschauen wie der Compiler bestimmte Anweisungen umsetzt ? (z.b. wie Du oben erwähntest void_ptr = &wert ===> void_ptr = (void *)&wert ). Das wird ja standardisiert sein. Und es interessiert mich.
-
ten schrieb:
hast ja recht. genug geflamed, sonst macht TactX noch zu...
-
ten schrieb:
entweder: "wir erweitern C um objektorientierte features, bleiben aber zu C 100%ig kompatibel"
oder: "wir machen eine komplett neue, objektorientierte programmiersprache, die auf C syntax basiert, verzichten aber sonst auf jegliche kompatiblität zu C"
beides wäre akzeptabel.
nicht aber: "wir versuchen 90% kompatibilität hinzubekommen, rsikieren dabei eine inkonsistente sprachdefinition und viel mehr undefiniertes verhalten als C sowieso schon hat"
'struppi & friends' haben einfach nicht zuende gedachtNein, dem ist nicht so. C++ erhebt überhaupt keinen Anspruch, zu C kompatibel sein zu wollen. Aufgrund der gemeinsamen Basis gibt es aber nunmal Gemeinsamkeiten. Früher oder später werden diese wegfallen, sofern man es für sinnvoll hält.
Und das Thema mit void* hat auch nichts mit Kompatibilität zu tun. Es ist einfach so, dass eine Umwandlung nach void* kein Sicherheitsrisiko darstellt. Es gibt keine void Objekte, und damit ist ein Dereferenzieren auch nicht möglich. Deshalb ist dies implizit erlaubt. Eine Umwandlung von void* hingegen stellt ein Sicherheitsrisiko dar, und somit wird einem ein Cast abverlangt. Diese Lösung ist jedenfalls sinnvoll, und keinesfalls weniger plausibel als etwas anderes.