2013-05-23 6 views
8

Ho una domanda su come viene allocata la memoria quando I calloc. Ho dato un'occhiata alla domanda this, ma non affronta come viene allocata la memoria nel caso di una matrice bidimensionale allocata dinamicamente.Matrici multidimensionali allocate tramite calloc

Mi chiedevo se ci fosse una differenza nella rappresentazione della memoria tra i seguenti tre modi di allocare dinamicamente un array 2D.

Tipo 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] 

Tipo 2:

double **array1; 
int ii; 

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

Tipo 3:

double **array1; 
int ii; 

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

Da quello che ho capito di calloc e malloc, la differenza tra gli ultimi due è che calloc azzererà tutti gli elementi dell'array, mentre lo malloc non lo farà. Ma sono i primi due modi per definire l'array equivalente in memoria?

+0

tipo 2 e tipo 3 sono fondamentalmente la stessa cosa, tranne che la memoria sarà impostata su 0 con 'calloc'. Non so se c'è una vera differenza con il Tipo 1 però. – JBL

+0

@JBL: in realtà solo lo spazio aggiuntivo per i puntatori e il fatto che i blocchi di 10 doppi possono finire per essere non contigui – Dancrumb

+0

@Dancrumb Oh davvero, non ci pensavo. Buon punto – JBL

risposta

2

sono i primi due modi di definire la matrice equivalente in memoria?

Non proprio. Nel secondo tipo sono quasi certamente contigui, mentre nel primo tipo questo non è sicuro.

Tipo 1: in memoria rappresentazione sarà simile a questa:

  +---+---+---+---+---+---+---+---+---+---+ 
    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| | | 
      +---+ 

Tipo 2: in memoria rappresentazione sarà simile a questa:

  +---+---+---+---+---+---+---+---+---+---+  +---+ 
    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

Nel primo modo, si assegnano 10 puntatori al doppio e 100 al doppio. Nel secondo modo si assegnano 100 puntatori al doppio. L'altra differenza è che nel secondo modo si assegna un grande blocco di memoria, in modo che tutti gli elementi dell'array si trovino nello stesso blocco. Nel primo modo, ogni "riga" del tuo array si trova in un blocco diverso rispetto agli altri. Tuttavia, nel secondo modo, l'array dovrebbe essere un double * invece di un double **, perché in questo modo di allocazione, l'array contiene solo puntatori da raddoppiare, non double.

+0

I primi due modi non utilizzano la stessa quantità di memoria. Il primo metodo utilizza più volte la dimensione nativa di un puntatore. – Dancrumb

+0

Primo metodo: 10 puntatori, 100 doppio, secondo metodo: 100 puntatori, non doppio, sì, non lo stesso importo mio cattivo. Ma il secondo metodo non ha ancora assegnato il doppio. –

+0

Modificato per correzioni. –

-1

Sul caso 1, si effettua:

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

Nota: non è possibile supporre che l'area di memoria è continuo, ci potrebbero essere vuoti.

Sul caso 2 si fanno:

array1 -> [memory area of 100] 

Il caso 3 è uguale al caso 2, ma la sua non inizializzare la memoria. Differenza tra i casi 1 e 2 & 3 è che nel primo caso si ha realmente una struttura di memoria 2D. Per esempio, se si desidera scambiare le righe 1 e 2, si può solo scambiare i puntatori:

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

Ma se si vuole fare lo stesso nel caso 2 & 3 è necessario fare vera memcpy. Cosa usare? Dipende da cosa stai facendo.

Il primo modo utilizza più memoria: se si dispone di array di 1000x10, la prima versione utilizzerà 1000 * 8 + 1000 * 10 * 8 (su sistema a 64 bit), mentre il 2 & 3 utilizzerà solo 1000 * 10 * 8.

+0

I casi 2 e 3 utilizzano una memoria inferiore o uguale alla prima. – alk

+0

Hai assolutamente ragione. Questo è quello che ho cercato di spiegare nell'ultimo paragrafo con i numeri, ma ho digitato 'typo' dicendo 'secondo' come si supponeva fosse 'primo'. Ho modificato il post per questo. – susundberg

+0

Sei consapevole che i casi 2 e 3 non assegnano affatto i doppi, ma solo i puntatori a quelli, vero? – alk

1

Semplice esempio

#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; 
}