int* nach void*



  • Guten Tag liebe Forummitglieder 🙂 ,

    ich habe versucht, an einem sehr simplen Programm zu beweisen, dass ein Typ-Cast von int* nach void* nicht notwendig ist. Leider ist mir das nicht gelungen, weiss vielleicht jemand wo hier der Fehler ist. Danke Euch..

    Mit freundlichen Grüßen,

    Ratna aus Rheinland :xmas1:

    #include <iostream>
    
    void func(void* i) 
    { 
      *i=6;
    } 
    
    int main() 
    { 
    	int b=5;
    	int* ptr = &b;
    	func(ptr);
      std::cout << b << std::endl; 
    }
    

  • Administrator

    1. Nicht gelungen == sehr sehr schlecht Fehlerbeschreibung.
    2.

    void func(int* i)  // <-- Fehler, int* statt void*!
    { 
      *i=6;
    }
    

    3. Was soll damit bewiesen werden?
    4. Zu viel Alkohol gehabt? Hab zwar auch gerade eine ganze Flasche Wein hinter mir, aber da war wohl noch ein wenig mehr dabei *g*

    Grüssli



  • void func(void* i)
    

    Das ist so gewollt. Der Parameter soll also einen Zeiger eines Datentyps sein, der erst zur Ausführungslaufzeit gegeben ist. In diesem Fall integer, da:

    int b=5;
    int* ptr = &b;
    func(ptr); //ptr ist ein int-Zeiger
    


  • Ganz einfach, das "*i" ist illegal. Hier musst du casten.



  • Habe den Fehler gefunden:

    #include <iostream>
    
    void func(void* i) 
    { 
      *(int*)i=6;
    } 
    
    int main() 
    { 
    	int b=5;
    	int* ptr = &b;
    	func(ptr);
      std::cout << b << std::endl; 
    }
    

    Danke trotzdem...

    Viele Grüße aus dem Rheinland,

    Ratna 🙂



  • In C++ würde ich statt den C-Klammercasts den Operator reinterpret_cast verwenden, der ist sicherer und man sieht besser, was getan wird.



  • Nexus schrieb:

    In C++ würde ich statt den C-Klammercasts den Operator reinterpret_cast verwenden, der ist sicherer und man sieht besser, was getan wird.

    Hier reinterpret_cast zu empfehlen ist IMO grob falsch.
    Angebracht wäre ein static_cast.


  • Administrator

    Wäre nicht sogar angebracht zu sagen, dass man in C++ gleich komplett auf void* verzichten sollte und dafür eher zum Beispiel Templates nehmen sollte?
    Es ging zwar darum, dass er beweisen wollte, dass ein Typ-Cast nicht notwendig ist, aber wofür wollte er es beweisen? Es ist wohl ziemlich wahrscheinlich, dass er irgendwo ein void* einsetzen möchte.

    Grüssli



  • Jo ich wollte nur eine IMO grob falsche Empfehlung hier korrigieren.
    Dass void* für die grossen Jungs ist, weil die kleinen Jungs immer soviel Unfug damit anstellen, ist wieder eine andere Sache 😉



  • Hallo zusammen,

    ich habe versucht, auf ganz normale int-Variable zuzugreifen, ABER über einen void-Zeiger, der auf einen int-Zeiger zeigt. Da spuckte mir der Compiler einen Fehler aus.

    #include <iostream>
    
    int main() 
    { 
    	void** pv;
    	int* pint;
    	int i;
    	pint 	   = &i;
    	pv 	   = &pint; 
    	**(int*)pv = 3378; 
    
    	std::cout << i  <<std::endl; 
    }
    

    Wisst Ihr vielleicht, wo ich hier den Fehler fabriziert habe? Danke für Eure Hilfe.

    Grüße aus Rheinland,

    Ratna 🙂


  • Administrator

    1. Wofür experimentierst du in C++ mit solch hässlichem Code herum?
    2. Ungenügende Compilerfehlermeldung. Wenn es eine Fehlermeldung gibt, zeig sie uns doch bitte.
    3. Verwende bitte cpp Tags für C++ Code.
    4. Einen doppelten void -Zeiger gibt es nicht. Der Cast am Ende ist zudem falsch, du möchtest schliesslich zurück auf einen int** . Also korrekt wäre:

    int main()
    { 
    	void* pv; 
    	int* pint; 
    	int i; 
    	pint = &i; 
    	pv = &pint; // Gibt einen int** zurück, wird als void* gespeichert.
    	**static_cast<int**>(pv) = 3378; 
    
    	std::cout << i  <<std::endl;
    
    	return 0;
    }
    

    Grüssli



  • Dravere schrieb:

    Also korrekt wäre:

    int main()
    { 
    	void* pv; 
    	int* pint; 
    	int i; 
    	pint = &i; 
    	pv = &pint; // Gibt einen int** zurück, wird als void* gespeichert.
    	**static_cast<int**>(pv) = 3378; 
    
    	std::cout << i  <<std::endl;
    
    	return 0;
    }
    

    Wozu hierbei Zeiger auf Zeiger?
    Korrekt ist:

    #include <iostream>
    int main()
    {
        int i;
    	void* pv = &i; 
    	
    	*static_cast<int*>(pv) = 3378;
    
        std::cout << i  <<std::endl;
    
        return 0;
    }
    

    :xmas1:
    Grüssli



  • Hallo zusammen 🙂 ,
    vielen Dank erstmal für Eure Beiträge, entschuldige wenn ich einige mit meinen Fragen ein wenig genervt habe. Ich glaube es ist einfach von der Natur aus so, dass Frauen Dinge mehr hinterfragen, weshalb ich mich ein wenig mehr mit dem Thema void* beschäftige.

    Ich habe aus dem Internet ein Stück Lernmaterial gefunden, dem glaube ich ein Fehler unterläuft. Vielleicht könnt Ihr mir das bestätigen oder gar mich korriegeren, wenn ich nen Denkfehler fabriziert habe. Danke im Voraus.

    Folgender Code:

    #include <iostream>
    
    int main() {
        int intValue = 1;
        int *intPointer = & intValue; // zeigt auf intValue
    
        void *voidPointer;
        voidPointer = intPointer; // voidPointer zeigt auf intPointer
    

    Meiner Ansicht nach ist der 2. Kommentar nicht richtig, zeigt denn voidPointer nicht auf intValue? Der Verlauf geht zwar ÃœBER intPointer, zeigen tut aber voidPointer doch auf intValue, oder?

    Grüße aus Rheinland,

    Ratna 😉

    PS. Kann man eigentlich im Beitrag Datei anhängen? Ich habe bei Zeigeraufgaben immer Zeichnungen gemacht, zu diesem Code hab ich auch eine gemacht, leider kann man scheinbar keine Datei anhängen.



  • Richtig, voidPointer zeigt auf intValue.

    'intPointer' repräsentiert die Adresse von intValue, und da du voidPointer auf diese Adresse zeigen lässt ...

    MfG



  • hustbaer schrieb:

    Hier reinterpret_cast zu empfehlen ist IMO grob falsch.
    Angebracht wäre ein static_cast.

    Argh! Natürlich, static_cast reicht ja bei void* . Dabei habe ich das gerade eben noch verwendet... 🙄

    muss_klausur_bestehen schrieb:

    PS. Kann man eigentlich im Beitrag Datei anhängen? Ich habe bei Zeigeraufgaben immer Zeichnungen gemacht, zu diesem Code hab ich auch eine gemacht, leider kann man scheinbar keine Datei anhängen.

    Nein, das ist hier nicht möglich. Du kannst aber die Datei irgendwo hochladen und verlinken.

    Wobei bei Code-Zeichnungen die Chance natürlich grösser ist, dass sich jemand Zeit dafür nimmt. Besonders, wenn man sieht, dass sich jemand Mühe gegeben hat... 🙂


  • Administrator

    muss_klausur_bestehen schrieb:

    Ich glaube es ist einfach von der Natur aus so, dass Frauen Dinge mehr hinterfragen, weshalb ich mich ein wenig mehr mit dem Thema void* beschäftige.

    Ist einfach unglaublich, früher haben die Männern die Frauen diskriminiert, heute ist es umgekehrt ...
    Es ist schön, dass man die Sache hinterfragt und sich damit beschäftigt. Die Sache ist einfach nur so, dass man void* in C++ so gut wie gar nie benötigt. void* ist extrem gefährlich, da es ein Zeiger ohne Typ ist. Man vernichtet damit jegliche Typsicherheit, welche in C++ sehr wertvoll ist. Jeglicher void -Zeiger ist eine extreme Gefahr in einem C++ Programm und sollte daher unbedingt vermieden werden, ausser man weiss ganz genau, was man macht.

    void* stammt hauptsächlich aus C, wo man keine Templates hat und dadurch, wenn man über eine Funktion mehrere Typen durchbringen möchte, auf void* zurückgreifen musste.

    // Als typisches Beispiel, qsort aus C:
    void qsort(void* base, size_t num, size_t size, int (*comparator)(void const*, void const*));
    

    In C++ hat man genau für solche Dinge die C++ Templates:

    template<typename TypeT>
    void qsort(TypeT* base, size_t num, int (*comparator)(TypeT const*, TypeT const*));
    

    Dadurch wird für jeden Typ vom Kompiler eine entsprechende Funktion automatisch generiert und die Typsicherheit ist völlig gewährleistet.
    Ich möchte auch darauf hinweisen, dass dies nur als Beispiel gedacht ist. In C++ kann man dass sogar noch deutlich schöner mit den Templates lösen, aber ich wollte, dass man noch eine Ähnlichkeit wahrnehmen kann 😉

    Wieso der ganze Vortrag?
    Es ist schön und gut, dass man sich mit void* auskennt, aber man sollte sich bewusst sein, dass void* in 99,99% der Fälle unter C++ der falsche Weg ist.

    Grüssli



  • Dravere schrieb:

    4. Einen doppelten void -Zeiger gibt es nicht.

    Glaub ich nicht!
    "void**" frisst jeder Compiler. Hab den Standard gerade nicht hier, würde mich aber SEHR wundern wenn da drin stehen würde dass das nicht OK sein sollte. Wieso auch, sehe keinen Grund warum das nicht erlaubt sein sollte.


  • Administrator

    hustbaer schrieb:

    Dravere schrieb:

    4. Einen doppelten void -Zeiger gibt es nicht.

    Glaub ich nicht!

    Ist auch korrekt, dies nicht zu glauben, habe gerade nachgeschaut. Es gibt anscheinend von der Standardbibliothek selbst eine Verwendung in den Streamklassen.

    Aber der Sinn entzieht sich mir. Jeglicher Zeiger (auch Funktionszeiger) lässt sich auf einen void* abbilden. Das ist so im Standard definiert. Daher ist ein void** nur möglich, als Zeiger auf einen void* , alles andere ist nicht erlaubt, was auch irgendwo Sinn macht.

    ...

    Und mir kam gerade eine Idee, wieso das auch Sinn macht. Ach ich trottel ... ein void* hat im Gegensatz zu einem void natürlich einen Speicherbereich. *Kopf -> Tisch / Tastatur*
    Man denke daher nur zum Beispiel an folgendes Beispiel:

    int (*func)(char, double) = 0; // Nur als Beispiel.
    
    void** p = new void*[20]; // 20 oder x oder y was auch immer.
    
    *p = func;
    ++p;
    *p = 0; // usw.
    
    delete[] p;
    

    Allerdings ist mir sowas in den x Jahren noch nicht begegnet in C++.

    Grüssli



  • Ich glaube mich zu erinnern dass der Standard nicht "erlaubt" Funktionszeiger zu Datenzeigern (inklusive void*) zu casten (bzw. es nicht garantiert, kann sein dass "implementation defined" dasteht oder sowas). Was *sehr* schade ist, da man so einige Lowlevel-Dinge nicht portabel programmieren kann.


  • Administrator

    hustbaer schrieb:

    Ich glaube mich zu erinnern dass der Standard nicht "erlaubt" Funktionszeiger zu Datenzeigern (inklusive void*) zu casten (bzw. es nicht garantiert, kann sein dass "implementation defined" dasteht oder sowas). Was *sehr* schade ist, da man so einige Lowlevel-Dinge nicht portabel programmieren kann.

    Ich weiss nur, dass es mit dem MSVC funktioniert und habe auch nie gegenteiliges gelernt. Nur bei Zeiger zu Membern weiss ich, dass man sie nicht einem void* zuweisen darf. Dort hat es allerdings irgendeinen Grund wegen Typsicherheit.
    Aber keine Ahnung, ich weiss es schlicht und einfach nicht und brauche solche Konvertierungen auch nicht 😉

    Grüssli


Log in to reply