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