void **mar_init(...), float **f; --> WTF



  • Eine void*-Funktion gibt nicht Nichts zurück, sondern einen Void-Pointer. Das ist ein Pointer, der auf eine einzelne Adresse zeigt, aber keine "Größe" hat. Somit kann man ihn auch nicht dereferenzieren. Ich würde mal sagen, dass man einen Doppel-Voidpointer also erstmal konvertieren müsste, um einmal dereferenzieren zu können um an die 2. Adresse zu kommen.
    Ein float-Doppelpointer könnte zum Einen benutzt werden, um ein 2-dimensionales dynamisches Array "darzustellen", oder wenn du die Adresse von dem Pointer in einer Funktion, der der Pointer als Argument übergeben wurde geändert werden muss. Dies wäre sonst nicht möglich, da die Adresse eines Pointers wie bei jeder anderen Variablen by-value übergeben wird.



  • masterofx32 schrieb:

    Ein float-Doppelpointer könnte zum Einen benutzt werden, um ein 2-dimensionales dynamisches Array "darzustellen"

    Das könnte hinkommen, denn die Aufgabe ist es eine Matrix einzulesen. Dazu wird als erstes angegeben wie viele Elemente diese Matrix hat (quadratisch) und diese sollen dann in einem Array gespeichert werden, kann mir bitte noch wer einen Tipp geben wie man das realisieren könnte, ich steh im Moment leider total daneben und die Suchen Funktion will auch nicht so recht. 😞

    Danke



  • Hat sich alles erledigt, vielen Dank für die hilfreichen Tipps. 🙂



  • void** macht doch gar keinen sinn, oder übersehe ich da was?



  • Ja, void-Pointer != void.



  • DrGreenthumb schrieb:

    void** macht doch gar keinen sinn, oder übersehe ich da was?

    Wie übergibst du einen void* denn sonst by reference?

    Oder wenn wir von generischen Funktionen ausgehen, die nehmen oft nen void* als Param, da kann man leicht void** haben. Sehe da kein 'Sinn'-Problem. Oft vorkommen tut es allerdings nicht.



  • DrGreenthumb schrieb:

    void** macht doch gar keinen sinn, oder übersehe ich da was?

    ich glaub auch. 'void*' (nur ein sternchen) reicht



  • Shade Of Mine schrieb:

    DrGreenthumb schrieb:

    void** macht doch gar keinen sinn, oder übersehe ich da was?

    Wie übergibst du einen void* denn sonst by reference?

    ja stimmt, hatte nen denkfehler



  • DrGreenthumb schrieb:

    void** macht doch gar keinen sinn, oder übersehe ich da was?

    wieso soll das kein Sinn machen? Ein Doppelpointer auf etwas unbekanntes.

    rex@supertux:~/tmp> cat s.c 
    #include <stdio.h>
    
    void p(void** b)
    {
            printf("%s\n", *b);
    }
    
    int main(void)
    {
            char* x = "Hello World";
            p((void**) &x);
    
            return 0;
    }
    rex@supertux:~/tmp> gcc s.c -osc -ansi
    rex@supertux:~/tmp> ./sc 
    Hello World
    

    Ich hab sowas auch nie gesehen, aber so geht es, der Compiler meckert nicht einmal. Ich glaube, es gibt einfach keine Verwendung dafür



  • DrGreenthumb schrieb:

    void** macht doch gar keinen sinn, oder übersehe ich da was?

    ist mir nur einmal begegnet, daß ich void** schreiben wollte:

    class Memory
    {
    public:
    	enum{PAGESIZE=8192,FRAGMENTATION=65536};
    private:
    	void *freePage;
    public:
    	Memory(void)
    	{
    		freePage=NULL;
    	};
    	~Memory(void)
    	{
    	};
    	void grow(void)
    	{
    		char *p=(char*)VirtualAlloc(
    			NULL,
    			FRAGMENTATION,
    			MEM_COMMIT|MEM_RESERVE,
    			PAGE_READWRITE
    			);
    		for(int i=0;i<FRAGMENTATION/PAGESIZE;i++)
    		{
    			free(p);
    			p+=PAGESIZE;
    		};
    	};
    	void *alloc(void)
    	{
    		if(freePage==NULL)
    		{
    			grow();
    		}
    		void *result=freePage;
    		freePage=*(void**)freePage;
    //		cout<<"alloc "<<result<<'\n';
    		return result;
    	};
    ...
    


  • "void**" wird sogar sehr haeufig verwendet, wenn eine Funktion einen oder mehrere "void*" zurueckliefern muss, der Return-Wert aber schon von einem Error-Code z.B. vorbelegt ist (z.B. bei API-Funktionen):

    typedef int ApiError;
    
    ApiError API_AllocBuffers( void* Context_Handle, size_t size, void** buf1, void** buf2 );
    

    verwendet wird das dann z.B. so:

    bool init1( void* context ) {
       ApiError err; void* buf1 = 0; void* buf2 = 0;
       err = API_AllocBuffers( context, sizeof( XYZ ), &buf1, &buf2 );
       if ( err != APIERR_OKAY ) return false;
       /* ... */
       return true;  
    }
    


  • Power Off schrieb:

    "void**" wird sogar sehr haeufig verwendet, wenn eine Funktion einen oder mehrere "void*" zurueckliefern muss, der Return-Wert aber schon von einem Error-Code z.B. vorbelegt ist (z.B. bei API-Funktionen):

    welche api macht das so?



  • volkard schrieb:

    welche api macht das so?

    z.B. COM- und OLE-APIs in Windows, wie z.B. QueryInterface(), meine eigenen Libraries, die ich auf der Arbeit oder zu Hause entwickelt habe, usw. -- oder CORBA, z.B.

    Es gehoert zum sauberen API-Design, dass APIs, die einen Error-Code zurueckliefern, dies konsequent z.B. ueber den Rueckgabewert tun. Andere Rueckgabewerte muessen dann in C++ als Referenz, in C als Pointer-to-X an die API-Funktion als InOut-Parameter uebergeben werden.

    Pointer sind ja die einzige Moeglichkeit, in C bei einem Call einen Pass-By-Reference von Argumenten zu machen.

    Hat man jetzt ein Pass-By-Reference eines Zeigers, kommen zwangslaeufig "X**" ins Spiel. Das laesst sich gar nicht verhindern.

    Dafuer gibt's solche Konstruktionen in C ja. Sogar der Parameter von main(), argv, ist vom Typ "char**", also nutzt fast jedes C Programm Pointer-To-Pointer. 😉

    "X***" hingegen oder "X****" werden seltener verwendet, kommen aber gelegentlich vor.



  • Power Off schrieb:

    Pointer sind ja die einzige Moeglichkeit, in C bei einem Call einen Pass-By-Reference von Argumenten zu machen.

    jo, aber es reicht ein einfacher void*, weil der schon eine adresse beinhaltet, die auf alles mögliche zeigen kann. auch auf andere pointer oder pointer auf pointer usw. bei void* ist mehrfache indirektion echt überflüssig



  • void** als Parameter ist ja verständlich, aber was bringt einem denn ein Doppel-Voidpointer als Rückgabewert?



  • net schrieb:

    jo, aber es reicht ein einfacher void*, weil der schon eine adresse beinhaltet, die auf alles mögliche zeigen kann. auch auf andere pointer oder pointer auf pointer usw. bei void* ist mehrfache indirektion echt überflüssig

    Das fuehrt dazu, dass auf beiden Seiten (beim Caller und bei der API-Funktion) dann bloedsinnige Casts erforderlich sind:

    void apifunc( void* arg ) {
       *((void**)arg) = allocate_it();
    }
    void caller( void ) {
       void* ptr = 0;
       apifunc( (void*)(&ptr) );
    }
    

    waehrenddessen "void**" viel sauberer und leicher verstaendlich ist:

    void apifunc( void** arg ) {
       *arg = allocate_it();
    }
    void caller( void ) {
       void* ptr = 0;
       apifunc( &ptr );
    }
    

    klaro? 🙂



  • Power Off schrieb:

    net schrieb:

    jo, aber es reicht ein einfacher void*, weil der schon eine adresse beinhaltet, die auf alles mögliche zeigen kann. auch auf andere pointer oder pointer auf pointer usw. bei void* ist mehrfache indirektion echt überflüssig

    Das fuehrt dazu, dass auf beiden Seiten (beim Caller und bei der API-Funktion) dann bloedsinnige Casts erforderlich sind:

    na, so schlimm ist das nicht. bei void* kommt man in den meisten fällen sowieso nicht um casts herum, wenn man was damit machen will. und wenn einem das zu kryptisch erscheint kann man ja noch ein 'typedef' oder '#define' daraus machen.

    btw: in c braucht der caller keinen cast zu machen, in c++ aber schon.



  • masterofx32 schrieb:

    void** als Parameter ist ja verständlich, aber was bringt einem denn ein Doppel-Voidpointer als Rückgabewert?

    Es zeigt, dass ein zweidimensionales, obskures Array zurueckgegeben wird, d.h. der Aufrufer weiss, das es zweidimensional ist, weiss aber nicht, welche Daten darin enthalten sind. Vielleicht sind die Datentypen ja auch variabel, dann ergibt es auch einen Sinn.

    void** alloc_float_or_double_array( size_t xdim, size_t ydim, int type ) {
       if ( type == 1 ) {  // float-Array
          float** f = (float**) malloc( sizeof(float*) * ydim ); size_t i;
          if ( f == 0 ) return 0;
          for ( i=0; i < ydim; ++i ) {
             f[i] = (float*) malloc( sizeof(float) * xdim );
             if ( f[i] == 0 ) { 
                while ( --i >= 0 ) free( f[i] );
                free( (void*) f );
                return 0;
            }
          }
          return (void**) f;
       }
       if ( type == 2 ) {  // double-Array
          double** d = (double**) malloc( sizeof(double*) * ydim ); size_t i;
          if ( d == 0 ) return 0;
          for ( i=0; i < ydim; ++i ) {
             d[i] = (double*) malloc( sizeof(double) * xdim );
             if ( d[i] == 0 ) { 
                while ( --i >= 0 ) free( d[i] );
                free( (void*) d );
                return 0;
            }
          }
          return (void**) d;
       }
       return 0;     
    }
    void testfunc( void ) {
       float** f = (float**) alloc_float_or_double_array( 30U, 40U, 1 );
       if ( f != 0 ) { /* ... */ }
    }
    


  • net schrieb:

    btw: in c braucht der caller keinen cast zu machen, in c++ aber schon.

    Laut Standard mag das stimmen -- und zwar auch nur, wenn der Level of Indirection derselbe ist -- aber die Compiler verlangen in der Praxis oft immer noch casts.


Anmelden zum Antworten