verschachtelete Struktur an Funktion übergeben



  • Hallo zusammen,
    folgendes friemeliges Problem:
    Ich habe zwei Strukturen definiert, wobei die eine ein Member der anderen ist:

    typedef struct _schachtel_1 {
        char* name;
        int   nummer;
    } schachtel_1;
    
    typedef struct _schachtel_2 {
        schachtel_1** my_schachtel;
        int page;
    } schachtel_2;
    

    Wohl bemerkt daß in der Struktur schachtel_2 die Struktur schachtel_1 'nur' als Zeiger auf Zeiger vorkommt! Nun habe ich eine Funktion definiert, der ich den Zeiger einer schachtel_2 Struktur übergebe:

    void function_1(schachtel_2* outer_schachtel, int i);
    

    Für schachtel wird Speicher alloziert und dann der Zeiger an die Funktion übergeben:

    int main(int argc, char* argv[])
    {
        int i = 0;
        schachtel_2* outer_schachtel = NULL;
    
        if((outer_schachtel = (schachtel_2*) calloc(1, sizeof(schachtel_2))) == NULL)
        {
    	printf("geht nicht\n");
    	exit(-1);
        }
    
        for(i=0; i<=10; i++)
        {
    	function_1(outer_schachtel, i);	
    	i++;
        }
    
        return 0;
    }
    

    In der Funktion function_1 wird zunächst ein Zeiger der Struktur schachtel_1 angelegt und dann ein Speichersegement reserviert. Dann wird dieser Zeiger an das entsprechende Member. In der Funktion wird aus einer Datei geparst und die Ergenisse werden in schachtel_1 gespeichert. Bei jedem neuen Aufruf von function_1 soll so ein neue schachtel_1 gespeichert werden. Also ein Array von schachtel_1 innerhalb von schachtel_2, aber seht selbst:

    void function_1(schachtel_2* outer_schachtel, int i)
    {
        schachtel_1* inner_schachtel;
        printf("i: %d\n", i);
        if((inner_schachtel = (schachtel_1*) calloc(1, sizeof(schachtel_1))) == NULL)
        {
    	printf("geht gar nicht\n");
    	exit(-1);
        }
        inner_schachtel->nummer = 5;
        outer_schachtel->my_schachtel[i] = inner_schachtel;
    }
    

    Das ganze lässt sich wunderbar compilieren, bricht dann aber beim Versuch die innere-Schachtel an das Array der äusseren zu docken.

    Wo liegt mein Denkfehler? Und an welcher Stelle müsste ich den Speicher von schachtel_1 wieder freigeben?

    Vielen Dank vorab

    Gruss Christian



  • Du allokierst keinen Speicher für das Array "my_schatel".



  • Ich denke das muss ich ja nicht. my_schatel ist nur ein Zeiger auf einen Zeiger, an welchen man beliebig viele Zeiger anhängen kann. Ich erzeuge ja in der Funktion einen Speicherbereich vom Typ schachtel_1 und hänge/speichere diesen an dem Array.

    Gruss Christian

    <bitte löschen>



  • Ich denke das muss ich ja nicht. my_schatel ist nur ein Zeiger auf einen Zeiger, an welchen man beliebig viele Zeiger anhängen kann. Ich erzeuge ja in der Funktion einen Speicherbereich vom Typ schachtel_1 und hänge/speichere diesen an dem Array.

    Ahh, moment, die Zeiger brauch ja auch Platz!?!?

    Gruss Christian



  • Doch, musst du. Du allokierst Speicher für den Zeiger selbst, denn er ist Teil der Struktur. Doch wohin zeigt der Zeiger? Auf beliebigen Speicher. Und genau auf diesen greifst du zu. Du schreibst einfach irgendwo hin.

    Du musst Speicher für das Array allokieren.



  • #include <stdlib.h>
    
    struct inner {
      int foo;
    };
    
    struct outer
    {
      struct inner **ptrArr; /* Array von Zeiger auf inner-Strukturen. */
    };
    
    int main(void)
    {
      int i;
    
      /* Speicher für outer-Struktur allokieren */
      struct outer *o = malloc(sizeof *o);
    
      /* Speicher für Zeiger-Array allokieren */
      o->ptrArr = malloc(5 * sizeof *o->ptrArr); /* Array für 5 Zeiger!! */
    
      /* Array mit 5 Zeiger auf neue inner-Strukturen füllen. */
      for (i = 0; i < 5; ++i)
      {
        o->ptrArr[i] = malloc(sizeof *o->ptrArr[i]);
      }
    
      /* GESAMTEN ALLOKIERTEN SPEICHER WIEDER FREIGEBEN!!!! */
    
      return 0;
    }
    

    Den Rückgabewert von malloc, calloc und realloc castet man nicht!



  • Ich versuche mal es zu erklären:
    Für alles was du deklarierst bekommst du automatisch Speicher vom Compiler.

    schachtel_1 my_schachtel; //du kriegst Speicher für eine schachtel_1
    schachtel_1* my_schachtel; //du kriegst Speicher für einen Pointer auf schachtel_1, aber nicht für schachtel_1
    schachtel_1** my_schachtel; //du kriegst Speicher für einen Pointer auf einen Pointer auf schachtel_1, aber nicht für einen Pointer auf schachtel_1 und nicht für schachtel_1
    

    Du musst dir also per malloc einen Zeiger und eine Struktur besorgen. Wenn du allerdings den Zwischenzeiger nicht brauchst solltest du ihn weglassen.

    BTW: Ja, in C castet man malloc nicht. Wer allerdings einen C++-Compiler verwendet muss malloc sehr wohl casten. Das kommt von dieser C/C++-Sprache die jeder kennt und niemand wahr haben will.



  • nwp2 schrieb:

    BTW: Ja, in C castet man malloc nicht. Wer allerdings einen C++-Compiler verwendet muss malloc sehr wohl casten. Das kommt von dieser C/C++-Sprache die jeder kennt und niemand wahr haben will.

    Wenn er einen C++-Compiler benutzt, dann macht er was falsch. Er programmiert C, und muss daher einen C-Compiler benutzen. So einfach ist das.



  • Erst mal vielen Dank für Antworten.
    Aber noch ist mir das noch nicht ganz klar. Also ich habe am 17.04. um 0:49 erkannt, daß ich Platz schaffen muss für die Zeiger, die auf die inneren Segmente oder inneren Schachteln zeigen. Leider kann man hier ja keine Grafiken einbinden, denn ich habe zur Verdeutlichung so eine gezeichnet. Also eine grosse Box welche die äußere Schachtel zeigt. Von dort aus ein Pfeil auf eine Art Tabelle von der aus jede Zeile auf je eine kleine Box zeigt, welche die innere Box darstellt. So wie ich es verstanden habe, muss ich nun Platz für diese Tabelle anlegen, allerdings muss ich es dynamisch machen und mit realloc den Platz immer noch vergrössern.

    In Janjan's Code ist wird nachdem Platz für 5 Zeiger geschaffen wurde diese 5 Plätze noch einmal mit einem Zeiger initialisiert. Aber es wird gar kein Platz geschaffen für die inneren Schachteln.

    Ich habe jetzt folgendes gebaut:

    Die Strukturen innere und äussere Schachtel sind bekannt. Die Funktion habe ich zu Gunsten der Übersichtlichkeit mal ausgelassen, obwohl sie dem Thread den Namen gegeben haben.

    /*! Speicher für die Seiten-Tabelle belegen                         */
    	if((outer_schachtel = calloc(1, sizeof(schachtel_2))) == NULL)
    	{
    		printf("problem beim Allozieren von Speicher fuer die outer_schachtel\n");
    		exit(-1);
    	}
    

    jetzt kommen wir zur Schleife, hier wird z.B. immer eine Zeile aus einer Datei eingelesen, geparst und Informationen dann in die inneren Schachtel geschrieben.

    while(fgets(pBuffer, ZEILENLEN, fh))
    	{
            i = 0;
            /* Speicher erweitern für das Zeigerarray                */
    			if((outer_schachtel->my_schachtel =  realloc(outer_schachtel->my_schachtel, 1 * sizeof(schachtel_1*))) == NULL)
                {
    				printf("problem beim Belegen von Speicher\n");
    				exit(-1);
    			}
    
    			/* Jetzt Speicher anlegen für den Font Entry      */
    			if((outer_schachtel->my_schatel[i] =  calloc(1, sizeof(schachtel_1))) == NULL)
    			{
    				printf("problem beim Belegen von Speicher\n");
    				exit(-1);
    			}
            }
    

    Nur bricht das ganze immer noch, nicht sofort aber nach einer bestimmten Menge von Durchläufen (zwischen 4 und 5)

    Dann bin ich mir auch nicht ganz sicher was das casten angeht. Ich habe es hier mal rausgenommen, aber in meinem Lieblingswerk zu C: "Darnell/Margolis C a software Engineering Approach" und in vielen anderen Werken die man so im Netz findet, wird fleissig gecastet (z.B. in diesem C von A bis Z aus dem Galileo-Verlag)

    Gruss Christian



  • columbus schrieb:

    In Janjan's Code ist wird nachdem Platz für 5 Zeiger geschaffen wurde diese 5 Plätze noch einmal mit einem Zeiger initialisiert. Aber es wird gar kein Platz geschaffen für die inneren Schachteln.

    Doch, für die 'inneren Schachteln' hat er auch Speicher reserviert. Das kannst du sogar in doppelter Ausführung lesen, nämlich einmal als Quellcode und als Kommentar darüber in Zeile 23.

    columbus schrieb:

    Ich habe jetzt folgendes gebaut:
    ...

    if((outer_schachtel->my_schachtel =
      realloc(outer_schachtel->my_schachtel, 1 * sizeof(schachtel_1*))) == NULL)
    

    ...

    Ich nehme an, du willst das Zeigerarray für jede neue Zeile vergrößern? Dafür musst du aber auch die Anzahl erhöhen, bei dir ist sie konstant.

    columbus schrieb:

    Dann bin ich mir auch nicht ganz sicher was das casten angeht. Ich habe es hier mal rausgenommen, aber in meinem Lieblingswerk zu C: "Darnell/Margolis C a software Engineering Approach" und in vielen anderen Werken die man so im Netz findet, wird fleissig gecastet (z.B. in diesem C von A bis Z aus dem Galileo-Verlag)

    Dazu kannst du dir das hier angucken,
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-206606.html
    da findest du ach, wie man realloc benutzen sollte.

    Gruß,
    B.B.



  • nwp2 schrieb:

    BTW: Ja, in C castet man malloc nicht.

    falsch... in c verwendet man kein malloc 😉



  • malloc? was ist das schrieb:

    nwp2 schrieb:

    BTW: Ja, in C castet man malloc nicht.

    falsch... in c verwendet man kein malloc 😉

    schreib doch nicht son stuss, sonst glaubt das noch jemand.



  • ich wollte damit eigentlich nur sagen, das man sich den einsatz von malloc(), calloc() und realloc() gut überlegen sollte. da diese functionen fehleranfällig und langsam sind. wollts ja selbst auch nicht glauben, mußte aber gestern mal einen kurzen blick in den dlmalloc source code werfen, der mal die implementation von gcc dargestellt hat 😮

    um mal zu zeigen was man nicht machen sollte

    void fn(){
      char *buffer = malloc(25);
      sprintf(buffer,"%d",0xFFFFFF);
      free(buffer);
    }
    

    sondern viel besser schöner und schneller 😉

    void fn(){
      char buffer[25];
      sprintf(buffer,"%d",0xFFFFFF);
      free(buffer);
    }
    

    lg lolo



  • noobLolo schrieb:

    void fn(){
      char buffer[25];
      sprintf(buffer,"%d",0xFFFFFF);
       :warning: free(buffer); :warning: 
    }
    

    "free(buffer);" gehört da natürlich nicht hin. 😞



  • Noch genauer könnte man sich das sprintf sparen weil es eh statisch ist.
    Also char buffer[] = "16777215";
    Noch genauer gesehen tut die ganze Funktion nichts und kann daher komplett weggelassen werden.
    Auch wenn mir das niemand glaubt, buffer[zahl] ist immer ganz schlecht.



  • nwp2 schrieb:

    Noch genauer könnte man sich das sprintf sparen weil es eh statisch ist.
    Also char buffer[] = "16777215";
    Noch genauer gesehen tut die ganze Funktion nichts und kann daher komplett weggelassen werden.
    Auch wenn mir das niemand glaubt, buffer[zahl] ist immer ganz schlecht.

    Mitdenken ist nicht deine Stärke, oder?



  • nwp2 schrieb:

    Auch wenn mir das niemand glaubt, buffer[zahl] ist immer ganz schlecht.

    Wenn "zahl" eine Variable ist, so daß Du ein VLA bekommst, riskierst Du einen potentiellen Stack-Überlauf. Ist "zahl" jedoch eine Konstante, gibt es nichts, was daran schlecht wäre.



  • Mit Zahl meine ich eine Konstante.
    Das Problem daran ist, dass man die Zahl nicht korrekt festlegen kann.
    Dieses übliche

    struct person{
        char name[80];
        int alter;
    };
    

    ist einfach falsch. Der Name von Personen ist nicht immer 79 Zeichen lang, somit ist char name[80] Blödsinn.

    Auch der Verweis dass malloc viel CPU frisst ist Quatsch, ich habe es noch nie erlebt, dass ein Programm hängt weil es nicht genug CPU-Power hat. Programme hängen nur ständig weil die Festplatte nicht nachkommt, CPU ist im Überfluss vorhanden seit es GHz-Rechner gibt (ja, ich weiß, Mikrocontroller).

    Ich weiß, diese Diskussion hatten wir schon. Ihr findet char buffer[12345] ganz toll und ich nicht. Niemand wird von seiner Position abweichen, deswegen braucht man darüber auch nicht zu reden.



  • nwp2 schrieb:

    Auch der Verweis dass malloc viel CPU frisst ist Quatsch

    Es ist kein Quatsch, sondern eine Tatsache. Bei malloc() muss beim Kernel mühsam nach Speicher gefragt werden, der Kernel muss sich dann erst darum kümmern. Arrays jedoch liegen auf dem Stack, dort muss einfach nur der Stackpointer verschoben werden -> SEHR viel schneller.



  • Janjan schrieb:

    SEHR viel schneller.

    Dauert statt einer Millisekunde 10. Das ist 1000% schneller. Trotzdem merkts keiner.
    Wenn man hingegen den buffer[12345]-Stil benutzt ist man immer großzügig mit dem Speicher und kommt ins Swappen, und das ist langsam.
    Mal ganz von Abstürzen und blödsinnigen Grenzen der Software abgesehen, weil die Buffergröße doch zu klein gewählt wurde.

    Edit: Der Punkt ist, dass man hier Korrektheit des Programms gegen ein paar CPU-Zyklen tauscht. Meiner Meinung nach ein ganz schlechter Tausch.



  • nwp2 schrieb:

    Der Punkt ist, dass man hier Korrektheit des Programms gegen ein paar CPU-Zyklen tauscht. Meiner Meinung nach ein ganz schlechter Tausch.

    Nein, der Punkt ist, dass man Dinge einfach halten will. Für jedes malloc() muss man auch einmal free() aufrufen. Das spart man sich mit Arrays.


Anmelden zum Antworten