2016-11-16 2 views
3

Ich kämpfe um die C-Regeln von malloc()/free() zu lernen. Betrachten Sie den folgenden Code, der gut läuft. (Unter Linux, kompiliert mit GCC.) Ich frage mich speziell über das Array arr. Ich, dass Sie für alle Struktur-Elemente innerhalb das Array malloc haben ... aber, warum Sie nicht für das Arrayselbst malloc haben?Muss ich malloc() dieses Array nicht?

#include<stdio.h> 
#include<stdlib.h> 
#define ARR_SIZE 100 

typedef struct containerStruct{ 
    int data1; 
    int data2; 
    int data3; 
    // ...etc... 
} container; 

int main(){ 

    container* arr[ARR_SIZE];  // Don't I have to malloc this? 

    int i; 
    for(i=0; i<ARR_SIZE; i++){ 
      arr[i] = (container*) malloc (sizeof(container)); // I get why I have to malloc() here 
    } 

    ...do stuff... 

    for(i=0; i<ARR_SIZE; i++){ 
      free(arr[i]);  // I get why I have to free() here 
    } 

    // Don't I have to free(arr) ? 
    return 0; 
} 

Ich vermute, dass die container* arr[ARR_SIZE]; Linie die Compiler alle erzählt es wissen muss für das Array den Platz im Speicher zu schnitzen, weshalb funktioniert dieser Code.

Aber irgendwie fühlt es sich immer noch nicht richtig an. Wenn ich etwas malloc(), reserviere ich Speicherplatz auf dem Heap, richtig? Aber mein container* arr[ARR_SIZE]; Aufruf erstellt das Array auf dem Stapel. Also ... das Array von Container * -Portern existiert auf dem Stapel, und all diese Container * -Zeiger verweisen auf eine Container-Struktur auf dem Heap. Ist das korrekt?

+1

Für eine echte Überraschung, versuchen Sie 'container arr [ARR_SIZE];' ohne den Stern, und sehen Sie, dass Sie nicht 'malloc' brauchen, um ein Array zu erhalten. –

+1

Sie deklarieren eine Reihe von Zeigern auf eine Weise, die 'malloc()' nicht erfordert. Sie müssen möglicherweise zu malloc() 'was sie zeigen; oder Sie könnten nicht, je nachdem, wie/ob Sie dclare das – Mawg

Antwort

2

Da ARR_SIZE festes Mitglied des dynamischen Arrays *arr[ARR_SIZE] ist, müssen Sie nicht mehr Speicher für das gesamte Array zuweisen, sondern nur die darin enthaltenen Elemente, da sie in diesem Fall flexibel sind.

Wenn Sie malloc jede Art von dynamischer Arrays, es ist sicher, den Rückgabewert des void* Zeigers von malloc zurückgegeben immer zu überprüfen. Wenn Sie jedes Element verwenden, ist es sogar noch sicherer, jedes Element erneut auf NULL festzulegen, um zu verhindern, dass der Zeiger erneut auf den Speicher zugreift.

Illustrated durch diesen Code:

#include <stdio.h> 
#include <stdlib.h> 

#define ARR_SIZE 100 

typedef struct { 
    int data1; 
    int data2; 
    int data3; 
    // ...etc... 
} container_t; 

int 
main(void) { 
    container_t *arr[ARR_SIZE]; 
    int i; 

    for (i = 0; i < ARR_SIZE; i++) { 
     arr[i] = malloc(sizeof(container_t)); 
     if (arr[i] == NULL) { 
      printf("Malloc Problem here\n"); 
      exit(EXIT_FAILURE); 
     } 
    } 

    for (i = 0; i < ARR_SIZE; i++) { 
     if (arr[i]) { 
      free(arr[i]); 
      arr[i] = NULL; 
     } 
    } 

    return 0; 
} 
+1

Zwei Zeilen mit 'arr [i] = malloc (sizeof (container); if (NULL == arr [i]) ...'funktioniert gut, benötigt keine 4 Klammerebenen und ist damit * viel, viel * weniger fehleranfällig. Wenn Sie eine Zuweisung in eine Bedingungsklausel füllen, wird eine Zeile für ein Lehrbuch gespart, was sich auf Kosten der Wartung und der Wahrscheinlichkeit, dass Fehler auftreten, erschweren würde. –

+0

@AndrewHenle Prost. Ich habe es modifiziert. – RoadRunner

2

container* arr[ARR_SIZE]; sagt dem Compiler ein Array von ARR_SIZEElemente vom Typcontainer* und Compiler ordnet den Speicher entsprechend zuzuordnen.

Mit anderen Worten, das ist ähnlich zu sagen int x[5] = 0; wo Compiler genug Platz für ein Array von 5 int s reserviert. In Ihrem Fall reserviert der Compiler genug Platz für ARR_SIZE Anzahl der Zeiger, container* und das ist es. Nun ist es an Ihnen, diese Zeiger zeigen auf gültigen Speicherort. Dafür können Sie entweder

  • Verwendung Speicherzuordner Funktionen (die Speicher von Heap zuordnet, wie Sie bereits erwähnt)
  • weisen Sie die Adresse anderer Variablen des gleichen Typs (nicht Zuteilung von Heap benötigen, sowieso).

Also unter dem Strich, Sie brauchen keine Speicher für das Array zuzuordnen. Für jede einzelne Array-Elemente müssen Sie Speicher mit Speicherzuordnungsfunktionen zuweisen, wie Sie möchten, dass jedes Element auf gültige Speicher verweist.

+0

@SeanBright Im Allgemeinen ist das richtig, aber nicht sehr relevant für _this_ case, richtig? –

+0

OP fragt, warum er das Array nicht mit malloc zuordnen muss. Ich denke, es ist vollkommen relevant. (Ich habe meinen vorherigen Kommentar gelöscht, weil ich bemerkte, dass er in seiner Frage bereits Stack/Heap erwähnt hat) –

+0

@SeanBright Ich verstehe immer noch nicht, warum das relevant ist. eine Operation wie 'arr [i] = & arr2 [i];' würde den Haufen komplett aus dem Zusammenhang nehmen, richtig? –

4

rechts unter Ihrer Erklärung arr, haben Sie die folgenden Schritte aus:

int i; 

Welche genügend Platz (wahrscheinlich auf dem "Stack") speichern eine int behält. Fühlt sich das auch für Sie falsch an?

Die Deklaration von arr ist nicht anders. Der Compiler reserviert genug Platz (wahrscheinlich auf dem "Stapel") für ein ARR_SIZE Element Array von container * s.

+2

Bemerkenswert, dass so etwas wie * stack * ein Compiler-Implementierungsdetail ist, das nicht vom C-Standard diktiert wird. –

+1

Ich habe bearbeitet. Ich hoffe es ist ausreichend. –

3

Sie können entweder eine der folgenden Optionen wählen:


int i; 
container** arr = malloc(sizeof(container*)*ARR_SIZE); 
for (i=0; i<ARR_SIZE; i++) 
    arr[i] = malloc(sizeof(container)); 
// do stuff... 
for(i=0; i<ARR_SIZE; i++) 
    free(arr[i]); 
free(arr); 

int i; 
container* arr[ARR_SIZE]; 
for (i=0; i<ARR_SIZE; i++) 
    arr[i] = malloc(sizeof(container)); 
// do stuff... 
for(i=0; i<ARR_SIZE; i++) 
     free(arr[i]); 

container arr[ARR_SIZE]; 
// do stuff... 

Da ARR_SIZE konstant ist, man kann auch c wähle die letzte Option.

3

Als ich malloc() etwas, ich bin auf dem Heap Speicherplatz reserviert, nicht wahr?

Sagen Sie das nicht, der Heap ist ein Implementierungsdetail. Sie sind Speicher dynamisch zugewiesen. Sie sind auch für die dynamische Freigabe verantwortlich.

Aber mein Container * arr [ARR_SIZE]; Anruf

Es ist kein Anruf, es ist eine Erklärung.

erstellt das Array auf dem Stapel.

Sagen Sie das auch nicht, der Stack ist auch ein Implementierungsdetail.

Sie deklarieren ein lokales (hier ein Array von Zeigern) mit automatischem Gültigkeitsbereich, und der Compiler ist verantwortlich für die Verwaltung seines Speichers und seiner Lebensdauer.

Es ist lebenslang nicht dynamisch, weil es unerreichbar wird, sobald Sie die } am Ende des umschließenden Blocks erreichen, und so kann der Compiler es für Sie deterministisch behandeln.

alle lokalen Variablen verhalten sich hier das gleiche:

{ 
    int i; 
    double d[2]; 
} /* neither i nor d are reachable after here, 
    so the compiler takes care of releasing any storage */ 

So ...das Array der Container * -Zeiger existiert auf dem Stapel,

Betrachten Sie die einfachere Deklaration container c;. Dies ist ein Local mit automatischem Gültigkeitsbereich, und der Compiler kümmert sich wie erwähnt um die (De-) Zuweisung.

Jetzt betrachten container *p;. Dies ist auch eine lokale Variable mit automatischem Bereich, aber die Variable ist ein Zeiger. Sie müssen es immer noch bei etwas manuell, und wenn die Sache, auf die Sie zeigen, wurde von malloc zurückgegeben, müssen Sie free es selbst.

Betrachten Sie weiter ein einfaches Array container a[2];. Jetzt haben Sie ein lokales Array mit automatischem Bereich, der zwei Instanzen Ihres container Typs enthält. Beide Instanzen haben dieselbe Lebensdauer, sie werden immer noch vom Compiler verwaltet. Sie können auf a[0] und a[1] zugreifen und so ziemlich alles andere ist illegal.

und alle diese Container * -Zeiger verweisen auf eine Container-Struktur auf dem Heap. Ist das korrekt?

Nein. Schließlich betrachten container* ap[2]. Wieder haben wir ein lokales Array mit automatischem Geltungsbereich, das zwei Zeiger enthält. Der Typ des Zeigers ist Pointer-to-Container, und die Lebensdauer der Zeiger wird vom Compiler verwaltet. Allerdings sind die Zeiger nicht Punkt bei noch nichts, und es wird Ihre Verantwortung zu entscheiden, was Sie darauf hinweisen, und herausfinden, welche Lebensdauer wies auf Sache hat.

Sobald Sie das tun

ap[i] = malloc(sizeof(*ap[i])); 

(Sie nicht werfen müssen, und es ist im Allgemeinen sicherer zu vermeiden, dass die Art explizit zu benennen, falls Sie es später ändern und die falsche Größe zuzuteilen), die Sie haben hat ein Objekt zugewiesen, für das Sie verantwortlich sind. Sie haben auch auf einen der Zeiger in Ihrem Array auf dieses Objekt hingewiesen, aber das ändert nicht irgendwie die Lebensdauer des Zeigers selbst. Das Array verlässt nur wie üblich wo auch immer seine Zeiger zeigen.