L’aritmetica dei puntatori Solo due operatori aritmetici possono essere applicati alle variabili di tipo puntatore: somma e sottrazione. int *p1,*p2; int vect[10]; p1 = & vect[0]; // p1 fa riferimento al primo elemento dell’array p2 = p1 + 3; // p2 ora fa riferimento ad una cella di memoria che è collocata ad una distanza in Byte // dalla prima pari a 3 * sizeof(int) p1 vect[0] Come interpreto questo simbolo? vect[1] (a) il quarto elemento dell’array di nome vect (b) l’elemento che, all’interno dell’array, si trova tre celle di memoria (celle di dimensione tale da contenere una variabile di tipo int) dopo la prima vect[2] p2= p1+3 vect[3] Tenendo conto che il nome dell’array, vect, risulta automaticamente definito come il puntatore alla prima cella di memoria dell’array, allora, se p1 = & vect[0]; p1 e vect hanno lo stesso valore Allora i seguenti nomi sono equivalenti: vect[3] / *(p1+3) / p1[3] / *(vect+3) Solo due operatori aritmetici possono essere applicati alle variabili di tipo puntatore: somma e sottrazione. int *p1,*p2; int vect[10]; p1 = & vect[0]; // p1 fa riferimento al primo elemento dell’array p2 = p1 + 4; // p2 ora fa riferimento ad una cella di memoria che è collocata ad una distanza in Byte // dalla prima pari a 4 * sizeof(int) Come posso ora identificare l’elemento vect[2]? p1 vect[0] vect[1] vect[2] p2= p1+4 vect[2] *(p1+2) vect[3] p1[2] *(p2-2) vect[4] p2[-2] // il secondo elemento di un array che inizia da vect // la cella di memoria che si trova due locazioni dopo // la cella cui fa riferimento p1 // il secondo elemento di un array che inizia da p1 // la cella di memoria che si trova due locazioni prima // della cella cui fa riferimento p2 // l’elemento di un array che si trova due posizioni prima // dell’elemento p2[0] Tutto questo ci indica che un’area di memoria contenente un numero arbitrario di variabili può essere gestita tranquillamente conoscendo l’indirizzo di una di queste variabili e la collocazione delle altre. Per semplicità è conveniente fare riferimento all’indirizzo del primo elemento e ricordarsi qual è il numero totale di elementi. Solo due operatori aritmetici possono essere applicati alle variabili di tipo puntatore: somma e sottrazione. int *p1,*p2; int vect[10]; p1 = & vect[0]; // p1 fa riferimento al primo elemento dell’array p2 = p1 + 4; // p2 ora fa riferimento ad una cella di memoria che è collocata ad una distanza in Byte // dalla prima pari a 4 * sizeof(int) Altri impieghi degli operatori + e – non sono ammessi ... int *p1,*p2; int vect[10]; p1=&vect[1]; p2=&vect[4]; p1=p1+p2; // NON ha senso p1=&vect[1]; p2=&vect[4]; p1=p2-p1; // NON ha senso p1=&vect[1]; p2=&vect[4]; p2-p1; // questa espressione può avere senso ... cosa rappresenta? Scriviamo l’algoritmo di ordinamento usando l’aritmetica dei puntatori #include <stdio.h> #include <stdlib.h> #define NUM 50 int main() {int vet[NUM],i,scambio; o for(i=0;i<NUM;i++) o { *(vet+i) =rand()%51; printf(“Ecco il valore di vet[%d]: %d\n”,i, *(vet + i) );} o o o o o o o o o o scambio = 1; while(scambio) // se scambio vale 1 significa che {scambio = 0; // i valori di alcuni elementi sono stati for(i=0;i<(NUM-1);i++) // scambiati tra loro {if( *(vet+i) > *(vet + i+1) ) // non è stato incluso il caso = {scambia(vet + i, vet + i+1 ); scambio=1;} } } o for(i=0;i<NUM;i++) o {printf(“Ecco il valore di vet[%d]: %d\n”,i, *(vet+i) );} o return 0; } Le righe di codice precedute dal simbolo o rappresentano istruzioni in cui si ha un accesso all’area di memoria in cui è contenuto l’array (ovvero le le NUM variabili di tipo int). Visto che un’area di memoria di dimensione arbitraria si gestisce conoscendo, ad esempio, l’indirizzo del primo elemento ed il numero totale di elementi, possiamo implementare delle funzioni che, ricevendo tali parametri, eseguano, in modo compatto, le operazioni svolte da questo codice, in modo da renderlo più modulare. Inoltre, tali funzioni ci permetteranno di visualizzare, inizializzare ed ordinare un QUALSIASI array di variabili di tipo int. funzioni.h #include <stdio.h> #include <stdlib.h> void StampaArrayInt(int *,int); void InitArrayInt(int *, int); void OrdinaArrayInt(int*, int); void StampaArrayInt(int *vet,int dim) {int i; for(i=0 ;i< dim ; i++) printf(“indice: %d, valore: %d\n”,i, *(vet+i)); } void InitArrayInt(int *vet, int dim) {int i; for(i=0;i<dim;i++) *(vet+i) = rand()%51; // qui si può anche usare IntCasuale() } void OrdinaArrayInt(int *vet, int dim) {int i, scambio = 1; while(scambio) {scambio = 0; for(i=0;i<(dim-1);i++) {if( *(vet+i) > *(vet + i+1) ) {scambia(vet + i, vet + i+1 ); scambio=1;} } } array.c #include “funzioni.h” #define NUM 50 int main() {int vet[NUM]; InitArray(vet,NUM); StampaArrayInt(vet,NUM); OrdinaArrayInt(vet,NUM); StampaArrayInt(vet,NUM); return 0; } Le stringhe In generale, di un vettore è noto l’inizio (o meglio la collocazione in memoria del primo elemento), ma nell’array non è contenuta alcuna informazione relativa al numero totale di elementi dell’array stesso. Le stringhe sono degli array di char che hanno un “terminatore”, ovvero si sa dove iniziano ed anche dove terminano. Il carattere che fa da terminatore è il carattere con codice ASCII nullo, ‘\0’ array di 14 char u n a s t r i n g a \0 x v g a \0 x v stringa formata da 11 char array di 14 char u n a \0 s t r i n stringa formata da 3 char La lunghezza di una stringa non coincide con quella dell’array di char che la contiene, per lo meno la lunghezza è inferiore di un’unità (in quanto deve esserci spazio per il carattere terminatore) Come inizializzare una stringa? #include <stdio.h> #include <string.h> #define NUM 50 int main() {char stringa[NUM]; char *indice; printf(“Inserisci una frase (al max di %d caratteri): ”,NUM-1); // NUM-1 per lasciare spazio al terminatore scanf(“%s”,stringa); // stringa rappresenta il puntatore alla locazione di memoria // in cui si inizierà a copiare i caratteri inseriti printf(“Il testo inserito: %s\n”,stringa); // allo specificatore di formato %s deve corrispondere il puntatore // al primo elemento della stringa che deve essere visualizzata printf(“ Di quanti caratteri e’ composta la stringa?\n”,strlen(stringa)); /* La funzione int strlen(char *) appartiene alla libreria standard e permette di determinare di quanti caratteri sia composta una stringa; la funzione conteggia tutti i caratteri ESCLUSO il terminatore */ indice=stringa; // stringa è automaticamente definito come il puntatore al primo elemento dell’array, quindi while(*indice) // il suo valore NON deve essere modificato {printf(“Ecco una parte di stringa: %s\n”,indice); indice=indice+1;} // per ottenere lo stesso risultato, si può scrivere anche indice=stringa; while(indice[0]) printf(“Ecco una parte di stringa: %s\n”,indice++); return 0; } Esercizi proposti Nel testo che segue, vengono richiamate le funzioni definite nella pagina “Esercizi proposti/2” della scorsa lezione. (a) implementare una funzione che riceve come parametro una variabile di tipo char e che restituisce un valore di tipo int; la funzione deve determinare se il carattere ricevuto corrisponda a quello di una vocale. Se così è la funzione deve restituire il valore 1, altrimenti 0. (b) implementare una funzione che riceve come parametro un puntatore a char e restituisce un valore di tipo intero. La funzione deve contare il numero di vocali contenute nella stringa cui fa riferimento il puntatore a char che la funzione riceve come parametro; fatto questo, essa deve restituire un valore pari a tale numero. Per l’implementazione servirsi della funzione definita al punto precedente. (c) implementare una funzione che riceve come parametro una variabile di tipo char e che restituisce un valore di tipo int. Se il carattere ha codice ASCII 32 (è lo spazio, ‘ ‘), la funzione deve restituire 1, altrimenti 0. (d) implementare una funzione che riceve come parametro un puntatore a char e che non restituisce alcun valore: la funzione deve fare in modo che i caratteri della stringa vengano disposti in ordine alfabetico (senza considerare la differenza tra lettere maiuscole e minuscole). Per ottenere questo risultato utilizzare la funzione definita al punto (3) come criterio di ordinamento (permette di decidere se una coppia di caratteri sia disposta o meno secondo l’ordine alfabetico) e la funzione definita al punto (1) per scambiare i valori di due variabili di tipo char (ovvero di due elementi della stringa) (e) Scrivere un programma in cui venga dichiarato un vettore di char formato da NUM elementi (definire la macro NUM); chiedere all’utente di inserire del testo e salvare tale testo nell’array di char appena definito. Visualizzare i caratteri inseriti dall’utente. (Facoltativo: svolgere quest’ultimo compito senza usare un indice di tipo intero). Usando la funzione definita al punto (b) contare quante vocali siano presenti nel testo inserito dall’utente. (Facoltativo: usando la funzione definita al punto (c), provate a far diventare maiuscole tutte le iniziali delle parole inserite dall’utente). Usando la funzione definita al punto (d) ordinare i caratteri contenuti nella stringa. (f) Facoltativo: vi viene in mente un metodo per invertire l’algoritmo di ordinamento, ovvero per ottenere la stringa originale a partire da quella ordinata?