2013-05-23 14 views
8

Mam pytanie dotyczące przydzielania pamięci, gdy calloc. Spojrzałem na pytanie this, ale nie opisuje ono sposobu przydzielania pamięci w przypadku dynamicznie przydzielanej tablicy dwuwymiarowej.Tablice wielowymiarowe przydzielone przez calloc

Zastanawiam się, czy istnieje różnica w reprezentacji pamięci między następującymi trzema sposobami dynamicznego przydzielania tablicy 2D.

Typ 1:

double **array1; 
int ii; 

array1 = calloc(10, sizeof(double *)); 
for(ii = 0; ii < 10; ii++) { 
    array1[ii] = calloc(10, sizeof(double)); 
} 
// Then access array elements like array1[ii][jj] 

Typ 2:

double **array1; 
int ii; 

array1 = calloc(10 * 10, sizeof(double *)); 
// Then access array elements like array1[ii + 10*jj] 

Typ 3:

double **array1; 
int ii; 

array1 = malloc(10 * 10, sizeof(double *)); 
// Then access array elements like array1[ii + 10*jj] 

Z tego co rozumiem, z calloc i malloc, różnica między ostatnimi dwoma polega na tym, że calloc wyzeruje wszystkie elementy tablicy, podczas gdy malloc nie będzie. Ale czy są to pierwsze dwa sposoby definiowania odpowiednika tablicy w pamięci?

+0

typ 2 oraz typ 3 są w zasadzie to samo, z wyjątkiem pamięci ustawionej na 0 za pomocą 'calloc'. Nie wiem jednak, czy jest jakaś różnica w typie 1. – JBL

+0

@JBL: tak naprawdę to tylko dodatkowa przestrzeń dla wskaźników i fakt, że bloki 10 podwójnych mogą się okazać niekonkretne. – Dancrumb

+0

@Dancrumb Och, rzeczywiście, nie myślałem o tym. Słuszna uwaga. – JBL

Odpowiedz

2

są dwie pierwsze sposób definiowania równoważnik tablicy w pamięci?

Niezupełnie. W drugim typie są prawie na pewno ciągłe, podczas gdy w pierwszym typie nie jest to pewne.

Typ 1: w pamięci reprezentacja będzie wyglądać następująco:

  +---+---+---+---+---+---+---+---+---+---+ 
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 
      +---+---+---+---+---+---+---+---+---+---+ 
      ^
      |------------------------------------          
       . . . . . . . . | // ten rows of doubles 
               - 
      +---+---+---+---+---+---+---+---+---+--|+ 
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0|| 
      +---+---+---+---+---+---+---+---+---+--|+ 
      ^ . . .      - 
      | ^^^ . . . . . | 
      | | | | ^^^^^ | 
      +-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+ 
array1[ii]| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | // each cell points to ten doubles 
      +---+---+---+---+---+---+---+---+---+---+ 
      ^
      | 
      | 
      +-|-+ 
    array1| | | 
      +---+ 

typu 2 w pamięci reprezentacja będzie wyglądać następująco:

  +---+---+---+---+---+---+---+---+---+---+  +---+ 
    double| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 
      +---+---+---+---+---+---+---+---+---+---+  +---+ 
      ^^^^^^^^^^  ^
      | | | | | | | | | |   | 
      | | | | | | | | | |   | 
      +-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+-|-+  +-|-+ 
array1[ii]| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... |99 | // each cell points to one double 
      +---+---+---+---+---+---+---+---+---+---+  +---+ 
      ^
      | 
      | 
      +-|-+ 
    array1| | | 
      +---+ 
-1

Po pierwsze, przydziela się 10 wskaźników do podwojenia i 100 podwójnych. W drugim sposobie przydzielamy 100 wskaźników do podwojenia. Inna różnica polega na tym, że w drugim przypadku przydzielamy jeden duży blok pamięci, tak aby wszystkie elementy tablicy były w tym samym bloku. Po pierwsze, każdy "wiersz" twojej tablicy znajduje się w innym bloku niż pozostałe. Po drugie, twoja tablica powinna być podwójna * zamiast podwójna **, ponieważ w ten sposób alokacji twoja tablica zawiera tylko wskaźniki do podwojenia, a nie podwójne.

+0

Pierwsze dwa sposoby nie wykorzystują tej samej ilości pamięci. Pierwsza metoda wykorzystuje dziesięciokrotność naturalnej wielkości wskaźnika. – Dancrumb

+0

Pierwsza metoda: 10 wskaźników, 100 podwójnych, druga metoda: 100 wskaźników, brak podwójnych, tak nie ta sama kwota moja zła. Ale druga metoda nadal nie ma podwójnego przyznania. –

+0

Edytowane w celu korekty. –

-1

W przypadku 1, można dokonać:

array1[0] -> [memory area of 10] 
array1[1] -> [memory area of 10] ... 
array1[N] -> [memory area of 10] ... 

Uwaga: Nie można zakładać, że obszar pamięci jest ciągły, nie może być luk.

W przypadku 2 zrobić:

array1 -> [memory area of 100] 

Przypadek 3 jest taki sam jak w przypadku 2, ale nie jest inicjowanie pamięci. Różnica między przypadkiem 1 a 2 & 3 polega na tym, że w pierwszym przypadku masz naprawdę strukturę pamięci 2D. Na przykład, jeśli chcesz zamienić wiersze 1 i 2, może po prostu zamienić wskaźniki:

help  = array1[1] 
array1[1] = array1[2] 
array1[2] = help 

Ale jeśli chcesz zrobić to samo w przypadku 2 & 3 trzeba zrobić prawdziwe memcpy. Co używać? Zależy od tego, co robisz.

Pierwszy sposób wykorzystuje nieco więcej pamięci: gdybyś miał tablicę 1000x10, to pierwsza wersja użyje 1000 * 8 + 1000 * 10 * 8 (w systemie 64-bitowym), podczas gdy 2 & 3 użyje tylko 1000 * 10 * 8.

+0

Przypadki 2 i 3 używają mniejszej lub równej pamięci niż pierwsza. – alk

+0

Masz całkowitą rację. To było to, co próbowałem wyjaśnić w ostatnim paragrafie liczbami, ale miałem "literówkę" mówiącą "sekundę", jak to miało być "pierwsze". Edytowałem post tego. – susundberg

+0

Czy wiesz, że przypadki 2 i 3 nie przydzielają żadnych podwojeń, ale tylko wskaźniki do nich, prawda? – alk

1

Prosty przykład

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

int **d ; 
int sum(); 

//-----------------------------------------------    
int main(){ 

    d = (int **)calloc(3,sizeof(int)); 
    printf("\n%d",sum()); 

} 

//----------------------------------------------- 
int sum(){ 
int s = 0; 
for(int i = 0; i < 3; i++) 
    d[i] = (int *)calloc(3,sizeof(int)); 

for(int i = 0; i < 3; i++){ 
    for(int j = 0; j < 3; j++){ 
     d[i][j] = i+j; 
     s += d[i][j]; 
     printf("\n array[%d][%d]-> %d",i,j,d[i][j]); 
    } 
} 
return s; 
}