dynamische Allokierung, malloc, calloc



  • Mal abgesehen von pseudodynamischen VLA, welche eh nur den Stack belasten und ohne Fehlerbehandlung auskommen müssen, ist die dynamische Speicherbelegung in C ein sehr wesentliches Kriterium, ohne das keinerlei professioneller Code auskommt.
    Dynamische Speicherbelegung ist gekennzeichnet durch die Funktionen malloc,calloc,realloc und free welche immer einen Zeiger zurückliefern (und kein Array, wie bei deinem Eingangsstatement).

    Beispiel statischer Speicher:

    #define GROESSEINBYTES 100
    char array[GROESSEINBYTES];
    strcpy(array,"blafasel");
    puts(array);
    

    Beispiel dynamischer Speicher:

    #define GROESSEINBYTES 100
    char *zeiger;
    zeiger=malloc(GROESSEINBYTES);
    strcpy(zeiger,"blafasel");
    puts(zeiger);
    free(zeiger);
    

    Wie du siehst, ist dynamische Speicherbelegung aufwändiger und wahrscheinlich meinte dein Prof. deshalb, du sollst es erstmal mit statischer Speicherbelegung sprich Arrays versuchen.
    Und um der Frage gleich vorzubeugen: NEIN, Array und Zeiger sind NICHT dasgleiche und schon gar nicht dasselbe, nur snippetgläubige Laien behaupten dieses.



  • Wutz schrieb:

    ist die dynamische Speicherbelegung in C ein sehr wesentliches Kriterium, ohne das keinerlei professioneller Code auskommt.

    Janeisklar...



  • Hallo,

    danke für deine Antwort, Wutz. Naja.. mein Prof. sagt uns nämlich immer, dass es "im Prinzip" keinen Unterschied zwischen Zeiger und Felder gibt. Naja, wie auch immer..

    Dynamische Allokierung mit Strings haben wir bisher auch nie gemacht. Mein Prof. ist eher einer, der Mathefunktionen und -aufgaben mit C verbindet. Ich habe mittlerweile doch eine Homepage gefunden, wo Sachen wie dynamische Allokierung und so erklärt wird(http://r4r.co.in).

    Von den Skripten ausgehend habe ich mal folgendes versucht und habe auch folgende Frage:

    Würde ich mir mit dem Code

    main(){
      int size = 8;
      int a;
    
      a = (int)malloc(size * sizeof(int));
    }
    

    welches als Ergebnis 146903048 liefert, "146903048" Bytes freimachen?

    Wenn ja, dann versteh ich es so allmählich glaub ich 🙂

    Liebe Grüße,

    Student.



  • Student123 schrieb:

    welches als Ergebnis 146903048 liefert, "146903048" Bytes freimachen?

    Zonk!



  • Student123 schrieb:

    Würde ich mir mit dem Code

    main(){
      int size = 8;
      int a;
      
      a = (int)malloc(size * sizeof(int));
    }
    

    welches als Ergebnis 146903048 liefert, "146903048" Bytes freimachen?

    Wenn ja, dann versteh ich es so allmählich glaub ich 🙂

    Nein. Das macht so keinen Sinn. Du sagst malloc() wieviel Speicher du reservieren willst, in deinem Fall 8 mal die Größe eines int, also Platz für 8 int hintereinander im Speicher. Der Rest ist schlicht falsch. malloc gibt dir einen Zeiger auf den reservierten Speicherbereich zurück, also eine Adresse, und im Fehlerfall (z.B. nicht ausreichend Speicher frei) den NULL-Zeiger. Korrekt wäre dein Beispiel so:

    int main(void){
      int size = 8;
      int *a;
    
      a = malloc(size * sizeof(int));
      if (a) {
        /* mache was mit a */
    
        /* Speicher wieder freigeben */
        free(a);
      }
      else {
        printf ("Fehler");
      }
    }
    


  • Die Aussage deines Prof. beruht darauf, das in den Beispielen von Wutz die Funktionen strcpy und puts nicht mehr unterscheiden können ob die Daten zu einem array oder einem dynamischen Speicher gehören.

    Die Funktionen in denen array bzw. zeiger deklariert sind können das.

    Man kann Zeigern die Adresse eines Arrays zuweisen, aber nicht umgekehrt. ⚠

    zeiger = array;  // geht. zeiger Zeigt auf da erste Element von array
    array = zeiger;  // geht nicht.
    

    Beachte: array ist nicht das gleiche wie array[0].
    Zudem braucht zeiger selber auch Speicherplatz.

    PS: Die 146903048 ist die Adresse ab der dein geforderte Speicher liegt.
    mit free(a) sagst du dann: den Speicher ab Adresse 146903048 freimachen.
    Wieviel SPeicher das ist erfährt free aus internen Daten von malloc.



  • Also habe ich mit meinem Code

    main(){
      int size = 8;
      int a;
    
      a = (int)malloc(size * sizeof(int));
    }
    

    ab der Adresse "146903048" 8x4=32 Bytes reserviert?



  • Genau.



  • Student123 schrieb:

    Würde ich mir mit dem Code

    main(){
      int size = 8;
      int a;
      
      a = (int)malloc(size * sizeof(int));
    }
    

    welches als Ergebnis 146903048 liefert, "146903048" Bytes freimachen?

    Komplett falsch.
    malloc liefert einen Zeiger zurück, du solltest den obigen Text nicht nur lesen sondern auch umsetzen, du hast nämlich die Adresse des von malloc gelieferten belegten Speicherbereiches einem int zugewiesen, das ist natürlich Unsinn.



  • Ok, alles klar.

    Jetzt stehen noch einige Fragen aus. Wenn ich mit malloc arbeite wie im ebigen Beispiel, muss ich es in einem Feld, Pointer oder in einer Variable abspeichern?

    Wutz hat eben nämlich a als Pointer deklariert aber als Variable genutzt.

    Wenn ich mit einem Feld arbeiten muss und angenommen ich will das Feld i mit i * i füllen, wie müsste ich da vorgehen?



  • Vom Prinzip hast du das verstanden, aber dein Code hat noch Fehler.
    Zeiger werden in C mit dem * davor deklariert.

    main(){
      int size = 8;
      int *a; // a ist ein Zeiger, *a ist ein int
    
      a = (int *)malloc(size * sizeof(int));
    
      *a      = 4;  // in das erste  Element von a wird eine 4 geschrieben.
      *(a+1)  = 5;  // in das zweite Element von a wird eine 5 geschrieben.
    //  :warning:  Der Compiler weiß wie groß ein int ist und zählt schon richtig weiter  (hier +(1*4))               
    }
    

    * ist der Dereferenzierungs-Operator



  • #define ANZAHLELEMENTE 8
    int i,*zeiger = malloc(ANZAHLELEMENTE*sizeof(int));
    for( i=0;i<ANZAHLELEMENTE;++i )
      zeiger[i] = i*i;
    for( i=0;i<ANZAHLELEMENTE;++i )
      printf("\n%d",zeiger[i]);
    free(zeiger);
    


  • aber benutzt du beim deklarieren den Pointer a(also 😉 und weiter unten beim Code wo das mit malloc gemacht wird das einfache a?



  • Nur zur Klarstellung:
    Das geht beides.

    zeiger[i] ist gleichbedeutend mit *(zeiger+i)

    Was wieder Verwirrung über den Unterschied zwischen Zeigern und Arrays stiften wird. 🙂



  • hmm.. ok.

    main(){
                  int size = 8;
                  int *ptr;
                  int i;
                  int grenze;
    
                  *ptr = (int)malloc(size * sizeof(int));
                  grenze = size * sizeof(int);
    
                   for(i = 0; i<grenze; i++){
                     ptr[i] = i * i;
                   }
    
    }
    

    so würde ich size * sizeof(int) Felder mit i * i füllen?



  • Beim deklarieren sagst du *int a; a ist ein Zeiger auf ein int. Oder zum merken: *a ist ein int.

    malloc liefert dir einen Zeiger (auf void) und den musst du dann zuweisen: a = malloc(..);

    int *a,
    
    a = malloc(...);
    
    *a = 5;
    printf("Der Inhalt von %p ist %d\n", a, *a);
    
    free(a);
    

    ----------------------
    Gerade noch gesehen:
    Nein eben nicht:

    main(){
                  int size = 8;
                  int *ptr;
                  int i;
                  int grenze;
    
    // FALSCH     *ptr = (int)malloc(size * sizeof(int));   // *ptr ist ein int
                  ptr = (int *)malloc(size * sizeof(int));  // ptr ist der Zeiger auf int
    // FALSCH     grenze = size * sizeof(int);
                  grenze = size ;  // Der Compiler weiß wie groß ein int ist!
                                   // Du hast nur 8 int, keine 32
    
                   for(i = 0; i<grenze; i++){
                     ptr[i] = i * i;  // Beim Zugriff achtet der Compiler auf die richtigen Offsets
                   }
    

    }



  • Hm, ok alles klar. Ich nehme das nun einfach mal so hin. Ich muss eine Aufgabe mit malloc lösen, die wie folgt beschrieben ist:

    Erzeugen Sie in einem Programm zu einer Folge von Zahlen, die vom Benutzer
    einzugeben sind, das Zahlendreieck, in dem sich ab der zweiten Zeile jede Zahl als
    Summe der darüberliegenden Zahl und deren rechter Nachbarzahl ergibt.
    Die Anzahl der zu behandelnden Zahlen ist vom Benutzer einzugeben, dann ist ein
    Feld geeigneter Größe dynamisch (mit malloc oder calloc) zu erzeugen.

    Das war jene Aufgabe, wo der Prof von mir wollte dass ich es erst statisch mache. Die dynamische Version gibts dann am Montag.



  • Hey,

    machen wir doch einmal gleich da weiter, wo wir aufgehört haben. Unzwar versuche ich grade das obrige Problem zu lösen.

    Ich habe folgenden Code geschrieben:

    //Deklaration
          int size = 0;
          int *zahlen;
          int i;
    
          //Eingabe der Größe
          printf("Wieviele Zahlen?\n");
          scanf("%d",&size);
    
          //Reservierung der Größe
          zahlen = (int *)malloc(size * sizeof(int));
    
          //Eingabe der Zahlen
          for(i = 0; i<size;i++){
    	      printf("Bitte geben Sie %d Zahlen ein:\n",size);
    	      scanf("%p",zahlen[i]);
          }
    

    Aber ich bekomme nach Eingabe der ersten Zahl ein "Segmentation fault".

    Wieso?



  • 1. musst du an scanf Adressen (Zeiger) übergeben

    2. Was willst du denn mit "%p" bei scanf erreichen?
    Probier mal %i oder %d.

    scanf("%i",&zahlen[i]);
    //oder 
    scanf("%i",zahlen+i);
    


  • 3. bei malloc immer auf Erfolg (!= NULL) testen ⚠


Anmelden zum Antworten