2016-05-26 5 views
5

Ich bin vor allem an der Lebensfähigkeit von Schrumpf so ein Array interessiert.Verwenden Sie realloc() auf einem dynamisch zugewiesenen 2D-Array eine gute Idee?

Ich arbeite an einem Projekt, bei dem ich einzelne malloc() - Aufrufe verwendet habe, um einzelne mittelgroße 2D-Arrays zu erstellen. (Jeweils nur einige Dutzend MiB, höchstens.) Die Sache ist, dass über die Lebensdauer eines der Arrays sein Inhalt dramatisch in der Größe schrumpft (um mehr als die Hälfte). Offensichtlich könnte ich die Array-Größe einfach für die gesamte Lebensdauer des Programms belassen. (Es ist nur eine x MiB auf einem System mit GiB RAM verfügbar.) Aber wir sprechen davon, dass mehr als die Hälfte des zugewiesenen Speicherplatzes lange bevor das Programm beendet wird, und aufgrund der Art, wie ich bin, außer Gebrauch kommen Mit dem Array werden alle überlebenden Daten in einer zusammenhängenden Reihe von Zeilen (am Anfang des Blocks) gehalten. Es scheint eine Verschwendung zu sein, an all dem RAM festzuhalten, wenn ich es wirklich nicht brauche.

Während ich weiß, dass realloc() verwendet werden kann, um dynamisch erstellte Arrays zu verkleinern, ist ein 2D-Array komplexer. Ich denke, ich verstehe das Speicherlayout davon (da ich die Funktion implementiert habe, die es strukturiert), aber dies treibt die Grenzen meines Verständnisses der Sprache und der Funktionsweise ihrer Compiler. Offensichtlich müsste ich mit Zeilen arbeiten (und mit den Zeilenzeigern umgehen), nicht nur Bytes, aber ich weiß nicht, wie vorhersehbar das Ergebnis von all dem sein würde.

Und ja, ich muss das Array mit einem einzigen malloc() erstellen. Das Objekt hat mehrere Millionen Zeilen. Ich habe versucht, eine Schleife zu malloc() jeder Zeile getrennt, aber das Programm immer um rund 100.000 malloc() s eingefroren.

für den Hintergrund, die Quelle ich diese Anordnung zu konstruieren bin ist wie folgt:

char ** alloc_2d_arr(int cnum, int rnum) { 
     /* ((bytes for row pointers + (bytes for data)) */ 
     char **mtx = malloc(rnum * sizeof (char *) + rnum * cnum * sizeof (char)); 

     /* Initialize each row pointer to the first cell of its row */ 
     char *p = (char *) (mtx + rnum); 
     for (int i = 0; i < rnum; i++) { 
       mtx[i] = p + i * cnum; 
     } 

     return mtx; 
} 
+2

'realloc' ist keine gute Idee für so große Tische, schauen Sie sich" avl "und" rot-schwarz "Bäume an. –

+1

"Es scheint eine Verschwendung zu sein, an all dem RAM festzuhalten, wenn ich es wirklich nicht brauche." - Erstes * Profil *.Zweitens, ein Realloc läuft das hohe Risiko des Auslösens Kopieren all Ihrer Still-Interior-Daten auf verschiedene Seiten, die nicht trivialen Kosten, die Sie allein aus dem Grund zu versuchen, RAM zu speichern Sie selbst Anspruch ist nicht wirklich ein Problem. Das einzige Win-Szenario ist "Realloc", bei dem der Regionskopf als Speicherbasis beibehalten wird und die Endseiten für andere Zwecke zurückgegeben werden. etwas "Realloc" hat keine Garantien über ... – WhozCraig

+0

... so haben Sie stattdessen nur 2 (oder 3 oder 4, was auch immer) Allokationen, denken Sie daran, welche Sie schließlich behalten, und 'free()' - diejenigen, die Sie nicht mehr brauchen, sobald dieses Ereignis eintritt? I.e. die "behalten" Hälfte Ihrer Matrix ist in der ersten Zuteilung, die zweite Hälfte ist in einer anderen Zuteilung, und schließlich befreien Sie einfach die zweite Hälfte. – WhozCraig

Antwort

2

multidimensionalen Arrays verwenden, kann dies mit oder ohne Zeiger auf variabler Länge Arrays durchgeführt werden. Da Sie wahrscheinlich keinen zusätzlichen Speicher reservieren möchten, wird dies an Ort und Stelle geschehen.

Zunächst wird eine 20 um 10 Array zuteilen:

int (*array)[10] = malloc(sizeof(int) * 20 * 10); 
for(size_t i = 0 ; i < 20 ; i++) 
    for(size_t j = 0 ; j < 10 ; j++) 
      array[i][j] = i * 100 + j; 

Wenn Sie die Anzahl der Zeilen ändern möchten, keine Elemente bewegt werden müssen, nur ein realloc benötigt wird. Ändern der Zeilenanzahl auf 15 ist trivial:

array = realloc(array , sizeof(int) * 15 * 10); 

Wenn Sie die Anzahl der Spalten ändern möchten, dann müssen die Elemente verschoben werden. Da wir die erste Spalte nicht kopieren müssen, beginnt der Kopiervorgang mit dem zweiten. Die Funktion memmove wird verwendet, um Speicherüberlappungen zu vermeiden, die in diesem Fall nicht auftreten können, aber dies könnte der Fall sein, wenn die neue Spaltenanzahl größer ist. Außerdem vermeidet es Alias-Probleme. Beachten Sie, dass dieser Code nur definiert wird, weil wir reservierten Speicher verwenden. Lassen Sie sich die Säule bis 3 zählen ändern:

int (*newarray)[3] = (int(*)[3])array; 
for(size_t j = 1 ; j < 15 ; j++) 
    memmove(newarray[j] , array[j] , sizeof(int) * 3); 
newarray = realloc(array , sizeof(int) * 15 * 3); 

Arbeitsbeispiel: https://ideone.com/JMdJO0

Wenn die neue Spaltenanzahl passiert sein, größer als die alten, dann wird der Speicher hat ersten umverteilt werden (einfach zu erhalten mehr Platz), und dann wird die Spaltenkopie statt in der letzten Spalte ausgeführt.

+0

Es beschämt mich, es zuzugeben, aber ich habe Probleme beim Verstehen von 'int (* array) [10] = malloc (...);'. Basierend auf meinem anscheinend mangelhaften Verständnis von C, sieht das so aus, als würde man eine neu erzeugte Variable mit einem dereferenzierten Zeiger als ihren Identifizierer initialisieren. Es wäre eine Sache, einen Triple-Pointer (zweimal) zu dereferenzieren und die Ausgabe malloc() zu geben, aber wenn man einen Typ davor stellt, sieht es so aus, als ob eine RAM-Adresse als Symbol verwendet wird (was keinen Sinn ergibt) . Ich habe das Gefühl, dass ich sah, was auch immer das ist, wenn ich C lernte, aber das Googeln der relevanten Begriffe ergibt offensichtlich verschmutzte Ergebnisse. – CircleSquared

+0

@CircleSquared Es ist ein Zeiger auf ein mehrdimensionales Array. Zwei Dimensionen in meinem Beispiel. So: 'int a [7] [9]; int (* pa) [9] = a; ', außer in meinem Beispiel zeige ich diesen Zeiger nicht auf ein automatisches Array, sondern auf zugewiesenen Speicher. – 2501

+0

Vielen Dank, dass Sie mir eine effizientere Möglichkeit gezeigt haben, ein mehrdimensionales Array dynamisch zuzuweisen ** zusätzlich **, um einfach meine Frage zu beantworten. * So, es ist sicher genug, aber das ist eine bessere Möglichkeit, das Array einzurichten. * – CircleSquared