con riferimento al tipo intero Siete il visitatore n. Click here for English version Pagina ottimizzata per risoluzione 1024x768 |
||
Torna alla Home Page | Argomenti
simili su questo sito Funzioni C++ per input da tastiera controllato in applicazioni console |
In
questa pagina sono descritti alcuni problemi che possono sorgere
utilizzando i tipi discreti del C++. In particolare sono riportate
alcune note ed esempi che possono aiutare ad evitare, in fase di
programmazione, errori logici nell'utilizzo degli interi. Il C++ consente l'impiego
di alcuni tipi predefiniti, quali il tipo int.
Si tratta, come il suo stesso nome suggerisce, di un tipo in grado di
rappresentare gli interi, o, meglio, un sottoinsieme degli interi. In
generale, è possibile utilizzare più di un tipo
di interi, per esempio short
int, int, long int,
ciascuno con la propria dimensione (occupazione di memoria), ma,
attenzione, non è sempre vero che short int, int e long int
hanno tre dimensioni diverse. Ma c'è anche qualcos'altro da dire: in generale, altri compilatori dovrebbero usare lo stesso spazio utilizzato dal compilatore originale, altrimenti potrebbero verificarsi alcuni effetti imprevisti, specialmente nel caso eseguiate operazioni bit a bit sui vostri interi. Attenzioni alle dimensioni e all'aritmetica I brevi programmi d esempio in questa pagina sono stati scritti e compilati con DevCpp 4.9.8.0 per Win. Si invita a leggere la sezione "Riconoscimenti e avvertenze". Per controllare la dimensione (in byte) con la quale il compilatore memorizza e tratta gli elementi di un dato tipo, potete usere nei vostri programmi l'operatore sizeof (type). Questo operatoer è valutatao a tempo di compilazione, non di esecuzione (run-time). Se desiderate scrivere un programma in cui, per esempio, utilizzati interi da memorizzare su due byte, potete eseguire questo controllo attraverso l'operatore sizeof (int). Siupponiamo che questo operatore restituisca 2, così il tipo int va bene. Se, su un'altra piattaforma, compilate il programma e, lì, sizeof(int) restituisce un valore maggiore di 2, il programma potrebbe non funzionare; se, invece, scrivete un programma che utilizza interi memorizzati su quattro byte, e poi, cambiando piattaforma, il programma ricompilato vi informa attraverso l'operatore sizeof che il compilatore memorizza il tipo int su due byte, mai, dico mai, dovreste usare il vostro programma originale su tale piattaforma. Una possibilità di consentire la portabilità del codice consiste nell'utilizzare interi exact-type, per esempio int16_t, int32_t, int64_t, per i quali è necessaria la libreria stdint. Controllate come utilizzarla. Un modo per controllare che il vostro compilatore memorizza gli interi sul numero di byte che vi aspettate è, per esempio, quello di usare nelle prime righe del vostro programma qualcosa come if (sizeof (int) != 4)
Riguardo alle dimensioni dei tipi interi, per esempio, sul mio sistema DevCpp per Win si comporta in questo modo: sizeof (short) = 2 Negli esempi seguenti, salvo diversa indicazione, assumeremo che il tipo int è memorizzato dal compilatore su quattro byte. Concetti analoghi, mutatis mutandis, possono essere applicati a interi su meno o più di quattro byte (generalmente due e otto). Talvolta, negli esempi, useremo int su due byte, visto che i loro limiti sono abbastanza piccoli e facili da ricordare. Con il tipo int su due byte, e la rappresentazione in complemento a due, i numeri int minore e maggiore che possono essere rappresentati sono -32768 and +32767. Con la rappresentazione in complemento a due su N bit, ad esempio 16, i numeri interi minore e maggiore che possono essere rappresentati sono -(2^(N-1)) e 2^(N-1)-1. Si fa notare che in queste righe relative alla rappresentazione dei numeri il simbolo ^ è usato per significare "elevato a", e non è l'operatore C++ XOR. Dato un numero intero x, la sua rappresentazione X in complemento a due è X=x se 0 <= x <= 2^(N-1) -1 (ad esempio, 0<= x <= 32767) X= 2^N - |x|, se -(2^(N-1)) <= x <= -1 (ad esempio, -32768 <= x <= -1) Viceversa, data la rappresentazione X in complemento a due su N bit di un intero, il corrispondente numero intero può essere determinato dalle relazioni x=X se 0 <= X <= 2^(N -1) -1 (ad esempio, 0<= X <= 32767) x= - (2^N - X) , se 2^(N-1) <= X <= (2^N) -1 (ad esempio, 32768 <= X <= 65535) Ad esempio, x= -32768 è rappresentato come X=32768, x= -32769 non può essere rappresentato su 16 bit, x= -32767 è memorizzato come X=32769, x= -1 come X=65535, mentre x=0 diventa X=0, x=1 diventa X=1, x=32767 diventa X=32767. Viceversa, X=65534, essendo maggiore di 2^(16-1)-1=32767, rappresenta un numero negativo, esattamente -(2^16-X) = -(65536-65534) = -2. Dopo quanto descritto qui sopra, potete vedere che nella rappresentazione in complemento a due, per lo più o universalmente adottata per rappresentare i numeri interi all'interno delle macchine, se il primo bit (MSb, il bit più significativo) è zero il numero rappresentato è positivo o è zero, se il MSb è uno il numero rappresentato è negativo. Diamo ora un'occhiata ad alcuni problemi che possono sorgere con gli interi, se non si fa abbastanza attenzione. Incremento/decremento, adddizione/sottrazione Supponete di voler eseguire una certa operazione per 40000 volte, supponete di scrivere il seguente programma C++. Se siete consapevoli di aver bisogno di un intero su quattro byte, ma non controllate se il tipo int è davvero memorizzato su quattro byte, e il vostro compilatore memorizza gli interi su due byte, allora il programma int a=0; for (int a=0; a<40000; a++)
esegue un loop infinito, poiché dopo 32767 viene ... -32768, -32767 ... E' possibile che il compilatore vi avverta, se nel programma viene assegnato il valore costante 40000 a una variabile che non può coontenerlo. In questo caso dovete aver cura di utilizzare un tipo intero memorizzato almeno su 4 byte, ad esempio potreste provare con il tipo long long int. Ricordate che questo era un esempio; come altro esempio, nel DevCpp 4.9.8.0 per Win un int è su 4 byte e un long long int è su 8 byte. Attenzione: in generale il tipo long int è memorizzato o su un numero di byte pari al tipo int, o su più byte: controllate la vostra implementazione. Anche se avete avuto cura che i valori delle variabili possano essere contenuti nel tipo che avete scelto per esse, la loro somma o sottrazione potrebbe generare un risultato fuori dai limiti ammessi per tale tipo, e voi non potreste accorgervene a tempo di esecuzione (a meno che proviate a gestire l'overflow, il carry e così via). Forse potrebbe essere utile utilizzare tipi più "grandi" per memorizzare il risultato di tali operazioni. Cosa analoga (molto più frequente) quando moltiplicate due interi; se siete sicuri che ogni possibile risultato possa essere memorizzato in un int, potete usare un int per il risultato, altrimenti dovete utilizzare un tipo più "grande". Con un tipo int su 4 byte, per il quale i limiti sono -2147483648 e +2147483647, scriviamo il seguente programma: int a= 200000; int
b=200000; Quale pensate sia il risultato? Il programma visualizza sullo schermo "success", e a*b viene mostrato valere 1345294336 dall'operatore cout, invece di 40000000000. Avreste dovuto usare un long long int per c e assegnare a*b a un long long int d, ammesso che il tipo long long int sia su otto byte. Valore assoluto Quando utilizzate la funzione int abs(int n), data la asimmetria dell'intervallo di rappresentazione degli interi in complemnto a due, dovreste fare attenzione che n non sia il minimo intero ammesso; infatti, supposto che uno short int sia su due byte, il seguente programma non funziona. short int asi = 0; Poiché abs(-32768) non può essere rappresentato su due byte, la funzione abs restituisce -32768, così sullo schermo si ottiene asi = -32768 , aaa = abs(asi) = -32768 Dividere un numero in cifre In base all'implementazione, il quoziente della divisione intera, quando il quoziente è negativo, può essere troncato verso zero o verso meno infinito. Nel primo caso, il resto ha lo stesso segno del dividendo, nel secondo ha il segno del divisore. In ogni caso, a = (a/b)*b + a%b Supponete di voler dividere un numero intero in cifre, memorizzandole in un array. Il seguente programma, se a < 0, può non funzionare; const int
n_max_int_digits=32; // int type on 4 bytes Infattim suoponiamo che serv sia -342; se serv/10 vale -34, e perciò serv %10 vale -2, allora digits[0]=2, ma se serv/10 = -35, serv%10 = +8, e a digits[0] viene assegnato un valore errato. Per dividere in cifre un numero intero, suggerisco invece la seguente routine: int
servizio=0; Sperando di esservi stato utile, spero di poter aggiungere presto qualcos'altro. Se volete, scrivetemi per commenti e suggerimenti. Buon lavoro! |
|
|
Ultimo aggiornamento: 30 Luglio 2004 |