malloc in externer funktion?



  • hallo,

    ich hab hier vor einiger zeit in einem thread gelesen, dass man malloc, bzw. free nicht in externen funktionen aufrufen sollte.

    meine frage ist, wie dass dan die vielen anderen bibliotheken, z.b. die zlib macht.

    dies ist mein code:

    #include <stdio.h>
    #include <stdlib.h>
    
    int openThisFile(char *a,unsigned char *_malloc) // a=dateiname, _malloc=pointer auf den zu allozierenden speicher
    {
    	FILE *test=fopen(a,"rb");
    	if(test==NULL)
    	{
    		return 1; // wenn datei nicht geöffnet werden kann
    	}
    
    	fseek(test,0,SEEK_END);
    	unsigned int filesize=ftell(test);
    	fseek(test,0,SEEK_SET);
    
    	_malloc=(unsigned char *)malloc(filesize);
    	if(_malloc==NULL)
    	{
    		fclose(test);
    		return 2; // wenn speicher nicht alloziert werden kann
    	}
    
    	fread(_malloc,1,filesize,test);
    	fclose(test);
    
    	return 0; // wenn alles ok
    }
    
    int closeThisFile(unsigned char *_malloc) // _malloc=pointer auf speicherbereich
    {
    	free(_malloc);
    	return 0; 
    }
    
    int main()
    {
    	unsigned char *_malloc;
    	int return1=openThisFile("test.txt",_malloc);
    	printf("openThisFile -- return value: %d\n",return1);
    	int return2=closeThisFile(_malloc);
    	printf("closeThisFile -- return value: %d\n",return2);
    	return 0;
    }
    

    ist dies nun gut oder schlechter stil, bzw. wie machen dass die libs und engines?



  • Hallo,
    ich lagere Speicherreservierung immer aus. Der Code lässt sich so m.M.n. übersichtlicher gestalten.
    Das hat auch den Vorteil, dass du bei größeren Projekten die Anzahl der malloc's/free's für Debugzwecke besser zählen/ kontrollieren kannst ( zu jedem malloc ein free ).

    int alloc ( some_type** some_pointer )
    {
    	some_type* new_type = malloc ( sizeof ( *new_type ));
    	*some_pointer = new_type;
    	return NULL == new_type; // 1: Fehler, 0: OK.
    }
    
    int main()
    {
    	some_type* ptype;
    	if ( alloc ( &ptype ))
    		return 1; // Fehlerbehandlung ...
    	else
    		... // oki doki ...
    	return 0;
    }
    

    Ansonsten hätte ich etwas über deinen Code zu meckern :D:
    -Dein Speicher wird nicht freigegeben, obwohl du eine Funktion dafür vorgesehen hast
    -Variablen mit beginnendem Unterstrich sollte man vermeiden.
    (Mögliche Konflikte mit bereits bestehenden große Bibliotheken.)
    -Nicht ausgewertete Rückgabewerte von fseek, ftell, fread etc.
    -Filesize kann negativ werden

    💡 Wie es die großen Libs und Engines machen, guckst du am besten selbst in den Open Source Code rein.



  • CJosef schrieb:

    int alloc ( some_type** some_pointer )
    {
    	some_type* new_type = malloc ( sizeof ( *new_type ));
    	*some_pointer = new_type;
    	return NULL == new_type; // 1: Fehler, 0: OK.
    }
    
    int main()
    {
    	some_type* ptype;
    	if ( alloc ( &ptype ))
    		return 1; // Fehlerbehandlung ...
    	else
    		... // oki doki ...
    	return 0;
    }
    

    ok, dein code ist ein bisschen abstrakt:

    int alloc(unsigned char **my_malloc)
    {
         unsigned char *new_malloc=malloc (sizeof(*new_malloc)) // diese zeile verstehe ich nicht recht. du reservierst hier den inhalt von new_malloc, allerdings ist new_malloc noch nichts zugewießen, daher kannst du unmöglich die größe davon bestimmen
         *my_malloc=new_malloc;
         return NULL==new_malloc;
    }
    

    ist das so ungefähr richtig?

    CJosef schrieb:

    -Dein Speicher wird nicht freigegeben, obwohl du eine Funktion dafür vorgesehen hast

    warum? ich rufe die funktion "closeThisFile" am ende auf.

    CJosef schrieb:

    -Variablen mit beginnendem Unterstrich sollte man vermeiden.
    (Mögliche Konflikte mit bereits bestehenden große Bibliotheken.)

    CJosef schrieb:

    -Filesize kann negativ werden

    wann denn? ich glaube du meinst eher den rückgabewert von fseek()? im falle eines fehlers?



  • mallllllllloc schrieb:

    wann denn? ich glaube du meinst eher den rückgabewert von fseek()? im falle eines fehlers?

    natürlich meine ich ftell()


  • Mod

    mallllllloc schrieb:

    ist dies nun gut oder schlechter stil, bzw. wie machen dass die libs und engines?

    Die machen quasi ihr eigenes malloc/free, aber eine Ebene abstrakter, objektorientierter. So wie auch die Dateien in C funktionieren, wo man sie am Anfang initialisiert (open) und am Ende freigibt (close), ohne dass der Benutzer weiß, was da in den Funktionen genau passiert. Der Nutzer hält aber die ganze Zeit den Dateizeiger und ist dafür verantwortlich, dass er eine geöffnete Datei wieder schließt - gerade so wie bei malloc und free.

    Beispiel:

    // Bibliothekscode
    typedef struct
    {
     char *data;
     // Und mehr
    } HandleType;
    
    HandleType get_handle(/*Parameter*/)
    {
     HandleType temp;
     temp.data = malloc(irgendwas);
     // Anderes geheimnisvolles Zeug, was für die Bibliothek wichtig ist
     return temp;
    }
    
    void free_handle(HandleType which)
    {
     free(which.data);
     // Anderes geheimnisvolles Zeug
    }
    
    int do_stuff(HandleType handle)
    {
     // Die Bibliothek macht ihr Ding
    }
    
    // Anwendungscode:
    int main()
    {
     HandleType handle = get_handle();
     // ...
     do_stuff(handle);
     // ...
     free_handle(handle);
    }
    


  • ja aber da ist doch im grund das gleiche? das malloc/free wird in einer externen funktion aufgerufen.



  • mallllllllloc schrieb:

    ok, dein code ist ein bisschen abstrakt:
    ...
    ist das so ungefähr richtig?

    Ja, es ist allgemein gehalten, das kann ein beliebiger, selbstdefinierter Typ sein.
    Wenn du dir überlegst, wie groß sizeof(char*) ist, dann wohl eher nicht.

    mallllllllloc schrieb:

    CJosef schrieb:

    -Dein Speicher wird nicht freigegeben, obwohl du eine Funktion dafür vorgesehen hast

    warum? ich rufe die funktion "closeThisFile" am ende auf.

    Weil dein char* _malloc in der allokierenden Funktion ein anderes ist als in der main Funktion.

    mallllllllloc schrieb:

    CJosef schrieb:

    -Filesize kann negativ werden

    wann denn? ich glaube du meinst eher den rückgabewert von fseek()? im falle eines fehlers?

    Edit: Ooops, ich hab da ein int gesehen. 🙄 👎 vergiss es 😃

    malllllllloc schrieb:

    ja aber da ist doch im grund das gleiche? das malloc/free wird in einer externen funktion aufgerufen.

    Das sind Minimalstbeispiele. Weil es aber oft, wie schon erwähnt, selbstdefinierte Datentypen sind für die Speicher reserviert und freigegeben wird, reicht oftmals ein einziges malloc/free nicht aus, wenn der Typ seinserseits Zeiger hat mit denen weiterer Speicher reserviert wird.


  • Mod

    malllllllloc schrieb:

    ja aber da ist doch im grund das gleiche? das malloc/free wird in einer externen funktion aufgerufen.

    So ist das auch vollkommen ok. Was hier öfters vorgeschlagen wird und vor dem du gewarnt wurdest, ist das Schema, dass eine Funktion überraschend einen Zeiger zurück gibt, auf den man später free aufrufen muss, obwohl man selber gar kein malloc gemacht hat. Dass man das was ich oben "Handle" genannt habe wieder aufräumen muss ist hingegen für den Benutzer klar, er hat es schließlich vorher explizit mit der get_handle-Funktion erzeugt.



  • mallllllllloc schrieb:

    diese zeile verstehe ich nicht recht. du reservierst hier den inhalt von new_malloc, allerdings ist new_malloc noch nichts zugewießen, daher kannst du unmöglich die größe davon bestimmen

    Kann man. Das ist im Prinzip das selbe in Grün:

    typedef struct tagSomeType
    {
    	double dnumber;
    	int inumber;
    	char* pchar;	
    }SomeType;
    
    int main(void)
    {
    	SomeType* pst;
    	printf("%d\n", sizeof(SomeType) == sizeof(*pst));
    };
    


  • SeppJ schrieb:

    So ist das auch vollkommen ok. Was hier öfters vorgeschlagen wird und vor dem du gewarnt wurdest, ist das Schema, dass eine Funktion überraschend einen Zeiger zurück gibt, auf den man später free aufrufen muss, obwohl man selber gar kein malloc gemacht hat. Dass man das was ich oben "Handle" genannt habe wieder aufräumen muss ist hingegen für den Benutzer klar, er hat es schließlich vorher explizit mit der get_handle-Funktion erzeugt.

    ok, also im anderen thread wurde gesagt, dass das malloc, bzw. das free auf "gleicher ebene" aufgerufen werden sollte, das ist hier allerdings nicht der fall.



  • mallllllloc schrieb:

    SeppJ schrieb:

    So ist das auch vollkommen ok. Was hier öfters vorgeschlagen wird und vor dem du gewarnt wurdest, ist das Schema, dass eine Funktion überraschend einen Zeiger zurück gibt, auf den man später free aufrufen muss, obwohl man selber gar kein malloc gemacht hat. Dass man das was ich oben "Handle" genannt habe wieder aufräumen muss ist hingegen für den Benutzer klar, er hat es schließlich vorher explizit mit der get_handle-Funktion erzeugt.

    ok, also im anderen thread wurde gesagt, dass das malloc, bzw. das free auf "gleicher ebene" aufgerufen werden sollte, das ist hier allerdings nicht der fall.

    das ist käse.
    free wird aufgerufen, wenn das objekt nicht mehr benötigt wird.
    wann das der fall ist, das ist abhängig vom anwendungsfall und nicht von irgendwelchen ebenen. basta.
    🙂



  • käselochaufspürer schrieb:

    mallllllloc schrieb:

    SeppJ schrieb:

    So ist das auch vollkommen ok. Was hier öfters vorgeschlagen wird und vor dem du gewarnt wurdest, ist das Schema, dass eine Funktion überraschend einen Zeiger zurück gibt, auf den man später free aufrufen muss, obwohl man selber gar kein malloc gemacht hat. Dass man das was ich oben "Handle" genannt habe wieder aufräumen muss ist hingegen für den Benutzer klar, er hat es schließlich vorher explizit mit der get_handle-Funktion erzeugt.

    ok, also im anderen thread wurde gesagt, dass das malloc, bzw. das free auf "gleicher ebene" aufgerufen werden sollte, das ist hier allerdings nicht der fall.

    das ist käse.
    free wird aufgerufen, wenn das objekt nicht mehr benötigt wird.
    wann das der fall ist, das ist abhängig vom anwendungsfall und nicht von irgendwelchen ebenen. basta.
    🙂

    "man soll die verantwortung über malloc/free nicht an funktionen übergeben". das ist die hauptaussage des aderen threads.

    seltsam, ich frage hier sachen, die in anderen threads schon beantwortet wurden, erhalte allerdings gänzlich andere antworten...


  • Mod

    mallllllloc schrieb:

    ok, also im anderen thread wurde gesagt, dass das malloc, bzw. das free auf "gleicher ebene" aufgerufen werden sollte, das ist hier allerdings nicht der fall.

    Wo denn nicht?

    seltsam, ich frage hier sachen, die in anderen threads schon beantwortet wurden, erhalte allerdings gänzlich andere antworten...

    Du hast die Antworten und die Konzepte dahinter anscheinend nicht verstanden, daher deine Verwirrung.



  • nein. auf gleicher eben bedeutet für mich: in EINER funktion wird speicher alloziert und in der GLEICHEN funktion wieder freigegeben.

    z.b.

    int main()
    {
        char *speicher=malloc(500);
        free(speicher);
    }
    

    und nicht:

    int a(char *speicher)
    {
         speicher=malloc(500);
         return 0;
    }
    
    int b(char *speicher)
    {
        free(speicher);
        return 0;
    }
    
    int main()
    {
         char *speicher;
         a(speicher);
         b(speicher);
    }
    

    laut dem anderen threads wird hier die verantwortung über malloc/free an die funktionen übergeben, und das soll man (lt. anderem thread) nicht machen.



  • malllllllllloc schrieb:

    nein. auf gleicher eben bedeutet für mich: [...]

    SeppJ schrieb:

    Du hast die Antworten und die Konzepte dahinter anscheinend nicht verstanden, daher deine Verwirrung.



  • malllllllllloc schrieb:

    laut dem anderen threads wird hier die verantwortung über malloc/free an die funktionen übergeben, und das soll man (lt. anderem thread) nicht machen.

    Ungläubiger!!! 😃
    Werf doch mal nen Blick in den Code größerer Projekte, das ist allgemein üblich eigene "Konstruktoren" und "Destruktoren" zu verwenden, a la

    typedef struct tagAnyObject1
    {
    	int number;
    	// Eventuel andere Objektmember, die dynamisch erzeugt werden ...
    	...
    }AnyObject1;
    
    typedef struct tagAnyObject2
    {
    	double number;
    	// Eventuel andere Objektmember, die dynamisch erzeugt werden ...
    	...
    }AnyObject2;
    
    int create_any_object1( AnyObject1** p )
    {
    	AnyObject1* new_object = malloc ( sizeof ( *new_object ));
    	if ( NULL != new_object )
    	{
    		// Eventuell andere Member dynamisch erzeugen ...
    		...
    	}
        return NULL == ( *p = new_object ); // 1: Fehler, 0: OK. 
    }
    
    int create_any_object2( AnyObject1** p )
    {
    	AnyObject2* new_object = malloc ( sizeof ( *new_object ));
    	if ( NULL != new_object )
    	{
    		// Eventuell andere Member dynamisch erzeugen ...
    		...
    	}
        return NULL == ( *p = new_object ); // 1: Fehler, 0: OK. 
    }
    
    void free_object1 ( AnyObject1* p )
    {
    	if ( NULL == p ) 
    		return;
    	// Eventuell dynamisch erzeugte Objektmember freigeben ...
    	...
    	free ( p ); // Objekt freigeben.
    }
    
    void free_object2 ( AnyObject2* p )
    {
    	if ( NULL == p ) 
    		return;
    	// Eventuell dynamisch erzeugte Objektmember freigeben ...
    	...
    	free ( p ); // Objekt freigeben.
    }
    

    Was soll denn bitte daran schlecht sein? 😕
    Ganz im Gegenteil, das Auslagern der Erzeugung der Objekte und die Freigabe des
    Speichers schafft Übersicht und verhindert Spaghetticode, denn sehr oft reicht ein einziges
    malloc nicht aus, um ein Objekt zu erzeugen(hinter meinen ... in den Beispielen können viele viele weitere Zeilen stecken, die zur Erzeugung/Freigabe eines
    Objekts nötig sind). Klar soweit?
    Da gibts eigentlich auch nichts weiter zu diskutieren! 😃



  • ok. dann hat man wohl im anderen thread mist erzählt oder was?!?

    @Swordfish:
    wirklich aussagekräftig....



  • mallllllloc schrieb:

    FILE *test=fopen(a,"rb");
    	if(test==NULL)
    	{
    		return 1; // wenn datei nicht geöffnet werden kann
    	}
    	
    	fseek(test,0,SEEK_END);
    

    Die Kombination aus "rb" + fseek/SEEK_END liefert undefiniertes Verhalten.



  • mallllllloc schrieb:

    unsigned int filesize=ftell(test);
    

    datei > 4gb -> programm failure! ⚠
    -> betriebssystem funktionen benutzen! 💡


Log in to reply