Linguaggio C per MSX
Rubrica a cura di Nicola Brogelli (Parte 5)
Array e Puntatori
Array
Gli array sono sequenze di variabili dello stesso tipo che vengono memorizzare consecutivamente nella memoria ed alle quali è possibile accedere usando uno stesso nome (identificatore) a cui viene aggiunto un indice. L’uso principale di array è archiviare e organizzare un gran numero di elementi simili in una singola “struttura dati” , rendendone più facile la gestire e manipolazione.
Un “oggetto” di tipo array è una n-upla di elementi ordinati dello stesso tipo, ai quali è possibile accedere mediante il loro indice. Gli elementi di un array di dimensione n sono compresi in un intervallo che va da 0 a n – 1.
type identifier [constant-expressio]
La definizione sopra illustrata, rappresenta la dichiarazione di un array monodimensionale (vedremmo in seguito gli array a più dimensioni). Possiamo vedere come l’array ha un tipo ben definito il quale descrive gli elementi che l’array può contenere, un identificatore che è il nome dell’array e un valore numerico maggiore di zero che definisce la sua dimensione. Un array può essere inizializzato contestualmente alla sua dichiarazione elencando all’interno delle parentesi graffe i valori separati da virgole. Oppure può essere dichiarato e successivamente inizializzato.
Nell’esempio successivo non è necessario dare una dimensione all’array in quanto è inizializzato in modo esplicito (contestualmente alla dichiarazione inizializziamo l’array con i valori riportati tra parentesi graffe).
int numeri[] = {1,2,3,4,5};
Sugli array non sono definite operazioni aritmetiche e di confronto ne possono essere restituiti da una funzione, l’unica operazione definita è la selezione con indice (gli array posso essere utilizzati come argomento di una funzione, vedremmo alcuni esempi nella prossima dispensa).
Le istruzioni che seguono dichiarano che numeri è un array di interi con dimensione 5, e successivamente si assegna il valore 7 all’elemento numero quattro dell’array.
int numeri[5];
numeri[3] = 7;
Dobbiamo fare molta attenzione perché il linguaggio non e effettua nessun controllo sui valori degli indici, l’esempio seguente non darebbe nessun errore in fase di compilazione, ma potrebbe generare problemi a tempo di esecuzione. Andremmo infatti a scrivere il valore 7 in una zona di memoria di cui non conosciamo il significato.
int numeri[5];
numeri[5] = 7;
Array e Puntatori
Questo è un argomento un po’ più complesso, ma cercherò di descriverlo nel modo più semplice possibile. Il nome di un array (nel caso precedente era numeri) è anche l’indirizzo di memoria in cui si trova il primo elemento dell’array stesso. Pertanto possiamo dire che se numeri[n] è un array allora &numeri[0] e numeri rappresentano in modo analogo il suo indirizzo. Nella porzione di codice qui sotto le due stampe riporteranno lo stesso indirizzo di memoria.
int numeri[] = {1,2,3,5,6};
printf(“\n stampo numeri: %d”, numeri);
printf(“\n stampo &numeri[0]: %d”, &numeri[0]);
Se al nome dell’array viene sommato un valore intero i, il risultato sarà un indirizzo di memoria dato dall’indirizzo di base dell’array più un offset dato da i. Nell’esempio seguente entrambe le stampe restituiranno lo stesso indirizzo di memoria (la differenza sostanziale con l’esempio precedente risale nell’offset i).
int numeri[] = {1,2,3,5,6};
int i = 2; // i deve essere un numero compreso tra 0 e 4
printf(“\n stampo numeri: %d”, numeri + i);
printf(“\n stampo &numeri[i]: %d”, &numeri[i]);
Mentre *numeri e numeri[0] rappresentano allo stesso modo il primo elemento dell’array (valore). Quindi entrambe le stampe mostrate nella porzione di codice daranno lo stesso risultato.
int numeri[] = {1,2,3,5,6};
printf(“\n stampo *numeri: %d”, *numeri); // stamperà 1
printf(“\n stampo numeri[0]: %d”, numeri[]); // stamperà 1
Come per gli indirizzi, anche in nell’esempio di codice riportato qui sotto, per ottenere il valore di un i-esimo elemento dell’array, abbiamo sommato a numeri l’offset i. Entrambe le stampe restituiranno lo stesso risultato.
int numeri[] = {1,2,3,5,6};
int i = 2; // i deve essere un numero compreso tra 0 e 4
printf(“\n stampo *(numeri + i): %d”, *(numeri + i)); // stamperà 3
printf(“\n stampo numeri[i]: %d”, numeri[i]); // stamperà 3
Quindi riassumendo, il primo elemento di un array numeri[n] si riferisce mediante l’espressione *numeri, l’i-esimo elemento dell’array si riferisce con l’espressione *(numeri + i). Ma più in generale se ind è un indirizzo e ogni riferimento a ind[i] può essere espresso nella forma *(ind + i).
Aritmetica e Puntatori
Vediamo di dare un ulteriore approfondimento all’aritmetica dei puntatori che abbiamo accennato negli esempi precedenti. Utilizzando l’aritmetica dei puntatori potremmo accedere come visto in precedenza ai vari elementi dell’array. Incrementando un puntatore si incrementa l’indirizzo di memoria contenuto nel puntatore facendo si che punti all’elemento successivo. Per far ciò il C conosce il tipo di dato puntato e incrementa l’indirizzo in base alla sua dimensione. Quindi supponiamo che pi sia un puntatore ad un array di interi con l’istruzione pi++ il valore di pi viene incrementato della dimensione del tipo int (generalmente 2 byte). Allo stesso modo se pf punta a un array di float, pf++ incrementerà il suo valore della dimensione di tipo float (generalmente 4 byte). Questo vale anche per incrementi superiori all’unità. Se ad esempio aggiungiamo un valore n a un puntatore, il C incrementa di del numero di byte necessari per raggiungere il nuovo indirizzo di memoria. Quindi:
pi +=4
incrementerà il contenuto di pi di 8 byte (assumendo sempre che un int sia 2 byte).
Tutto il ragionamento fatto fino a qui vale allo stesso modo per il decremento che viene effettuato tramite gli operatori — o -= .
L’ultima operazione prevista a la differenziazione tra due puntatori che puntano a due differenti elementi dello stesso array, questo permette di sapere quanto distino l’uno dall’altro.
Quindi se pi1 e pi2 sono due puntatori ad un medesimo array di interi , l’espressione pi1 – pi2 fornisce la distanza tra loro due.
Sono validi anche tutti gli operatori relazionali come ==, !=, >, < ecc.. purché entrambi i puntatori puntino allo stesso array.




0 commenti