Fragen zum Verständnis von void



  • sorry, das habe ich nicht verstanden, könntest du mir bitte mal ein Beispiel geben?? z.b wenn ich Zeiger auf eine normale integer oder Zeiger auf ein int-Array als Parameter für die Funktion verwende?

    LG 😮



  • (void*) ist ein Zeiger auf einen nicht spezifizierten Typ.
    (void**) ist ein Zeiger auf einen Zeiger auf einen nicht spezifizierten Typ.



  • Danke für eure Antwort. es klingt schon logisch 😛

    Aber wenn ich in meiner Funktion auch einen Wert vom Typ void ausgeben möchte, wie mache ich denn das?

    Was sollte ich statt xxx reinschreiben? 😕

    void ausgabe(void **x){
    
      printf("Ergebnis %xxx :", **x);
    
    }
    

    Danke schön!

    LG, Carvin



  • Um Adressen auszugeben benutze %p 🙂



  • Einen Wert vom Typ void kann man nicht ausgeben, höchstens dessen Adresse.



  • So gehts, also nicht dereferenzieren:

    void ausgabe(void **x)
    { 
    	printf("Ergebnis %p :", x); 
    }
    


  • Btw, void** finde ich ziemlich blödsinnig. Zum Rumreichen beliebiger Pointer reicht void*.



  • void** darf durchaus dereferenziert werden.

    Ich frage mich allerdings warum der OP meint das zu brauchen?!

    Solange Du nicht irgendeine Bibliothek schreibst oder nutzt wirst Du kaum eine Verwendung für void* haben. Für void** erst recht nicht.

    Und Deine Frage mit dem printf() : denk daran, dass der Compiler nicht zaubern kann. Du musst wissen auf was der Zeiger zeigt(*).

    😉 eigentlich: ...auf was der Zeiger zeigt, auf den der Zeiger zeigt. 🙂



  • Einmal dereferenzieren kann man void** schon.



  • Z schrieb:

    Btw, void** finde ich ziemlich blödsinnig. Zum Rumreichen beliebiger Pointer reicht void*.

    void** ist gar nicht unsinnig. Es ist ein Zeiger auf einen Zeiger auf etwas.
    Es schränkt den Typ weiter ein als nur "Zeiger auf etwas".

    Anfangen kann man auch direkt was damit, vor allem in C, wo man void* -> T* ohne Cast konvertieren kann.
    Und ansonsten kann man *ppv auch direkt an memcpy & Co übergeben.



  • hustbaer schrieb:

    Z schrieb:

    Btw, void** finde ich ziemlich blödsinnig. Zum Rumreichen beliebiger Pointer reicht void*.

    void** ist gar nicht unsinnig. Es ist ein Zeiger auf einen Zeiger auf etwas.
    Es schränkt den Typ weiter ein als nur "Zeiger auf etwas".

    Das hört sich erstmal ganz logisch an. Aber wenn man bedenkt, dass void* ein Zeiger auf etwas Beliebiges ist, dann dann kann dieses Beliebige auch ein Zeiger auf Zeiger sein.

    Ich frage mich, ob es irgendeinen sinnvollen Anwendungsfall für void** gibt.



  • int x = 10;
    
    void liefert_einen_void_ptr(void** p_ptr)
    {
      *p_ptr = &x;
    }
    
    int main()
    {
      void* ptr = 0;
      liefert_einen_void_ptr(&ptr);
    }
    

  • Mod

    Z schrieb:

    Es schränkt den Typ weiter ein als nur "Zeiger auf etwas".

    Das hört sich erstmal ganz logisch an. Aber wenn man bedenkt, dass void* ein Zeiger auf etwas Beliebiges ist, dann dann kann dieses Beliebige auch ein Zeiger auf Zeiger sein.

    Aber eben nicht umgekehrt. Das ist es doch gerade, worum es geht!



  • Das hört sich erstmal ganz logisch an. Aber wenn man bedenkt, dass void* ein Zeiger auf etwas Beliebiges ist, dann dann kann dieses Beliebige auch ein Zeiger auf Zeiger sein.

    Nach der Argumentation müsste man Zeiger grundsätzlich als void* deklarieren.



  • Genau.
    T* ist weiter eingeschränkt* als void* , und daher vorzuziehen wenn es sich wirklich um einen T* handelt.
    replace("T", "void*")
    void** ist weiter eingeschränkt* als void* , und daher vorzuziehen wenn es sich wirklich um einen void** handelt.

    *: weiter eingeschränkt = weniger leicht falsch zu verwenden, beinhaltet mehr Informationen etc.

    @Z
    Wieso zitierst du den Satz "Es schränkt den Typ weiter ein als nur "Zeiger auf etwas".", ignorierst ihn in deine Antwort dann aber total? 😕

    Z schrieb:

    Aber wenn man bedenkt, dass void* ein Zeiger auf etwas Beliebiges ist, dann dann kann dieses Beliebige auch ein Zeiger auf Zeiger sein.

    Ich frage mich, ob es irgendeinen sinnvollen Anwendungsfall für void** gibt.

    Du musst bloss lesen was ich schreibe.
    void* wird z.B. oft als Zeiger auf "einfach Speicher" verwendet - wie bei memcpy etc.
    Und wenn du jetzt einen Zeiger auf so einen Zeiger auf "einfach Speicher" hast, dann ist das Typ davon natürlich void** .

    Oder: klassische C Type-Erasure --> man arbeitet mit void* . Wenn du jetzt eine Funktion hast die so einen void* befüllen muss, dann übergibst du natürlich einen void** .

    void Fun(void** ppv)
    {
        SomeType* px = ...;
        *ppv = px;
    }
    

    Was ist daran so schwer zu verstehen?



  • Z schrieb:

    Ich frage mich, ob es irgendeinen sinnvollen Anwendungsfall für void** gibt.

    Jup. Sobald man Graphen hat und für die Generizität statt KnotenFooBarEbbes* gerne void* mag, da hat man für KanteFooBarEbbes*==*KnotenFooBarEbbes eben void**. Und es fühlt sich gut an, gibt auf Anhieb fehlerfreien Code, speicherlochfrei, effizient und wartbar. Wenn die Datenstruktur danach verlangt.



  • Danke, Volkard.
    Ich erinnere mich, sowas ähnliches tatsächlich mal gesehen zu haben.



  • @Husti, dass man void** irgendwie verwenden kann, war nicht meine Frage.



  • Hi,

    Vielen Dank für eure hilfreichen Antworten und Erklärungen, sie haben mir beim Verstehen von void sehr geholfen.

    Mit void** wollte/möchte ich eine Funktion schreiben, die sowohl zwei Werte im beliebigen Format als auch 2 Arrays im beliebigen Format austauscht. Die Idee wäre eine Erweiterung von der Funktion für Austausch von 2 Integer-Werte / Int-Arrays mit pointer of pointer.

    Nun mein Code in header Datei:

    void tauschTowZeiger(int **i, int **j){
    
    	printf("\n Die Werte VOR: %d, %d \n", **i, **j);
    
    	int* tmpZeiger = *i;
    	*i = *j;
    	*j = tmpZeiger;
    
    	printf("\n Die Werte NACH: %d, %d \n", **i, **j);
    
    }
    
    void exchangTowPointerUniversal(void **i, void **j){
    
    	printf("\n Die Werte VOR: %d, %d \n", **i, **j);
    
    	void* tmpZeiger = *i;
    	*i = *j;
    	*j = tmpZeiger;
    
    	printf("\n Die Werte NACH: %d, %d \n", **i, **j);
    }
    

    Die 2. Funktion habe ich völlig nach der ersten nachgebaut und dachte dass ich die ausgetauschten Werte auch direkt in der Funktion ausgeben könnte...

    Das ist ja mein 1.Problem zu wissen ob ich wirklich den Wert ausgeben kann?

    Mein 2. Problem ist, ich rufe die 2. Funktion in meiner Main-Methode auf, werden aber die Werte nicht getauscht ... warum?

    Aufruf in der Main:

    //typenlose Variablen tauschen
    
    	float af = 7.56;
    	float bf = 9.81;
    	float* afp = ⁡
    	float* bfp = &bf;
    
    	printf("\n Typenlose Werte %d und %d tauschen für int. \n", inta, intb);
    
    	exchangTowPointerUniversal((void**)&pp1, (void**)&pp2);
    
    	printf("\n Die getauschte 1. Zahl %d und 2. Zahl %d \n", inta, intb);
    
    	printf("\n Typenlose Werte %f und %f tauschen für float. \n", af, bf);
    
    	exchangTowPointerUniversal((void**)&afp, (void**)&bfp);
    
    	printf("\n Die getauschte 1. Zahl %f und 2. Zahl %f \n",  af, bf);
    

    Vielen Dank!!!

    LG, Carvin 😋



  • hanowde schrieb:

    Die 2. Funktion habe ich völlig nach der ersten nachgebaut und dachte dass ich die ausgetauschten Werte auch direkt in der Funktion ausgeben könnte...

    Das ist ja mein 1.Problem zu wissen ob ich wirklich den Wert ausgeben kann?

    Wenn du in der Funktion weißt, welcher Typ das ist, kannst du entsprechen casten.

    printf("\n Die Werte NACH: %d, %d \n", *((int*)*i), *((int*)*j));
    

    Aber dann kannst du gleich die erste Version nehmen.

    hanowde schrieb:

    Mein 2. Problem ist, ich rufe die 2. Funktion in meiner Main-Methode auf, werden aber die Werte nicht getauscht ... warum?

    Du hast nur die Zeiger getausch und nicht die Werte.

    printf("\n Typenlose Werte %f und %f tauschen für float. \n", *afp, *bfp);
    
        exchangTowPointerUniversal((void**)&afp, (void**)&bfp);
    
        printf("\n Die getauschte 1. Zahl %f und 2. Zahl %f \n", *afp, *bfp);
    

Anmelden zum Antworten