Complementi - 1
Ver 2.4.1
© 2010 - Claudio Fornaro - Corso di programmazione in C
2
L’indentazione del codice L’indentazione consiste nel precedere le righe
di codice con un certo numero di spazi Ha lo scopo di evidenziare i blocchi di codice Ignorata dal compilatore, serve al
programmatore per cogliere visivamente la struttura del programma
E’ utile indentare il codice mentre lo si sviluppa, non dopo per renderlo “bello”
Il numero di spazi è sempre multiplo di un valore scelto come base (in genere 3 o 4)
Normalmente si può definire il tasto Tab in modo che introduca quel numero di spazi
3
L’indentazione del codice L’indentazione esprime “dipendenza”,
“controllo”: l’istruzione non indentata che precede il blocco controlla il blocco indentato
Esempio:for (i=0; i<10; i++){
}Le due istruzioni scanf e printf sono controllate dall’istruzione for precedente, la printf non è controllata dalla scanf e per questo è allo stesso livello (non è indentata)
Istruzione non indentata
Blocco indentato
scanf("%d", &a);printf("%d\n", a*2);
4
L’indentazione del codice Esempio:for (i=1; i<100; i++){
}Le istruzioni printf , if e printf del blocco del for sono allo stesso livello, il blocco dell’if è ulteriormente indentato
printf("%d", i);if (i % 7 == 0){
}printf("\n");
printf(" divisibile per 7");cont++;
5
L’indentazione del codice L’indentazione è necessaria anche in assenza
delle parentesi graffe (quando sono opzionali) Esempio:for (i=0; i<10; i++)
L’istruzione printf è controllata dall’istruzione for precedente che a sua volta è controllata dall’istruzione for più esterna
for (j=0; j<20; j++)printf("%d,%d", i, j);
6
L’indentazione del codice Per il posizionamento delle parentesi graffe
che racchiudono il blocco si suggerisce di utilizzare la modalità Kernighan-Ritchie 2a ed.: la graffa di inizio blocco è collocata sotto il primo
carattere della parola chiave che controlla il blocco, in una riga a sé stante
tutte le istruzioni del blocco sono indentate (es. 4 spazi)
la graffa di fine blocco è allineata sotto quella di inizio blocco, in una riga a sé
Ulteriori esempi di indentazione K&R2 corretta e coerente possono essere trovati in tutte le le soluzioni degli esercizi
7
Istruzione nulla In alcuni casi è necessario fornire un corpo
vuoto ad un’istruzione (es. come visto nei cicli) In questi casi si può usare l’istruzione nulla,
ossia il solo carattere ‘;’ Non produce alcuna azione e per chiarezza è
bene sia collocato in una riga a sé stante Conta solo i valori introdotti (il contatore è i):for (i=0; scanf("%d", &v)!=EOF; i++)
;
In alternativa si possono usare una coppia di parentesi graffe {} o un’istruzione continue
8
Magic numbers Per chiarezza, si cerca di evitare/limitare la
presenza di valori numerici nei programmi Si preferisce definire delle costanti Esempio errato (Che rappresenta quel “26”? ):for (i=0; i<26; i++)
printf("%c", 'A'+i);
Esempio corretto:#define LETTERE_ALFAB 26...for (i=0; i<LETTERE_ALFAB; i++)
printf("%c", 'A'+i);
9
Magic numbers Esempio errato (Che rappresenta quell’ “80” ?):int v[80];for (i=0; i<80; i++)
scanf("%d", &v[i]);
Esempio corretto:#define MAXVETT 80...
int v[MAXVETT]; for (i=0; i<MAXVETT; i++)
scanf("%d", &v[i]);MAXVETT è “auto-esplicativo”, inoltre si ha un ulteriore vantaggio: se si vuole cambiare la dimensione del vettore nel programma è sufficiente cambiare questa sola define
10
Uscita dal programma Il programma termina quando viene eseguita
un’istruzione return:return status;
Possono esserci più istruzioni return Nel main la return termina il programma e
passa il valore status al S.O. per informarlo sull’esito dell’esecuzione: il valore 0 indica che il programma ha eseguito
quanto doveva: terminazione con successo valori diversi da 0 indicano che il programma ha
avuto problemi (es. non ha trovato un file): terminazione con malfunzionamento
Includendo <stdlib.h>, per status si possono usare le costanti EXIT_SUCCESS (al posto di 0) ed EXIT_FAILURE (generico ≠ 0)
11
Effetti collaterali Un’espressione può produrre un risultato e/o
dare effetti collaterali (side-effect ) Esempiox = 3 * i++;3*i è un calcolo che produce un valore, mentre i++ ha l’effetto collaterale di incrementare i di 1
Quando tutti i side-effect di un’espressione sono stati effettuati, si dice che si è raggiunto un sequence point
Prima del raggiungimento del sequence point, il valore delle variabili soggette ad side-effect è indefinito e quindi non devono essere usate
12
Effetti collaterali In una stessa espressione quindi NON può
comparire più di una volta una variabile soggetta a side-effect
Il sequence point viene raggiunto solo prima di passare all’istruzione successiva, salvo: dopo l’esecuzione della prima parte delle
espressioni contenenti gli operatori &&, ||, ?: e l'operatore virgola ‘,’
quando vengono valutate le espressioni di while, for, do, if, switch e return
dopo che tutti gli argomenti di una funzione sono stati valutati, ossia subito prima della chiamata alla funzione stessa
13
Effetti collaterali Esempi di errori
x = i++ * i++;l’incremento/decremento postfisso non è garantito venga effettuato subito dopo la valutazione (uso) della variabile: le due i potrebbero essere incrementate solo appena prima di passare all’istruzione successiva (in questo caso xconterrebbe i elevato al quadrato)
x = ++a / --a;In questo caso ci sono problemi di ordine di valutazione: non si sa a priori se l’incremento viene effettuato prima del decremento o viceversa, i risultati possono dunque essere diversi su compilatori diversi
v[i] = i++;
14
Ordine di valutazione Le regole di precedenza tra gli operatori e le
parentesi impongono solo un parziale ordinamento nella valutazione delle espressioni
Ad esempio in un’espressione comex = f() + g() * h();viene calcolata prima la moltiplicazione e poi la somma, ma non è noto in quale ordine le funzioni siano chiamate (e producano i valori)
Per garantire un determinato ordine si usano variabili per mantenere i risultati intermedi
Si può talvolta utilizzare un costrutto che inserisce un sequence point (&&, ‘,’, etc.)
15
Ordine di valutazione Altri esempi di errori:
printf("%d %f\n", ++n, tan(1./n));non si sa se viene prima valutata l’espressione ++ne poi calcolata la funzione tan()o viceversa, quindi non si sa se il valore di n usato in tan() è quello prima o dopo l’incremento. La virgola che li separa non è un operatore, ma un separatore e quindi non garantisce alcun ordine di valutazione
x = sin(1/++x) + cos(1/++x);non è noto se viene calcolata prima sin o prima cos, per cui gli argomenti di sin e cos possono essere diversi utilizzando compilatori diversi
16
Ordine di valutazione Altri esempi di errori:
x = sin(x++) + sin(x++);non è noto quale delle funzioni sin venga calcolata prima; essendo uguali non è importante, ma solo perché la chiamata a funzione inserisce un sequence point e quindi permette a x di essere riutilizzata nella stessa espressione
x = i++ * i++;qui invece, non essendoci funzioni, non viene richiesto alcun sequence point, quindi NON è accettabile
x = v[++i] - v[++i];non si sa quali siano gli indici effettivi delle due v[++i] in quanto non è noto quale dei due venga valutato per primo prima di calcolare la sottrazione
17
Ordine di valutazione Altri esempi di errori:
in questi casi non è noto se venga valutata prima la parte a sinistra o quella a destra dell’uguale, inoltre in una stessa espressione NON può comparire più di una volta una variabile soggetta a side-effect
v[i] = ++i;v[i] = i++;non è noto quale valore di i venga usato in v[i]
v[++i] = ++i;non è noto quale valore di i venga incrementato prima
18
Operatore virgola Unisce due espressioni in un’unica espressione Permette di inserire due espressioni dove
sintatticamente ne è prevista una sola:for (i=0, j=10; i<j ; i++, j--)
L’operatore virgola inserisce un sequence point: l’espressione di destra viene valutata solo dopo che l’espressione di sinistra è stata valutata completamente (side-effect terminati)
Le due espr. possono essere di tipo diverso: il tipo e il valore dell’espressione composta sono
quelli dell’espressione di destra il tipo e il valore dell’espr. di sinistra sono scartati
19
Operatore virgola In un contesto dove la virgola ha il significato
di separatore, per inserire l’operatore virgola si includono questo e suoi operandi tra parentesi: funz(a, (b=1,b+2), c);
funz ha 3 parametri il secondo vale 3, b vale 1 Esempix=2*4, 5*6; x=8, 30 scartato
(la prima espressione è x=2*4, l’altra 5*6) x=(2*4, 5*6); x=30, 8 scartatox=2*4, y=5*6; x=8, y=30x=(2*4, y=5*6); 8 scartato, y=30, x=30x=(y=2*4, 5*6); y vale 8, x = 30
20
Espressione condizionale E’ un’unica espressione che può assumere
due valori in base ad una condizione x = (condizione) ? espr1 : espr2;
equivale a:if (condizione)
x = espr1;else
x = espr2; condizione viene valutata completamente
prima delle expr (inserisce un sequence point), le parentesi sono opzionali ma consigliabili per chiarezza
Viene calcolata una sola delle due expr
21
Espressione condizionale Esempi
Il maggiore tra a e b:x = (a>b)?a:b;
Il valore assoluto di a:x = (a>0)?a:-a;
Il plurale di una parolaprintf("%d oggett%c\n",
k, (k==1)?'o':'i');
22
Espressioni condizionali Il tipo del risultato è sempre il più “capiente”
tra quelli prodotti dalle due expr(flag == 1) ? 11 : 12.0;restituisce sempre un valore double perché 12.0 è un double mentre 11 un int (quindi restituisce 11.0 quando flag vale 1)
Ha associatività da destra a sinistra:x=(a>b && a>c) ? a : (b>c)? b:c;equivale a:x=(a>b && a>c) ? a : ((b>c)? b:c);
Un espressione condizionale non produce unL-value quindi non si può scrivere:(a>b)?a:b = 12;
23
Precedenza e associatività() [] -> . SD! ~ ++ –– + – * & (cast ) sizeof SD* / % SD+ - (somma e sottrazione) SD<< >> SD< <= > >= SD== != SD& SD^ SD | SD && SD|| SD ?: SD= += –= *= /= %= &= ^= |= <<= >>= SD, SD SD: da Sinistra a Destra, SD: da Dst. a Sin.
24
Big-endian e little-endian Indicano l'ordine in cui una sequenza di byte
è memorizzata nella memoria big-endian rappresenta un ordine in cui
"big end" (il valore più significativo nella sequenza) è memorizzato ad un indirizzo di memorizzazione più basso
little-endian è un ordine in cui "little end" (il valore meno significativo della sequenza) è memorizzato ad un indirizzo più basso
Esempio: in un computer big-endian il num. esadecimale 2F82 è memorizzato con 2F all’indirizzo più basso e 82 in quello più alto
25
Big-endian e little-endian Nel formato little-endian alcuni calcoli sono
più semplici e veloci (quando un numero aumenta, si aggiungono byte per le cifre più significative nella parte alta della memoria)
I processori Intel sono little-endian Il TCP/IP memorizza i dati come big-endian
(detto per questo anche network order) I processori Motorola sono big-endian I mainframe IBM e i supercomputer Cray sono
big-endian
26
La funzione system Esegue un comando di shell E’ definita in <stdlib.h> Sintassisystem(stringa_con_comando);
EsempioPulisce lo schermosystem("CLS"); DOS/Windowssystem("clear"); Unix/Linux
EsempioSospende l’esecuzione di un programmasystem("pause"); DOS/Windows
27
Operatori bitwise Operano su valori interi (con o senza segno) a
livello dei singoli bit:~ complemento a 1 ~x& AND bit a bit x & y| OR bit a bit x | y^ EXOR bit a bit x ^ y<< SHIFT a sinistra x << 2>> SHIFT a destra x >> 2
Di solito si applicano a valori senza segno Hanno priorità inferiore agli oper. matematici Il risultato viene sempre convertito secondo le
regole delle promozioni integrali (ad esempio, anche se x è di tipo short, ~x è di tipo int)
28
Operatori bitwise ~ << >> L’operatore ~ inverte tutti i bit:~0 dà un valore int pari a 11..11 (–1 in CA2)
Gli operatori << e >> attuano uno shift del numero di posizioni indicato dall’operando di destra: y = x << 2;
Il numero di posizioni deve essere maggiore o uguale a zero e strettamente minore del numero di bit dell’operando di sinistra
L’operatore << aggiunge sempre bit 0 a destra L’operatore >> aggiunge bit 0 a sinistra solo se x è unsigned; se è signed non è definito
Questi operatori hanno forma di assegnamento abbreviata: &= ^= |= <<= >>=
29
Operatore bitwise & z = x & y;
Ciascuno dei bit di z viene determinato calcolando l’AND dei corrispondenti bit di x e y
L’operatore & viene spesso usato per azzerare (“unset” o “clear”) alcuni dei bit di un valore dato (x) lasciando invariati gli altri
Per specificare quali bit debbano essere azzerati si predispone una maschera da mettere in AND bit a bit con x
30
Operatore bitwise & La maschera è un numero intero, i bit del
numero dato x in corrispondenza di bit a 0 della maschera vengono azzerati (mascherati, non passano attraverso la mashera), mentre quelli in corrispondenza di bit a 1 restano invariati (passano attraverso la maschera)
Esempioy = x & 015;valore: x =2610 0..0110102maschera: 158 0..0011012risultato: y 0..0010002
31
Operatore bitwise | z = x | y; Ciascuno dei bit di z viene determinato
calcolando l’OR dei corrispondenti bit di x e y L’operatore | viene spesso usato per
impostare a 1 (“set”) alcuni dei bit di un valore dato (x) lasciando invariati gli altri
Per specificare quali bit debbano essere impostati a 1 si predispone una maschera da mettere in OR bit a bit con x
32
Operatore bitwise | La maschera è un numero intero, i bit del
numero dato x in corrispondenza di bit a 1 della maschera vengono impostati a 1, mentre quelli in corrispondenza di bit a 0 restano invariati
Esempioy = x & 012;valore: x =2210 0..0101102maschera: 128 0..0011002risultato: y 0..0111102
33
Operatore bitwise ^ z = x ^ y; Ciascuno dei bit di z viene determinato
calcolando l’EXOR dei corrispondenti bit di x e y
L’operatore ^ viene spesso usato per invertire alcuni dei bit di un valore dato (x) lasciando invariati gli altri
Per specificare quali bit debbano essere invertiti si predispone una maschera da mettere in EXOR bit a bit con x
34
Operatore bitwise ^ La maschera è un numero intero, i bit del
numero dato x in corrispondenza di bit a 1 della maschera vengono invertiti, mentre quelli in corrispondenza di bit a 0 restano invariati
Esempioy = x & 016;valore: x =2210 0..0101102maschera: 168 0..0011102risultato: y 0..0110002
35
Operatori bitwise e logici Quando le espressioni collegate dagli
operatori hanno solo valori 0 e 1, si potrebbero utilizzare gli operatori bitwise al posto degli operatori logici, ma: la valutazione delle espressioni logiche non si
ferma non appena il valore del risultato è individuabile
non c’è la garanzia di esecuzione da sinistra a destra degli operatori
Potrebbe invece essere utile utilizzare l’operatore bitwise EXOR in quanto non esiste il corrispondente operatore logico
36
Esercizi 1. Determinare di quanti bit è composto un tipo char, short, int e long contando i bit.
2. Visualizzare in binario il valore intero (decimale con segno) dato in input.
3. Scrivere un programma che chieda un valore intero decimale, lo visualizzi in binario e calcoli quanti sono i bit pari a 1 che contiene.
4. Scrivere un programma che chieda un valore decimale senza segno (da collocare in una variabile unsigned int) e ruoti i bit di nposizioni a sinistra o a destra a richiesta (“ruotare” i bit significa che quelli che escono da una parte entrano dall’altra).
37
Homework 3-4 Metodo di ordinamento Radix Sort Per ordinare numeri interi positivi Lavora facendo riordinamenti parziali prima
sulle unità, poi sulle decine, poi sulle centinaia, ecc.
Esempio Dati i valori: 7, 12, 9, 25, 36, 11, 20, 4 Scriverli utilizzando per tutti lo stesso numero di
cifre: 07, 12, 09, 25, 36, 11, 20, 04 Definire una matrice avente per righe i valori delle
cifre e per colonne i numeri dati Collocare i numeri sulla riga corrispondente alla
cifra delle unità, ma nella stessa colonna
38
Homework 3-4Collocamento dei valori in base alla cifra unità
07 12 09 25 36 11 20 040 201 112 1234 045 256 367 0789 09
39
Homework 3-4 Raccogliere i valori riga per riga e ricollocarli in
base alla cifra decine20 11 12 04 25 36 07 09
0 04 07 091 11 122 20 253 364 ................................ Raccoglierli nuovamente riga per riga (se ci fossero
più cifre l’operazione andrebbe ripetuta sulle centinaia, sulle migliaia, etc.)
04 07 09 11 12 20 25 36
40
Homework 3Si scriva un programma che realizzi il metodo di ordinamento Radix Sort di valori decimali (max 100 valori), considerando le singole cifre decimali come visto nell’esempio, i valori vengano assegnati in decimale mediante scanf.
41
Homework 4Scrivere un programma che realizzi il metodo di ordinamento Radix Sort di valori decimali (max 100 valori), considerando le singole cifre binariedel valore binario equivalente (utilizzare gli operatori bitwise sulle variabili), i valori vengano assegnati in decimale mediante scanf.