Void-Zeiger



  • 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 gedacht 😉

    groovemaster 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 gedacht 😉

    Nein, 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.



  • groovemaster schrieb:

    Nein, dem ist nicht so. C++ erhebt überhaupt keinen Anspruch, zu C kompatibel sein zu wollen.

    nein, überhaupt nicht 😃
    dann guck doch mal in's ISO/IEC 14882, insbesondere anhang C.
    🙂



  • Und was genau soll ich mir da angucken?



  • groovemaster schrieb:

    Und was genau soll ich mir da angucken?

    hast du es als pdf?
    such nach 'compatibility', dann wirste fündig.



  • Schon klar. Nur was genau soll ich mir dort anschauen? Aus Anhang C sollte doch mehr als deutlich hervorgehen, dass es Unterschiede gibt. Und dass C++ nicht um jeden Preis Gemeinsamkeiten nur wegen der Kompatibilität beibehält. Natürlich wirft man nicht einfach alles grundlos über den Haufen. Aber wenn Änderungen für sinnvoll erachtet werden, dann werden diese auch gemacht. C spielt dabei keine Rolle. Auch im kommenden Standard wird die eine oder andere Inkompatibilität vermutlich hinzukommen, zB das Schlüsselwort auto.



  • groovemaster schrieb:

    Natürlich wirft man nicht einfach alles grundlos über den Haufen.

    richtig, das tun sie nicht, aber *gerade das* beschert uns hier threads wie 'ich muss den rückgabewert von malloc doch casten, weil ich sonst 'nen compile error bekomme'. wieso gibt's überhaupt malloc in c++? geh' ins c++ forum und poste einen quelltext mit 'malloc' drin - das erste was du zu lesen bekommst ist 'meckermecker, malloc benutzt man aber nicht in c++, dafür gibt's new blubberblubb...' 😃

    die ganze ISO/IEC 14882 spec. ist gespickt mit hinweisen wie 'SEE ALSO: ISO C subclause blahblah...'. das zeigt ja nun mehr als deutlich wieviel C noch in c++ steckt. ...aber wie viele C-programme kann man wohl durch C++-compiler schicken ohne mit errors und warnings überhäuft zu werden? fast keines!
    ich bin wahrlich kein linux-fan, aber in einer sache stimme ich mit linus t. völlig überein: 'c++ just sucks. sorry, but it does!'

    btw: die sprache 'java' (ich meine nur die programmiersprache, nicht die plattform) hätte es niemals gegeben, wenn die erfinder von c++ nicht so einen obermist gebaut hätten...

    @TactX: sorry, ich hab' mal wieder getrollflamed. kann verstehen wenn du das jetzt löschst oder zensierst 😉


Anmelden zum Antworten