PDA

Visualizza la versione completa : [C] Puntatori a puntatori


Darèios89
19-01-2013, 00:05
Ho sempre saputo che un puntatore ad un puntatore, è un' area di memoria, che punta ad un' altra area di memoria, che punta ad una locazione di memoria contenente qualcosa. Di recente ho letto che un' espressione come **void può essere visto non come puntatore a puntatore, ma come array bidimensionale. Non riesco a capire e ad immaginarmi il perchè....

MItaly
19-01-2013, 03:49
Ni, non necessariamente un vero e proprio array bidimensionale, ma in generale un "jagged array". Normalmente quello che dici viene implementato come un vettore di puntatori, ciascuno dei quali punta ad un vettore. Tipicamente si fa una cosa di questo genere:


size_t w=10, h=15;
int ** arr;
arr=malloc(h*sizeof(int*));
for(size_t i=0; i<h; i++)
arr[i]=malloc(w*sizeof(int));

a questo punto arr punta ad un vettore di 15 puntatori, ciascuno dei quali punta ad un vettore di 10 puntatori; questa roba può in un certo senso essere vista come un array bidimensionale, specie visto che la sintassi per accedere agli elementi è ben quella:


arr[4][7]=42; /* imposta il settimo elemento del quarto vettore */

(per deallocare:


for(size_t i=0; i<h; i++)
free(arr[i]);
free(arr);)

Hard*RTOS
19-01-2013, 13:06
Originariamente inviato da Darèios89
**void può essere visto non come puntatore a puntatore, ma come array bidimensionale.

Più vero il contrario: un array bidimensionale è visto come un array di puntatori a puntatori.

Fermo restando i concetti espressi da MItaly, occorre specificare che nel linguaggio C gli array bidimensionali non esistono, e vengono gestiti - sia quando allocati dinamicamente che staticamente - come array di puntatori ad array e quindi come array di puntatori a puntatori.

Agli array multidimensionali C-style sono - nel C++ - nettamente preferibili direttamente list, string, vector.

MItaly
19-01-2013, 15:22
Originariamente inviato da Hard*RTOS
Fermo restando i concetti espressi da MItaly, occorre specificare che nel linguaggio C gli array bidimensionali non esistono, e vengono gestiti - sia quando allocati dinamicamente che staticamente - come array di puntatori ad array e quindi come array di puntatori a puntatori.

None. In C gli array multidimensionali esistono, e sono un concetto ben distinto dagli array di puntatori. Giusto per dire una differenza fondamentale, lo standard garantisce che gli elementi di un array multidimensionale siano memorizzati in maniera contigua, motivo per cui su un array bidimensionale si può iterare tranquillamente come se fosse un array monodimensionale, cosa che non si può fare con un jagged array. Inoltre, se gli array multidimensionali fossero dei jagged arrays sarebbe possibile convertirli in puntatori multipli, cosa che non è invece possibile fare.
Qui (http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c/4810676#4810676) la questione è spiegata bene (si parla di C++, ma per quanto riguarda gli array concetti sono fondamentalmente gli stessi).

Hard*RTOS
19-01-2013, 15:55
Il "non esistono" si riferisce al fatto che in C vengono emulati tramite array di puntatori, quindi sintatticamente esistono, in pratica no.

Scara95
19-01-2013, 16:08
Originariamente inviato da Hard*RTOS
Il "non esistono" si riferisce al fatto che in C vengono emulati tramite array di puntatori, quindi sintatticamente esistono, in pratica no.
No, in C non sono affatto un array di puntatori, hanno proprietà completamente diverse, questo voleva dirti MItaly!

MItaly
19-01-2013, 16:09
Originariamente inviato da Hard*RTOS
Il "non esistono" si riferisce al fatto che in C vengono emulati tramite array di puntatori, quindi sintatticamente esistono, in pratica no.


int a[10][20];

questo è un array bidimensionale, e non è un array di puntatori.


int (*a)[20]=malloc(10*20*sizeof(int));

Questo è lo stesso array allocato dinamicamente, e non è un array di puntatori (piuttosto, è un puntatore a vettori, che punta ad un'area di memoria che viene usata come vettore di vettori). In entrambi i casi, quando scrivo


a[5]

non ottengo un int *, ma un int[20], che è una cosa ben diversa. Inoltre, in entrambi i casi lo storage è continuo; è garantito che io possa fare


int * b=&(a[0][0]);

e quindi indicizzare gli elementi con un solo indice (che va da 0 a 199), mentre con un array di puntatori ciò non è possibile.

Se passo un array multidimensionale ad una funzione devo specificare tutte le dimensioni (tranne al più la prima) dato che l'operatore di subscripting per un array multidimensionale ha bisogno di sapere le dimensioni dei sotto-array per poter raggiungere l'elemento desiderato (con il procedimento descritto a C99 §6.5.2.1, ¶3); con un jagged array questo non è necessario, dato che per ogni dimensione c'è un array di puntatori che indica dove inizia la "riga" della dimensione successiva.

Hard*RTOS
19-01-2013, 16:48
Ripeto, sintatticamente esistono e possono essere usati; a livello più basso rispetto al codice vengono gestiti come array di puntatori ad array, e quindi come array di puntatori a puntatori; non mi riferisco alle specifiche del linguaggio o a mere definizioni, ma a quanto accade "dietro le quinte".

Scara95
19-01-2013, 16:55
Ed è appunto quanto accade dietro le quinte che è diverso:


int *a, b[3][3], i, j;
a = (int*)b;
for(i = 0; i < 9; i++)
a[i] = i;
for(i = 0; i < 3; i++) {
for(j = 0; j < 3; j++)
printf("%d ", b[i][j]);
printf("\n");
}


Con un array di array non puoi fare questo! E lo standard serve proprio a definire come vanno le cose "sotto il cofano".

MItaly
19-01-2013, 17:21
Originariamente inviato da Hard*RTOS
Ripeto, sintatticamente esistono e possono essere usati; a livello più basso rispetto al codice vengono gestiti come array di puntatori ad array, e quindi come array di puntatori a puntatori
È qui l'errore: dietro le quinte vengono gestiti in maniera diversa.
Un jagged array funziona con una doppia indirezione - a[j] è *(*(a+i)+j) - mentre un array bidimensionale "vero" va di singola indirezione, dato che le dimensioni delle varie dimensioni sono note e lo storage è contiguo - a[i­][j] è (&a[0][0] + i*N+j), dove N è la lunghezza della riga. Per averne conferma basta guardare lo standard da un lato (vedi il riferimento citato prima), e l'assembly emesso da qualunque compilatore dall'altro.
E, di nuovo, questo è il motivo per cui un array n-dimensionale non può decadere ad un puntatore n-uplo - questo richiederebbe la generazione di tutti gli array di puntatori "intermedi" che per un "vero" array n-dimensionale non sono richiesti (perché appunto gli elementi sono raggiunti "a colpo" e non con n-upla indirezione).

Poi ovviamente la regola "as-if" degli standard C e C++ consente al compilatore di fare quel che gli pare dietro le quinte (a patto che il comportamento osservabile sia conforme), ma i constraint che ci sono sugli array multidimensionali (storage contiguo, tutte le dimensioni meno la prima [i]devono essere note) nascono appunto per consentire un'implementazione di questo genere.

Loading