I caratteri



La gestione dei caratteri in C avviene mediante delle variabili di tipo char che, a tutti gli effetti, possono essere considerate degli interi positivi da 8 bit.

In C, cioè, vi è una corrispondenza biunivoca tra caratteri ed interi a 8 bit, ovvero ad ogni carattere corrisponde uno ed un solo numero (ovvero il CODICE ASCII del carattere, che varia da 0 a 255) e ad ogni numero compreso tra 0 e 255 corrisponde uno ed un solo carattere. Ad esempio, le lettere minuscole sono raggruppate in ordine alfabetico, al carattere ‘a’ corrisponde il numero 97, al ‘b’ il 98 e così via fino al carattere ‘z’ cui cirrisponde il 122. La stessa cosa vale per le lettere maiuscole; in questo caso i codici ASCII sono pari a quelli del corrispondente carattere minuscolo, diminuito di 32 (ovvero, se la ‘a’ ha codice 97, la ‘A’ ha codice ASCII 97 – 32, cioè 65). Anche i caratteri numerici hanno un codice ASCII, si parte dal carattere ‘0’ che ha come codice corrispondente il 48, poi c’è il carattere ‘1’ che ha codice ASCII 49 e così via.






Esempio:


Supponiamo di voler scrivere un programma in C che stampi la parola : ciao !


Per farlo, dovremo sostanzialmente stampare i 6 caratteri necessari per comporre la parola desiderata.



#include <stdio.h>   // includo il file per usare la funzione printf()


main()

{char car1,car2,car3,car4,car5,car6;

 int n1,n2,n3,n4,n5,n6;


 // prima soluzione

 printf(“\n ciao ! \n”);   //stampo la scritta ciao ! usando una sola istruzione


 // seconda soluzione

 car1=’c’;

 car2=’i’;

 car3=’a’;

 car4=’o’;

 car5=’ ’;

 car6=’!’;

 printf(“\n %c%c%c%c%c%c \n”,car1,car2,car3,car4,car5,car6);

 printf(“ecco i codici ASCII dei caratteri:”);

 printf(“\n c:%d i:%d a:%d o:%d spazio:%d p. escl:%d \n”,car1,car2,car3,car4,car5,car6);


 // terza soluzione

 n1=99;

 n2=105;

 n3=97;

 n4=111;

 n5=32;  // anche il carattere –spazio- ha il suo codice ASCII

 n6=33;

 printf(“\n %c%c%c%c%c%c \n”,n1,n2,n3,n4,n5,n6);




// Attenzione: anche i caratteri numerici hanno un codice ASCII corrispondente

 car1=’0’;

 car2=’1’;

 car3=’2’;

 car4=’3’;


/* NOTA

 l’istruzione

 car4 = 3;

produrrebbe tutt’altro effetto !

*/


// Ora stampo i 4 caratteri:


 printf(“\n %c %c %c %c \n”,car1,car2,car3,car4);


// lo stesso risultato si ottiene in questo modo:

 

 n1='0';

 n2=n1+1;

 n3=n1+2;

 n4=n1+3;


printf(“\n %d %d %d %d \n”,n1,n2,n3,n4);


// ora visualizzo i codici ASCII dei caratteri


 printf(“\nCODICI ASCII: %d %d %d %d \n”,car1,car2,car3,car4);


}

 

 

 

 

Una variabile di tipo char occupa in memoria 8 bit, mentre un intero ne occupa 32. Possiamo quindi immaginare che un intero sia composto da 4 caratteri. Questo ci aiuta a capire cosa succede se tentiamo di assegnare ad un carattere il valore di un intero il cui valore supera 255 (che è il massimo intero ottenibile con 8 bit).


Se questa è la rappresentazione binaria di un intero (32 bit), 2603


00000000000000000000101000001011


e tentiamo di assegnare tale valore ad una variabile di tipo char, che può contenere al massimo 8 bit, nell’assegnazione vengono mantenuti solo gli ultimi 8 bit a destra.


00000000000000000000101000101011


e quindi il carattere diventa


00101011


Questo intero di 8 bit vale 43, e a questo codice ASCII corrisponde il carattere ‘+’.


Questo risultato si può anche interpretare dicendo che, nell’assegnare il valore di una variabile di tipo intero, num1, ad una di tipo char, car1, il valore di quest’ultima è pari a quello di num1 modulo 256.



L’operatore modulo opera in questo modo: il risultato di


a modulo b


è pari al resto della divisione intera di a per b. Il simbolo dell’operatore modulo è : %


Per fare un esempio, calcoliamo 7 modulo 3, ovvero


7 % 3


Il 3 sta due volte nel 7 ed avanza 1. Quindi


7 % 3 = 1 


25 % 9 = 7


12 % 2 = 0

 

 

 

Lettura e scrittura dei caratteri


Per poter leggere UN SINGOLO CARATTERE dalla tastiera e visualizzare UN SINGOLO CARATTERE sul monitor si possono utilizzare le funzioni:


int getchar(void) (per leggere dalla tastiera)


int putchar(int) (per visualizzare un carattere sul monitor)


Per utilizzare tali funzioni è necessario richiamare un altro file della libreria standard: stdio.h

Valori restituiti dalle funzioni:


La funzione getchar() restituisce il codice ASCII del carattere che viene premuto. La funzione putchar() restituisce il codice ASCII del carattere che viene visualizzato.


Ad esempio, per stampare la lettera ‘a’ si deve usare l’istruzione:


char lettera = ‘a’;

putchar(97);

// o anche l’istruzione

putchar(lettera);

// o anche l’istruzione

putchar(‘a’);


// Tutte queste sono equivalenti a

printf(“%c”,lettera);


// oppure a

printf(“%c”,’a’);

Viceversa, per acquisire un carattere premuto sulla tastiera si usa l'istruzione:

 

char lettera;

lettera=getchar();

 


Esempio 1.


#include <stdio.h>     // per poter usare printf()


int main()

{char leggo;

 printf(“\nInserire un carattere : “);

 leggo=getchar();

 printf(“\n E\’ stato inserito il carattere: %c”,leggo);

 return 0;

}



Versione alternativa (cambia il tipo della variabile leggo)


#include <stdio.h>     // per poter usare printf()


int main()

{int leggo;

 printf(“Inserire un carattere : “);

 leggo=getchar();

 printf(“\n E\’ stato inserito il carattere: %c”,leggo);

 // o anche

 printf("\n E\' stato inserito il carattere: ");

 putchar(leggo);

 return 0;}






Esempio 2.


Sapendo come memorizzare i caratteri premuti sulla tastiera possiamo ora scrivere un programma che riconosca la categoria cui appartiene il carattere analizzando il codice ASCII del carattere stesso. Conosciamo infatti gli operatori di confronto che ci permettono di valutare in quale intervallo cade il codice ASCII del carattere premuto.


Promemoria:

lettere MAIUSCOLE : codici ASCII compresi tra 65 e 90, estremi inclusi

lettere minuscole : codici ASCII compresi tra 97 e 122, estremi inclusi

caratteri numerici : codici ASCII compresi tra 48 e 57, estremi inclusi


Tra il codice ASCII di una lettera MAIUSCOLA e quello della corrispondente lettera minuscola esiste una differenza di 32 unità.

Per individuare una lettera MAIUSCOLA, ad esempio, il codice ASCII del carattere deve essere compreso tra 65 e 90. Ovvero, se la variabile che contiene il valore del carattere si chiama car, devono essere verificate le seguenti condizioni:


car >= 65   // l'espressione è VERA se il codice ASCII di car supera 64

car <= 90   // l'espressione è VERA se il codice ASCII di car è inferiore a 91

Visto che le due espressioni devono essere SIMULTANEAMENTE vere, allora le due espressioni devono essere confrontate usando l'operatore logico AND, così da determinare se entrame le condizioni siano vere contemporaneamente:


(car >= 65) && (car <= 90)




Versione 1.


#include <stdio.h>


// effettuo il confronto utilizzando il codice ASCII del carattere inserito


int main()

{char lettura;

 printf("\n Inserisci una lettera a piacere: ");

 lettura = getchar();

 if( (lettura>=65) && (lettura<=90) )

   {printf("\nE\' stata inserita una lettera maiuscola.");

     printf("\nLa lettera e\' : %c",lettura);

     printf("\nLa corrispondente lettera minuscola e\' : %c",lettura+32);

   }

 return 0;}



Versione 2.


#include <stdio.h>


// effettuo il confronto utilizzando il valore del carattere inserito


int main()

{char lettura;

 printf("\n Inserisci una lettera a piacere: ");

 lettura = getchar();

 if( (lettura>='A') && (lettura<='Z') )

   {printf("\nE\' stata inserita una lettera maiuscola.");

     printf("\nLa lettera e\' : %c",lettura);

     printf("\nLa corrispondente lettera minuscola e\' : %c",lettura+32);

   }

 return 0;}


Versione 3.

In questa versione determino se il carattere inserito corrisponde ad una lettera maiuscola valutando se il suo codice ASCII sia inferiore a quello della lettera A o superiore a quello della lettera Z. Nel caso in cui almeno una delle due condizioni sia verificata abbiamo la certezza che il carattere non sia quello corrispondente ad una lettera maiuscola. Quindi, se l'espressione

(lettura < 'A') || (lettura > 'Z')


è vera, allora lettura non corrisponde ad una lettera maiuscola. Negando tale espressione, quindi, otterrò quella equivalente alla precedente.


#include <stdio.h>


// effettuo il confronto utilizzando il valore del carattere inserito


int main()

{char lettura;

 printf("\n Inserisci una lettera a piacere: ");

 lettura = getchar();

 if( ! ( (lettura<'A') || (lettura>'Z') ) )

   {printf("\nE\' stata inserita una lettera maiuscola.");

     printf("La lettera e\' : %c",lettura);

     printf("\nLa corrispondente lettera minuscola e\' : %c",lettura+32);

   }

 return 0;}


Versione 4.


In questa versione sfruttiamo il fatto che ogni assegnazione possiede un valore; il confronto tra tale valore e quello del codice ASCII della lettera A ci fornisce un primo indizio per capire se la lettera sia o meno maiuscola.


#include <stdio.h>


// effettuo il confronto utilizzando il valore del carattere inserito


int main()

{char lettura;

 printf("\n Inserisci una lettera a piacere: ");

 
 if(
(lettura = getchar())>='A' )

  if( lettura <= 'Z'  )

   {printf("\nE\' stata inserita una lettera maiuscola.");

     printf("La lettera e\' : %c",lettura);

     printf("\nLa corrispondente lettera minuscola e\' : %c",lettura+32);

   }

 return 0;}

 

 

In quest’ultima versione abbiamo sfruttato il fatto che la funzione getchar() restituisce un valore, ovvero il codice ASCII del carattere inserito da tastiera. Come abbiamo visto, anche putchar() restituisce un valore.

 

printf() restituisce un valore ? Sì, restituisce un intero il cui valore è pari al numero di caratteri stampati

 

 

int numero_caratteri;

numero_caratteri = printf(“ho stampato 24 caratteri”);

printf(“\n %d \n”,numero_caratteri);

 

// quest’ultima istruzione visualizza il valore della variabile, ovvero 24.

 

ATTENZIONE:

(1) l'espressione

lettura = getchar() >= 'A'

oltre ad essere ambigua avrebbe anche fornito il risultato sbagliato, visto che l'operatore di confronto ha priorità superiore a quello di assegnazione. il valore di lettura sarebbe stato 1 o 0 a seconda del valore del carattere inserito

(2) Gli operatori relazionali sono, come molti altri, associativi da sinistra a destra. Questo significa che se, nella stessa espressione, compaiono più operatori relazionali aventi la medesima priorità, il compilatore fa sì che i valori logici vengano calcolati a partire dall’espressione che sta a sinistra.

Ad esempio, supponendo di avere a disposizione tre variabili di tipo char, nell’espressione

( car1 > ‘A’ ) && ( car2 < ‘1’ ) || (car3 >= ‘z’)

la prima espressione ad essere valutata è  ( car1 > ‘A’ ), poi si valuta se ( car2 < ‘1’), poi si esegue l’AND e poi si procede con la terza. Inoltre, se prima di completare la valutazione di tutte le espressioni il programma è in grado di determinare il valore dell’espressione complessiva, allora il valore delle espressioni non valutate non viene calcolato.

Ad esempio

( 8 < 4 ) && ( car1 > ‘g’ ) && ( car3 < ‘u’ )

Dalla tabella di verità dell’operatore AND sappiamo che basta che una sola delle due condizioni sia FALSA affinché l’espressione complessiva risulti falsa. Quindi nell’esempio il risultato dell’espressione è noto prima ancora di valutare il risultato del secondo e del terzo confronto.

Questo può creare delle difficoltà nel caso in cui, all’interno dei confronti, ci siano delle assegnazioni o delle inizializzazioni.

Supponiamo di scrivere un’espressione simile a

( car1 > ‘9’ ) && ( car2 = ‘t’) && ( (car3 = getchar()) > ‘y’ )

che risulta composta da un confronto, un’assegnazione ed un’assegnazione con un confronto. Se scomponiamo l’espressione complessiva nelle sue sottoespressioni troviamo

 

car1 > ‘9’

car2 = ‘t’

(car3 = getche()) > ‘y’

VERA / FALSA

VERA

VERA / FALSA

 

Se per caso la prima risultasse FALSA il programma sarebbe già in grado di determinare il valore complessivo dell’espressione, ovvero 0. In questo caso, quindi, la terza espressione potrebbe non essere valutata e, di conseguenza, all’utente potrebbe non essere richiesto di inserire un carattere.

La conclusione è: mai fare affidamento su assegnazioni o inizializzazioni quando queste sono inserite all’interno di espressioni simili a quella indicata sopra.

NOTA : per risolvere i problemi connessi alla gestione che getchar() fa del buffer di input guardare qui.