Introduzione al C++
Tipi integrali, conversioni di tipo,
costanti ed espressioni costanti,
direttive al preprocessore, inclusione
condizionale
Il concetto di tipo
• Determina i valori che può assumere una variabile e le operazioni
ammissibili su di essa
• Determina la memoria minima necessaria per memorizzare una
variabile
• int: di solito una parola; float: di solito due parole

• Permette al compilatore di rilevare errori nell'uso delle variabili
– un linguaggio è fortemente tipizzato se garantisce:
• correttezza delle istruzioni rispetto al tipo degli operandi, verificabile dal
compilatore
• che non sorgano errori di tipo in esecuzione

• Il C++ persegue la tipizzazione forte
– però è al tempo stesso molto permissivo, poiché ammette molte
conversioni automatiche tra tipi diversi
2
Il sistema dei tipi del C++
• Uno degli obiettivi principali del C++ è
permettere al programmatore di creare tipi
personalizzati e usarli come se fossero offerti
dal linguaggio
• Il sistema dei tipi del C++ comprende
– Tipi predefiniti (predefined)
– Tipi composti (composite)
– Tipi definiti dall’utente (user defined)
– Tipi classe (class types)
Cosa c’è da sapere di ogni tipo
• Che valori ammette
• Come sono rappresentati i valori nel calcolatore
– Sarebbe bello ignorarlo, ma talvolta non si può

• Come rappresentare valori (costanti) di quel tipo nel
programma (anche detti "literal")
• Come dichiarare e inizializzare variabili di quel tipo
• Quali operazioni sono ammesse sulle variabili
• Quali tipi sono “affini”
– Sono definite conversioni automatiche tra tipi affini
Inizializzazione delle variabili
• Le variabili si dichiarano e inizializzano
– La dichiarazione definisce il tipo
– L’inizializzazione definisce il valore iniziale della
variabile neonata
– L’inizializzazione NON è l’assegnamento
• quest’ultimo definisce il valore corrente della variabile
• Alcune cose che si possono fare durante
l’inizializzazione sono vietate nell’assegnamento

int i = 0;
tipo

identificatore inizializzatore
Inizializzazione di default
(tipi predefiniti)
• Una variabile dei tipi predefiniti priva di inizializzatore
viene inizializzata per default
• Il valore di default dipende dal tipo della variabile e
dalla posizione della dichiarazione
– Variabili dei tipi predefiniti definite al di fuori di una
funzione ricevono per default il valore 0
– Variabili dei tipi predefiniti definite all'interno di una
funzione sono uninitialized, cioè il valore NON è definito

• NB: per le variabili di tipo classe, il valore di
inizializzazione per default può essere specificato dal
programmatore
Check this out!
• http://stackoverflow.com/questions/18870547/why-there-isno-difference-between-initialization-and-assignment-for-thebuild-im/articles/article.aspx?p=376876
I tipi predefiniti del C++
• I tipi aritmetici: integral e floating point
Il tipo bool
• Sta per Boolean (booleano)
• Rappresentare il tipo delle espressioni che il
programma usa per prendere decisioni (se…,
fintantoché…)
• Ammette solo due valori: true e false

– false è rappresentato tramite il valore numerico 0
– true è rappresentato da qualunque valore numerico
diverso da 0
bool
bool
bool
bool

b
b
b
b

=
=
=
=

true; // b vale true
23; // b vale true
false; // b vale false
0; // b vale false

• In altre parole bool è un tipo (affine a) intero!
Il tipo char
• Ammette come valore un singolo carattere
• Utilizza il minimo numero di bit necessario a rappresentare
l’insieme dei caratteri standard del calcolatore un byte (di
solito 8 bit per rappresentare i caratteri ASCII)
• I caratteri sono rappresentati internamente da numeri interi e
quindi tra essi è definito un ordinamento
– Esempio: il carattere '1' è rappresentato dall’intero 49, il carattere
'A' dall’intero 65, …
• quindi, nell’ordinamento, risulta '1' < 'A'

– Per le lettere è scelta una codifica tale per cui l’ordinamento dei codici
coincide con l’usuale ordinamento alfabetico: 'A'<'B' ecc.
• Lettere alfabeticamente adiacenti hanno codifiche adiacenti

– Negli insiemi più usati le maiuscole latine sono codificate “prima”
delle minuscole
• Quindi ad esempio 'A'<'a' ma anche 'Z'<'a'
10
Il concetto di insieme di caratteri
• A differenza dei numeri
interi, i caratteri non hanno
una rappresentazione
“naturale” nel calcolatore
• La rappresentazione è
convenzionale e si ottiene
“numerando” i simboli
dell’insieme dei caratteri
che si vuole rappresentare
• Esistono molti insiemi di
caratteri, che coprono un
numero differente di
simboli di lingue diverse

• Gli insiemi di caratteri e
le codifiche più usati
sono
– ASCII
– ISO/IEC 8859
– UNICODE con UTF-8 e
UTF-16
Why character encoding matters

http://en.wikipedia.org/wiki/Mojibake
Insieme di caratteri ASCII
• Lo American Standard Code for Information
Interchange (ASCII ) è una codifica dei caratteri basata
sull’alfabeto inglese che copre 27= 128 caratteri
• Usa 7-bit
• 95 caratteri stampabili: i numeri 0-9, le lettere a-z e AZ, simboli di punteggiatura e lo spazio bianco
• Vari caratteri non stampabili, tra cui alcuni codici di
controllo derivanti dalle Teletype machines
ASCII Table
ISO 8859 (ISO LATIN)
•
•

•
•

I 95 caratteri ASCII stampabili
sono sufficienti per lo scambio di
informazioni in lingua inglese
Molte altre lingue che usano
l'alfabeto latino hanno bisogno di
simboli addizionali, come la ß
(tedesco), la ñ (spagnolo), la å
(svedese e altre lingue
scandinave) o le lettere accentate
italiane (à, è, é, ì, ò, ù)
L'ISO 8859 utilizza l'ottavo bit del
byte, che permette la codifica di
altri 128 caratteri
ISO 8859 è organizzato in diverse
parti: es: ISO 8859-1, Latin-1
Western European

http://en.wikipedia.org/wiki/ISO/IEC_8859
UNICODE, UTF-8, UTF-16
•
•

•

Unicode è un insieme di caratteri
e una sistema di codifica che
comprende oltre 110.000 simboli
Unicode si sovrappone a ISO/IEC
8859-1, ma codifica inoltre i
caratteri di quasi tutte le lingue
vive e in alcune lingue morte,
nonché simboli matematici e
chimici, cartografici, l'alfabeto
Braille, ideogrammi ecc.
I caratteri Unicode sono
rappresentati con le codifiche
UTF-8 e UTF-16

•

•

UTF-8 (8-bit Unicode
Transformation Format) è una
codifica dei caratteri Unicode

– usa un byte per i caratteri
dell’insieme ASCII, che hanno lo
stesso codice sia UTF-8 sia in
ASCII, e fino a 4 bytes per gli altri
caratteri (codifica a lunghezza
variabile)

UTF-16 (16-bit Unicode
Transformation Format) è una
codifica dei caratteri Unicode

– può rappresentare fino a
1,112,064 entità (code points) tra
cui i caratteri UNICODE da 0 a
0x10FFFF
– La codifica è a lunghezza
variabile, con uno o due unità da
16 bit
I tipi int
• Approssima il dominio degli interi
• Normalmente utilizza 1 parola di memoria
• Esistono anche le varianti
– short int, long int e long long int
– tipico: long 32 bit, long long 64 bit, short 16 bit,
e int 16 o 32 (per cui spesso int è grande quanto long
int)

• Lo standard del C++ richiede che
– spazio per short <= spazio per int <= spazio per long
<= spazio per long long
La gestione del segno
• TUTTI i tipi integral, tranne bool, ammettono la
versione “senza segno” (unsigned)
• Il tipo normale permette di rappresentare numeri
interi positivi e negativi
• Il tipo unsigned permette di rappresentare solo
numeri interi positivi, ma utilizza un bit in più (e
quindi può rappresentare di più valori)

– unsigned int: un valore intero positivo di almeno 16
bit, compreso tra 0 e 216-1
– Il prefisso signed si può omettere
– unsigned èquivale a unsigned int
– unsigned char  da 0 a 255
Rapporto tra tipi signed e unsigned
• Tipi signed e unsigned non andrebbero mescolati
• Se si assegna un valore fuori intervallo a una variabile
di tipo unsigned, il risultato è definito come:
– Il resto della divisione del valore per il massimo intero che
il tipo unsigned può rappresentare

• Per esempio, un tipo unsigned char da 8-bit
rappresenta valori da 0 a 255.
• L’assegnamento di un valore fuori intervallo dà come
esito il resto di tale valore diviso per 256
– Assegnare –1 a un unsigned char da 8 bit produce il valore
255!
Check this out
int main() { // Assegna a unsigned valore negativo
unsigned char c;
// va da 0 a 255;
cout << "Inserire un numero negativo: " << endl;
int i;
cin >> i;
cout << "Valore inserito (intero originale): " << i << endl;
c=i; // assegno numero negativo a variabile char
cout << "Valore inserito (char): " << c << endl;
i=c; // recupero il valore intero di c
cout << "Valore inserito (intero dopo la conversione a char): "
<< i << endl;
return 0;
}

• Il compilatore non può rilevare l’errore
• Non sa predire che valore inserisce l’utente
• NB:
Rapporto tra char e int
• In molti linguaggi di programmazione, char e int sono
tipi non solo diversi, ma incompatibili
• C e C++ considerano i caratteri come “una particolare
interpretazione” dei numeri interi
• La convertibilità tra char e int può portare a
programmi difficili da comprendere
• Usare char solo quando serve davvero una variabile
che contenga un carattere, per esempio per
– Cercare/contare/sostituire tutte le occorrenze di un
carattere in un testo
Da char a int
• Determinare il codice di una lettera minuscola
• Cosa serve
– Una variabile char
– Una variabile int

• Algoritmo

1. Leggi il prossimo carattere
2. Se è una lettera maiuscola
1.
2.
3.

Convertilo in un intero
Stampa il risultato della conversione
Torna al punto uno

3. Termina
Da char a int
int main() {
// Legge un carattere e stampa il codice ASCII
char c;
// se è una minuscola; altrimenti termina
int i;
cout << "Scrivi un car. minuscolo (maiusc. x finire)" << endl;
cin >> c;
while (c >= 'a' && c <= 'z') { // minuscolo?
i = c; // assegna il valore del carattere a un intero
cout << "Valore ASCII per " << c << " è" << i << endl;
cout << "Scrivi car. minuscolo (maiusc. x finire)" << endl;
cin >> c;
}
return 0; // termina
}

NB: notare la ripetizione delle istruzioni che richiedono
l'input
Iterazione e condizione
• while (c >= 'a' && c <= 'z') {..
Corpo del ciclo..}
• Se la condizione è vera

• esegue le istruzioni all’interno del corpo

• Quando la condizione è falsa
• termina il ciclo (esce dal ciclo)

• (c >= 'a' && c <= 'z')
•
•
•
•

Esprime la condizione di permanenza nel ciclo
E’ un’espressione con valore di tipo bool, cioè vero o falso
&& Operatore logico AND
‘a’ è un valore costante (literal) di tipo carattere
Da int a char
int main() {
// Stampa il codice carattere
char c;
// corrispondente ai 128 codici ASCII
for (int i=0; i<128; ++i) {
c = i; // assegna il valore intero al carattere
cout << "Il carattere corrispondente a " << i
<< " è: " << c << endl;
}
return 0;
}
Iterazione a conteggio
• for (int i=0; i<128; ++i)
• Quando una iterazione deve essere svolta un
numero preciso di volte si può usare un “ciclo for”
• Il ciclo incrementa una variabile apposita (contatore)
per tener traccia del numero di interazioni
– int i=0; dichiarazione del contatore
– i<128; condizione di permanenza
– ++i avanzamento del contatore
Ciclo while e ciclo for
char c;
for (int i=0; i<128; i++) {
c = i;
cout << “Il carattere
corrispondente a " << i
<< " è: " << c << endl;
}

int i = 0;
char c;
while (i<128) {
c = i;
cout << “Il carattere
corrispondente a " << i
<< " è: " << c << endl;
i++;
}
I tipi float e double
• Approssimano i numeri razionali
• I valori si possono rappresentare con due notazioni
– 315.779
– 3.73E-5

• double di solito occupa più memoria di float
– tipicamente 4 byte per float e 8 byte per double
• (...esistono anche i long double, che devono occupare almeno
tanto spazio quanto i double)

• Lo standard prescrive la precisione minima, cioè il
minimo numero di cifre significative
28
Come rappresentare valori dei tipi
predefiniti

• Il valore 10 ha un tipo diverso da 10.0
• 10L è un valore di tipo long int
Cautele
• Attenzione ai confronti tra float
float a, b;
...
if (a == b) ...

• non ha senso, a causa delle approssimazioni nella
memorizzazione!!
diff = A - B;
if ((diff < EPSILON) && (-diff <
EPSILON));

• EPSILON costante definita in std::numeric_limits
Check this out
• http://stackoverflow.com/questions/17333/m
ost-effective-way-for-float-and-doublecomparison
Quali tipi scegliere
• Usare tipi unsigned quando si è certi che servono
solo valori positivi
• Usare int per valori aritmentici (short di solito
non basta)
• Se servono valori molto grandi usare long long
• Non usare char e bool in espressioni aritmentiche
– Se proprio proprio serve un intero piccolo usare signed
char o unsigned char

• Usare double per le operazioni in virgola mobile (di
solito float ha precisione insufficiente e le
operazioni sui double sono comunque veloci)
Conversioni di tipo
• Se un operando non è del tipo atteso e il compilatore
non ha informazioni sul tipo desiderato interviene
una conversione automatica di tipo
– È detta anche cast implicito
– Si opera la conversione anziché segnalare l'errore
• A volte è accompagnata da un “avvertimento” (warning)

• Esempio
– int i; float f;
– la valutazione dell’espressione i + f effettua prima la
conversione di i in float e poi la somma
La "scaletta" delle conversioni
Conversioni di tipo: esempi
int n, m;

float x,y;

n = 3.541 + 3; // il risultato è 6!
x = n;
x = n +
n = x;
n = n /
n = n /
x = n /
x = n /

n da “int” è promosso a “float”
x; // n è promosso a “float” e poi sommato a x
// x è troncato, ne sopravvive la parte intera
m; // il risultato della divisione tra interi è intero
x; /* n è convertito in “float”, poi si esegue la
divisione, il risultato è troncato a int */
x; //come sopra ma il risultato resta float
m; // attenzione: la divisione tra int tronca
//
Conversioni con il tipo bool
• Il tipo bool ha valore true o false
• I valori interi vengono convertiti a bool come segue
– 0 viene convertito a false
– ogni altro valore intero viene convertito a true

• Vale anche la conversione inversa (promozione a int)
bool b=false; int i; // inizializzazione
i = b; // i vale 0
b=true;
i = b; // i ora vale 1!
Conversioni con tipi un/signed
• Ecco perché è meglio non mescolare
unsigned u = 10;
int i = -42;
cout << i + i << endl; // prints -84
cout << u + i << endl; // if 32-bit ints, prints
4294967264

• Il valore di i (-42) viene convertito a unsigned
prima dell’addizione
unsigned u1 = 42, u2 = 10;
cout << u1 - u2 << endl; // ok: result is 32
cout << u2 - u1 << endl; // ok: but the result is… ?
Quando avvengono le conversioni
automatiche
• Nelle espressioni, i valori di tipi aritmetici piccoli vengono
solitamente promossi a int
char c, int i;
i=c+i; // da evitare

• Nelle condizioni, tipi non bool sono convertiti a bool
– while (33) equivale a while (true) !

• Nell’inizializzazione, il valore dell’inizializzatore è convertito al
tipo della variabile inizializzata
– int i = 3.14; // i inizializzato a 3 !

• Nelle espressioni aritmetiche, gli operando sono convertiti a
un tipo comune
• Nelle chiamate di funzione (che vedremo)
Altri esempi
bool b = 42; // b is true
int i = b; // i has value 1
i = 3.14; // i has value 3
double pi = i; // pi has value 3.0
// assuming 8-bit chars
unsigned char c = -1; // c has value 255
Conversione (cast) esplicita
• Quando una conversione utile non verrebbe
eseguita, la si può richiedere esplicitamente
• Es: divisione tra interi con risultato double
x = (double) n / m;

• ES: troncamento esplicito del risultato
cout << (int) x / y;

• C++11 introduce una sintassi più precisa
ATTENZIONE
• Il cast (implicito o esplicito che sia) non modifica il
tipo della variabile o delle variabili coinvolte, ma solo
il tipo associato al valore dell'espressione
• Le variabili in memoria continuano a essere del tipo
dichiarato staticamente nella parte dichiarativa del
programma
• Il cambio del tipo della variabile può portare al
cambio del valore dell’espressione dove la variabile è
usata
Le espressioni
•

Espressione: applicazione di operatori a
operandi
– Ha un valore e un tipo
– Condizione: espressione con tipo booleano

•

Cosa bisogna sapere sulle espressioni
1. Ordine di valutazione
2. Proprietà degli operandi (precedenza,
associatività)
3. Tipo degli operatori e del risultato, conversioni
Espressioni base
Le espressioni base si compongono di:
• Variabili
– Il tipo è noto in base alla dichiarazione, e NON cambia

• Valori (detti anche costanti o literal)
– Il tipo è deducibile da come sono “scritte”
•
•
•
•
•
•
•

3 : int (il più piccolo tra int, long, long long in cui il valore ci sta)
03 : int in notazione ottale
0x3 : int in notazione esadecimale
3.0 : float
3.0L : long
'3' : char
"3" : stringa (sequenza di caratteri di lunghezza variabile)
Constanti di tipo stringa
"Hello World!" // string literal
•Ha un tipo composto che vedremo in seguito
– Array di const char

•Il compilatore appende al valore un carattere
speciale "terminatore" ('0')
•La dimensione reale del valore è 1 + la dimensione
apparente (c'è anche il carattere "terminatore")
•Attenzione: "a" ha tipo e dimensione diversi da 'a'
Espressioni e operatori
• Operatori unari: sia applicano a un operando
– -2

• Operatori binari: sia applicano a due operandi
– a+b

• Il valore di un'espressione dipende da
precedenza e associatività degli operatori e può
dipendere dall'ordine di valutazione delle sottoespressioni
– Quanto vale : 5 + 10 * 20/2 ?

• Nella valutazione intervengono anche le
conversioni di tipo
Precedenza e associatività
•
•
•

•
•

Un'espressione con due o più operatori di dice composta
La sua valutazione richiede di assegnare gli operandi agli operatori
Si usano le regole di precedenza tra operatori diversi e le regole di
associatività per operatori con la stessa precedenza
– Precedenza: 3+4*5 vale 23, non 35
– Associatività: 20-15-3 vale 2, non 8 (associatività a sinistra)

Le parentesi possono cambiare le regole
– (3+4)*5 vale 35
– 20-(15-3) vale 8

Non solo operatori aritmetici:
–
–
–
–

cin >> v1 >> v2; // read into v1 and then into v2
L'operatore >> è associativo a sinistra
Il valore dell'espressione cin >> v1 è cin !
Equivale a (cin >> v1) >> v2;
Ordine di valutazione operandi
• In generale l'ordine di valutazione di sotto-espressioni con
la stessa priorità NON è definito
– int i = f1() + f2(); // f1, f2 sono funzioni

• Non si sa quale tra le funzioni f1() e f2() venga valutata per
prima
• Non si devono fare ipotesi indebite
– int i = 0;
– cout << i << " " << ++i << endl; // undefined
– ++i espressione che incrementa i e "torna" il nuovo valore

– Potrebbe stampare 1 1 oppure 0 1 o qualsiasi altra cosa

• Ci sono 4 operatori per cui invece è stabilito l'ordine di
valutazione delle sotto-espressioni

– AND logico (&&) OR logico (||) operatore condizionale (? :),
operatore virgola (,)
Operatori aritmetici
• Precedenza e associatività (tutti associativi a sinistra)
• PS: % indica il resto delle divisione tra due interi
– Se m e n sono interi e n è diverso da 0,
• (m/n)*n + m%n = m

– Non si applica a tipi in virgola mobile
precedenza
Operatori logici e relazionali
• Sono usati per costruire condizioni, cioè espressioni
con valore di tipo bool

precedenza
La regola del cortocircuito
• Gli operatori AND e OR in C e C++ hanno un ordine di
valutazione prefissato (a differenza delle pure
espressioni della logica)
– Prima l’operando di sinistra POI quello di destra

• Vale una regola detta del cortocircuito che evita di
valutare inutilmente l’operando di destra
– AND: se l’operando di sinistra vale false
– OR: se l’operando di sinistra vale true

• La regola del corto circuito ha applicazioni
interessanti, per esempio quando si ispezionano
sequenze di dati (lo vedremo in seguito)
L’operatore di assegnamento
• Anche = è un operatore!
• L’operando di sinistra deve essere una variabile (più
precisamente un lvalue), quello di destra un’espressione (più
precisamente un rvalue)
• Modifica lo stato del programma: assegna il valore
dell’operando di destra all’operando di sinistra
–
–
–
–

i=3 è un’espressione, il cui valore è l’operando di sinistra, cioè i!
i=3; è un’istruzione! Per via del ;
L’operatore = è associativo a destra
x=y=3 equivale a x=(y=3)

• Si assegna il valore 3 a y, il risultato dell’espressione è y
• Si effettua l’assegnamento x=y che copia il valore di y in x, anche x vale 3

• L’inizializzazione NON è un assegnamento (mai ribadito
abbastanza)
Esempi
int i = 0, j = 0, k = 0; // initializations, not
// assignment
1024 = k; // error: literals are rvalues
i + j = k; // error: arithmetic expressions are rvalues
k = 0; // result: type int, value 0
k = 3.14159; // result: type int, value 3
ATTENZIONE
• Possiamo usare assegnamenti come condizioni e viceversa?
• Si, perché = è un operatore, proprio come ==
• Di solito però… succede quando si sta commettendo un
errore...!
– x = y == 3;
– while ( 3 = x )

• Ma soprattutto...

// Assegna a x il valore 0 o 1 e non modifica
// Errore di sintassi (3 non è una variabile)

– while(x = 3) ... È SEMPRE VERO!!! (per ogni valore precedente di x)
– while (x = 0) ... È SEMPRE FALSO!!! (per ogni valore precedente di x)
– while (x = y) ... equivale a scrivere
• x = y;
• while ( y != 0 ) ...

• Ancora peggio...

– while (! (x = 0)) ... NON TERMINA MAI !!!!
I costruttori di tipi
• A partire dai tipi predefiniti si possono creare
nuovi tipi, che aggiungono proprietà speciali ai
tipi predefiniti
–
–
–
–

const
reference
pointer
array

• A questi si aggiungono costruttori di tipi definiti
interamente dall'utente
– class
– struct (eredità del C, poco usato in C++)
Il qualificatore const
• Talora serve definire una variabile che non cambi valore
• Per farlo si premette const alla dichiarazione

– const int modelloauto = 159; // tipo const int
– const float pigreco = 3.14159265; // tipo const float

• Un tipo const è diverso dal corrispondente tipo base
• Una variabile const
– Si dichiara e inizializza obbligatoriamente
– Non ammette assegnamento
•
•
•
•
•

int i = 42;
const int ci = i; // ok: the value in i is copied into ci
ci = 43; // error, no assignment
const int k; // error: k is uninitialized const
int j = ci; // ok: the value in ci is copied into j
Visibilità delle variabili const
• const float pigreco = 3.14159265;

• Il compilatore sostituisce pigreco con il valore
associato
• Il compilatore deve conoscere il valore
dell'inizializzatore
• Se il programma è diviso in più file, la variabile
const viene (ri)definita in ciascun file
• Alternativamente, la variabile const può essere
definita in un solo file e dichiarata come esterna
nei file che ne fanno uso
extern const
• Per definire una variabile const una sola volta, si usa la keyword
extern sia nella definizione che nelle dichiarazioni
// file_1.cc definisce e inizializza una variabile const
extern const float pigreco = 3.14159265;
// file_1.h
extern const float pigreco; // stessa const di file_1.cc

• file_1.cc definisce e inizializza pigreco. Siccome la dichiarazione
comprende un inizializzatore è una definizione. Però, siccome
pigreco è const, si specifica extern per farla usare in altri file.
• La dichiarazione in file_1.h è anch'essa extern. Significa che pigreco
non è locale a tale file ma definita altrove
• Altri file possono includere il file file_1.h e importare la definizione
di pigreco
Definizione di macro: #define
• Esiste anche un altro modo di definire un valore una volta per tutte
– la direttiva #define

• #define PIGRECO 3.141592

– #define è una direttiva al preprocessore (come #include)
– Non è terminata dal punto e virgola
• una direttiva al precompilatore non è un’istruzione C++

– Non causa allocazione di memoria

• PIGRECO è una "costante simbolica"

– Il simbolo PIGRECO viene sostituito nel codice con il valore 3.14 …
prima che il programma sia compilato
– Si dice che PIGRECO è una macro-definizione (o semplicemente una
MACRO)
– Per convenzione, le costanti definite tramite macro sono interamente
maiuscole (es: TIPICA_COSTANTE)
Definizione di macro: #define
• #define MIGLIO 1609.34
• #define YARDA
0.9144
• Si possono costruire MACRO a partire da altre
MACRO, e una macro può anche usare uno o
più parametri:
– #define AREACERCHIO(X) (PIGRECO∗(X)∗(X))
– area = AREACERCHIO(4);
– Diventa ⇒ area = (3.141592∗(4)∗(4));
– ma è un valore fisso !!
Altre direttive al precompilatore
• Abbiamo già visto:
#include <nome_libreria>
– Serve a richiamare librerie
– comanda al preprocessore di leggere anche da un altro file
sorgente

• Altra direttiva usata frequentemente:
#undef VERO
– Serve ad annullare la definizione di una MACRO
– Da quel punto in poi la costante VERO non è più definita
Esempio di programma multifile
/* File contenente funzioni di utilità sui cerchi
* dichiarazione e definizione di una costante
* riusabile : pigreco.cpp */
extern const float pigreco = 3.14159265;
float area(float r){
return r*r*pigreco;
}
float perimeter(float r){
return 2*r*pigreco;
}
File di intestazione della libreria
/*
*
*
*
*

File contenente solo le dichiarazioni delle
funzioni di utilità sui cerchi
e della costante riusabile: pigreco.h
Usato dal compilatore per controllare la
correttezza del programma */

extern const float pigreco;
float area(float r);
float perimeter(float r);
Programma principale
#include <iostream>
#include "pigreco.h"
using namespace std;
int main() {
float radius;
cout << "Insert the value of the radius: " << endl;
cin >> radius;
cout << "Value of pigreco: " << pigreco << endl;
cout << "Perimeter of circle: " << perimeter(radius) <<
cout << "Area of the circle: " << area(radius) << endl;
return 0;
}

endl;
I tipi definiti dall'utente
• La ragione principale del successo di C++ è che
consente di estendere il sistema dei tipi in
modo assai potente
• La novità introdotta dal C++ è che i tipi definiti
dall'utente si possono usare (quasi) come
quelli predefiniti
Requisiti
• Si vuole costruire un programma che gestisca le
vendite di libri
• Ogni libro è caratterizzato da un codice ISBN
• Il programma deve gestire per ogni libro il numero
totale di copie vendute, il ricavo totale e medio
• L'utente inserisce sequenzialmente i dati di vendita per
i diversi libri, il programma aggrega i dati per ciascun
codice ISBN e stampa il dato aggregato
• Deve quindi essere possibile:
– Inserire e stampare i dati di un libro
– Sommare più dati di vendita relativi allo stesso libro
– Stampare i dati di vendita aggregati relativi a un libro
Progettazione
• Inizializza il totale vendite a 0
• Leggi dati 1° transazione e aggiorna il totale
• Finché ci sono transazioni

– Leggi un'altra transazione;
– Se la transazione letta è dello stesso libro di prima
• sommala al totale corrente;

– Altrimenti

• stampa il totale corrente; // è cambiato il libro
• inizializza il totale corrente del nuovo libro coi dati della
transazione letta;

•

Stampa il totale di vendita dell'ultimo libro (perché
serve??)
Suddivisione del programma
• Un file di libreria (Sales_item.h) conterrà la
definizione del tipo che rappresenta il dato di
vendita: Sales_item
• Sales_item è un tipo classe, cioè un tipo
definito dall'utente che comprende dati e
operazioni relativi al dato di vendita
• Il programma principale (Booksales.cpp)
importa e usa Sales_item come se fosse un
tipo predefinito
– Sales_item item; // dichiara una var. di tipo Sales_item !
Un primo esempio di main()
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item book;
// read ISBN, number of copies sold, and sales price
std::cin >> book;
// write ISBN, # of copies sold, total revenue, and average price
std::cout << book << std::endl;
return 0;
}

•book è trattata come una variabile di un tipo predefinito
•Si applicano gli stessi operatori di I/O che a una variabile di tipo int
•Gli operatori sono definiti nel tipo classe Sales_item
L'esempio completo
int main()
{
Sales_item total; // variable to hold data for the next transaction
// read the first transaction and ensure that there are data to process
if (cin >> total) {
Sales_item trans; // variable to hold the running sum
// read and process the remaining transactions
while (cin >> trans) {
// if we're still processing the same book
if (total.isbn() == trans.isbn())
total += trans; // update the running total
else {
// print results for the previous book
cout << total << endl;
total = trans; // total now refers to the next book
}
}
cout << total << endl; // print the last transaction
} else {
// no input! warn the user
cerr << "No data?!" << endl;
return -1; // indicate failure
}
return 0;
}

if(cin >> x)
while(cin >> x)
La condizione è
soddisfatta se
l'acquisizione dell'input
non ha dato errori
Per terminare, basta
premere CTRL-Z
(carattere end of file)

05 2 integrali-conversioni-costanti-preproc-inclusione

  • 1.
    Introduzione al C++ Tipiintegrali, conversioni di tipo, costanti ed espressioni costanti, direttive al preprocessore, inclusione condizionale
  • 2.
    Il concetto ditipo • Determina i valori che può assumere una variabile e le operazioni ammissibili su di essa • Determina la memoria minima necessaria per memorizzare una variabile • int: di solito una parola; float: di solito due parole • Permette al compilatore di rilevare errori nell'uso delle variabili – un linguaggio è fortemente tipizzato se garantisce: • correttezza delle istruzioni rispetto al tipo degli operandi, verificabile dal compilatore • che non sorgano errori di tipo in esecuzione • Il C++ persegue la tipizzazione forte – però è al tempo stesso molto permissivo, poiché ammette molte conversioni automatiche tra tipi diversi 2
  • 3.
    Il sistema deitipi del C++ • Uno degli obiettivi principali del C++ è permettere al programmatore di creare tipi personalizzati e usarli come se fossero offerti dal linguaggio • Il sistema dei tipi del C++ comprende – Tipi predefiniti (predefined) – Tipi composti (composite) – Tipi definiti dall’utente (user defined) – Tipi classe (class types)
  • 4.
    Cosa c’è dasapere di ogni tipo • Che valori ammette • Come sono rappresentati i valori nel calcolatore – Sarebbe bello ignorarlo, ma talvolta non si può • Come rappresentare valori (costanti) di quel tipo nel programma (anche detti "literal") • Come dichiarare e inizializzare variabili di quel tipo • Quali operazioni sono ammesse sulle variabili • Quali tipi sono “affini” – Sono definite conversioni automatiche tra tipi affini
  • 5.
    Inizializzazione delle variabili •Le variabili si dichiarano e inizializzano – La dichiarazione definisce il tipo – L’inizializzazione definisce il valore iniziale della variabile neonata – L’inizializzazione NON è l’assegnamento • quest’ultimo definisce il valore corrente della variabile • Alcune cose che si possono fare durante l’inizializzazione sono vietate nell’assegnamento int i = 0; tipo identificatore inizializzatore
  • 6.
    Inizializzazione di default (tipipredefiniti) • Una variabile dei tipi predefiniti priva di inizializzatore viene inizializzata per default • Il valore di default dipende dal tipo della variabile e dalla posizione della dichiarazione – Variabili dei tipi predefiniti definite al di fuori di una funzione ricevono per default il valore 0 – Variabili dei tipi predefiniti definite all'interno di una funzione sono uninitialized, cioè il valore NON è definito • NB: per le variabili di tipo classe, il valore di inizializzazione per default può essere specificato dal programmatore
  • 7.
    Check this out! •http://stackoverflow.com/questions/18870547/why-there-isno-difference-between-initialization-and-assignment-for-thebuild-im/articles/article.aspx?p=376876
  • 8.
    I tipi predefinitidel C++ • I tipi aritmetici: integral e floating point
  • 9.
    Il tipo bool •Sta per Boolean (booleano) • Rappresentare il tipo delle espressioni che il programma usa per prendere decisioni (se…, fintantoché…) • Ammette solo due valori: true e false – false è rappresentato tramite il valore numerico 0 – true è rappresentato da qualunque valore numerico diverso da 0 bool bool bool bool b b b b = = = = true; // b vale true 23; // b vale true false; // b vale false 0; // b vale false • In altre parole bool è un tipo (affine a) intero!
  • 10.
    Il tipo char •Ammette come valore un singolo carattere • Utilizza il minimo numero di bit necessario a rappresentare l’insieme dei caratteri standard del calcolatore un byte (di solito 8 bit per rappresentare i caratteri ASCII) • I caratteri sono rappresentati internamente da numeri interi e quindi tra essi è definito un ordinamento – Esempio: il carattere '1' è rappresentato dall’intero 49, il carattere 'A' dall’intero 65, … • quindi, nell’ordinamento, risulta '1' < 'A' – Per le lettere è scelta una codifica tale per cui l’ordinamento dei codici coincide con l’usuale ordinamento alfabetico: 'A'<'B' ecc. • Lettere alfabeticamente adiacenti hanno codifiche adiacenti – Negli insiemi più usati le maiuscole latine sono codificate “prima” delle minuscole • Quindi ad esempio 'A'<'a' ma anche 'Z'<'a' 10
  • 11.
    Il concetto diinsieme di caratteri • A differenza dei numeri interi, i caratteri non hanno una rappresentazione “naturale” nel calcolatore • La rappresentazione è convenzionale e si ottiene “numerando” i simboli dell’insieme dei caratteri che si vuole rappresentare • Esistono molti insiemi di caratteri, che coprono un numero differente di simboli di lingue diverse • Gli insiemi di caratteri e le codifiche più usati sono – ASCII – ISO/IEC 8859 – UNICODE con UTF-8 e UTF-16
  • 12.
    Why character encodingmatters http://en.wikipedia.org/wiki/Mojibake
  • 13.
    Insieme di caratteriASCII • Lo American Standard Code for Information Interchange (ASCII ) è una codifica dei caratteri basata sull’alfabeto inglese che copre 27= 128 caratteri • Usa 7-bit • 95 caratteri stampabili: i numeri 0-9, le lettere a-z e AZ, simboli di punteggiatura e lo spazio bianco • Vari caratteri non stampabili, tra cui alcuni codici di controllo derivanti dalle Teletype machines
  • 14.
  • 15.
    ISO 8859 (ISOLATIN) • • • • I 95 caratteri ASCII stampabili sono sufficienti per lo scambio di informazioni in lingua inglese Molte altre lingue che usano l'alfabeto latino hanno bisogno di simboli addizionali, come la ß (tedesco), la ñ (spagnolo), la å (svedese e altre lingue scandinave) o le lettere accentate italiane (à, è, é, ì, ò, ù) L'ISO 8859 utilizza l'ottavo bit del byte, che permette la codifica di altri 128 caratteri ISO 8859 è organizzato in diverse parti: es: ISO 8859-1, Latin-1 Western European http://en.wikipedia.org/wiki/ISO/IEC_8859
  • 16.
    UNICODE, UTF-8, UTF-16 • • • Unicodeè un insieme di caratteri e una sistema di codifica che comprende oltre 110.000 simboli Unicode si sovrappone a ISO/IEC 8859-1, ma codifica inoltre i caratteri di quasi tutte le lingue vive e in alcune lingue morte, nonché simboli matematici e chimici, cartografici, l'alfabeto Braille, ideogrammi ecc. I caratteri Unicode sono rappresentati con le codifiche UTF-8 e UTF-16 • • UTF-8 (8-bit Unicode Transformation Format) è una codifica dei caratteri Unicode – usa un byte per i caratteri dell’insieme ASCII, che hanno lo stesso codice sia UTF-8 sia in ASCII, e fino a 4 bytes per gli altri caratteri (codifica a lunghezza variabile) UTF-16 (16-bit Unicode Transformation Format) è una codifica dei caratteri Unicode – può rappresentare fino a 1,112,064 entità (code points) tra cui i caratteri UNICODE da 0 a 0x10FFFF – La codifica è a lunghezza variabile, con uno o due unità da 16 bit
  • 17.
    I tipi int •Approssima il dominio degli interi • Normalmente utilizza 1 parola di memoria • Esistono anche le varianti – short int, long int e long long int – tipico: long 32 bit, long long 64 bit, short 16 bit, e int 16 o 32 (per cui spesso int è grande quanto long int) • Lo standard del C++ richiede che – spazio per short <= spazio per int <= spazio per long <= spazio per long long
  • 18.
    La gestione delsegno • TUTTI i tipi integral, tranne bool, ammettono la versione “senza segno” (unsigned) • Il tipo normale permette di rappresentare numeri interi positivi e negativi • Il tipo unsigned permette di rappresentare solo numeri interi positivi, ma utilizza un bit in più (e quindi può rappresentare di più valori) – unsigned int: un valore intero positivo di almeno 16 bit, compreso tra 0 e 216-1 – Il prefisso signed si può omettere – unsigned èquivale a unsigned int – unsigned char  da 0 a 255
  • 19.
    Rapporto tra tipisigned e unsigned • Tipi signed e unsigned non andrebbero mescolati • Se si assegna un valore fuori intervallo a una variabile di tipo unsigned, il risultato è definito come: – Il resto della divisione del valore per il massimo intero che il tipo unsigned può rappresentare • Per esempio, un tipo unsigned char da 8-bit rappresenta valori da 0 a 255. • L’assegnamento di un valore fuori intervallo dà come esito il resto di tale valore diviso per 256 – Assegnare –1 a un unsigned char da 8 bit produce il valore 255!
  • 20.
    Check this out intmain() { // Assegna a unsigned valore negativo unsigned char c; // va da 0 a 255; cout << "Inserire un numero negativo: " << endl; int i; cin >> i; cout << "Valore inserito (intero originale): " << i << endl; c=i; // assegno numero negativo a variabile char cout << "Valore inserito (char): " << c << endl; i=c; // recupero il valore intero di c cout << "Valore inserito (intero dopo la conversione a char): " << i << endl; return 0; } • Il compilatore non può rilevare l’errore • Non sa predire che valore inserisce l’utente • NB:
  • 21.
    Rapporto tra chare int • In molti linguaggi di programmazione, char e int sono tipi non solo diversi, ma incompatibili • C e C++ considerano i caratteri come “una particolare interpretazione” dei numeri interi • La convertibilità tra char e int può portare a programmi difficili da comprendere • Usare char solo quando serve davvero una variabile che contenga un carattere, per esempio per – Cercare/contare/sostituire tutte le occorrenze di un carattere in un testo
  • 22.
    Da char aint • Determinare il codice di una lettera minuscola • Cosa serve – Una variabile char – Una variabile int • Algoritmo 1. Leggi il prossimo carattere 2. Se è una lettera maiuscola 1. 2. 3. Convertilo in un intero Stampa il risultato della conversione Torna al punto uno 3. Termina
  • 23.
    Da char aint int main() { // Legge un carattere e stampa il codice ASCII char c; // se è una minuscola; altrimenti termina int i; cout << "Scrivi un car. minuscolo (maiusc. x finire)" << endl; cin >> c; while (c >= 'a' && c <= 'z') { // minuscolo? i = c; // assegna il valore del carattere a un intero cout << "Valore ASCII per " << c << " è" << i << endl; cout << "Scrivi car. minuscolo (maiusc. x finire)" << endl; cin >> c; } return 0; // termina } NB: notare la ripetizione delle istruzioni che richiedono l'input
  • 24.
    Iterazione e condizione •while (c >= 'a' && c <= 'z') {.. Corpo del ciclo..} • Se la condizione è vera • esegue le istruzioni all’interno del corpo • Quando la condizione è falsa • termina il ciclo (esce dal ciclo) • (c >= 'a' && c <= 'z') • • • • Esprime la condizione di permanenza nel ciclo E’ un’espressione con valore di tipo bool, cioè vero o falso && Operatore logico AND ‘a’ è un valore costante (literal) di tipo carattere
  • 25.
    Da int achar int main() { // Stampa il codice carattere char c; // corrispondente ai 128 codici ASCII for (int i=0; i<128; ++i) { c = i; // assegna il valore intero al carattere cout << "Il carattere corrispondente a " << i << " è: " << c << endl; } return 0; }
  • 26.
    Iterazione a conteggio •for (int i=0; i<128; ++i) • Quando una iterazione deve essere svolta un numero preciso di volte si può usare un “ciclo for” • Il ciclo incrementa una variabile apposita (contatore) per tener traccia del numero di interazioni – int i=0; dichiarazione del contatore – i<128; condizione di permanenza – ++i avanzamento del contatore
  • 27.
    Ciclo while eciclo for char c; for (int i=0; i<128; i++) { c = i; cout << “Il carattere corrispondente a " << i << " è: " << c << endl; } int i = 0; char c; while (i<128) { c = i; cout << “Il carattere corrispondente a " << i << " è: " << c << endl; i++; }
  • 28.
    I tipi floate double • Approssimano i numeri razionali • I valori si possono rappresentare con due notazioni – 315.779 – 3.73E-5 • double di solito occupa più memoria di float – tipicamente 4 byte per float e 8 byte per double • (...esistono anche i long double, che devono occupare almeno tanto spazio quanto i double) • Lo standard prescrive la precisione minima, cioè il minimo numero di cifre significative 28
  • 29.
    Come rappresentare valoridei tipi predefiniti • Il valore 10 ha un tipo diverso da 10.0 • 10L è un valore di tipo long int
  • 30.
    Cautele • Attenzione aiconfronti tra float float a, b; ... if (a == b) ... • non ha senso, a causa delle approssimazioni nella memorizzazione!! diff = A - B; if ((diff < EPSILON) && (-diff < EPSILON)); • EPSILON costante definita in std::numeric_limits
  • 31.
    Check this out •http://stackoverflow.com/questions/17333/m ost-effective-way-for-float-and-doublecomparison
  • 32.
    Quali tipi scegliere •Usare tipi unsigned quando si è certi che servono solo valori positivi • Usare int per valori aritmentici (short di solito non basta) • Se servono valori molto grandi usare long long • Non usare char e bool in espressioni aritmentiche – Se proprio proprio serve un intero piccolo usare signed char o unsigned char • Usare double per le operazioni in virgola mobile (di solito float ha precisione insufficiente e le operazioni sui double sono comunque veloci)
  • 33.
    Conversioni di tipo •Se un operando non è del tipo atteso e il compilatore non ha informazioni sul tipo desiderato interviene una conversione automatica di tipo – È detta anche cast implicito – Si opera la conversione anziché segnalare l'errore • A volte è accompagnata da un “avvertimento” (warning) • Esempio – int i; float f; – la valutazione dell’espressione i + f effettua prima la conversione di i in float e poi la somma
  • 34.
  • 35.
    Conversioni di tipo:esempi int n, m; float x,y; n = 3.541 + 3; // il risultato è 6! x = n; x = n + n = x; n = n / n = n / x = n / x = n / n da “int” è promosso a “float” x; // n è promosso a “float” e poi sommato a x // x è troncato, ne sopravvive la parte intera m; // il risultato della divisione tra interi è intero x; /* n è convertito in “float”, poi si esegue la divisione, il risultato è troncato a int */ x; //come sopra ma il risultato resta float m; // attenzione: la divisione tra int tronca //
  • 36.
    Conversioni con iltipo bool • Il tipo bool ha valore true o false • I valori interi vengono convertiti a bool come segue – 0 viene convertito a false – ogni altro valore intero viene convertito a true • Vale anche la conversione inversa (promozione a int) bool b=false; int i; // inizializzazione i = b; // i vale 0 b=true; i = b; // i ora vale 1!
  • 37.
    Conversioni con tipiun/signed • Ecco perché è meglio non mescolare unsigned u = 10; int i = -42; cout << i + i << endl; // prints -84 cout << u + i << endl; // if 32-bit ints, prints 4294967264 • Il valore di i (-42) viene convertito a unsigned prima dell’addizione unsigned u1 = 42, u2 = 10; cout << u1 - u2 << endl; // ok: result is 32 cout << u2 - u1 << endl; // ok: but the result is… ?
  • 38.
    Quando avvengono leconversioni automatiche • Nelle espressioni, i valori di tipi aritmetici piccoli vengono solitamente promossi a int char c, int i; i=c+i; // da evitare • Nelle condizioni, tipi non bool sono convertiti a bool – while (33) equivale a while (true) ! • Nell’inizializzazione, il valore dell’inizializzatore è convertito al tipo della variabile inizializzata – int i = 3.14; // i inizializzato a 3 ! • Nelle espressioni aritmetiche, gli operando sono convertiti a un tipo comune • Nelle chiamate di funzione (che vedremo)
  • 39.
    Altri esempi bool b= 42; // b is true int i = b; // i has value 1 i = 3.14; // i has value 3 double pi = i; // pi has value 3.0 // assuming 8-bit chars unsigned char c = -1; // c has value 255
  • 40.
    Conversione (cast) esplicita •Quando una conversione utile non verrebbe eseguita, la si può richiedere esplicitamente • Es: divisione tra interi con risultato double x = (double) n / m; • ES: troncamento esplicito del risultato cout << (int) x / y; • C++11 introduce una sintassi più precisa
  • 41.
    ATTENZIONE • Il cast(implicito o esplicito che sia) non modifica il tipo della variabile o delle variabili coinvolte, ma solo il tipo associato al valore dell'espressione • Le variabili in memoria continuano a essere del tipo dichiarato staticamente nella parte dichiarativa del programma • Il cambio del tipo della variabile può portare al cambio del valore dell’espressione dove la variabile è usata
  • 42.
    Le espressioni • Espressione: applicazionedi operatori a operandi – Ha un valore e un tipo – Condizione: espressione con tipo booleano • Cosa bisogna sapere sulle espressioni 1. Ordine di valutazione 2. Proprietà degli operandi (precedenza, associatività) 3. Tipo degli operatori e del risultato, conversioni
  • 43.
    Espressioni base Le espressionibase si compongono di: • Variabili – Il tipo è noto in base alla dichiarazione, e NON cambia • Valori (detti anche costanti o literal) – Il tipo è deducibile da come sono “scritte” • • • • • • • 3 : int (il più piccolo tra int, long, long long in cui il valore ci sta) 03 : int in notazione ottale 0x3 : int in notazione esadecimale 3.0 : float 3.0L : long '3' : char "3" : stringa (sequenza di caratteri di lunghezza variabile)
  • 44.
    Constanti di tipostringa "Hello World!" // string literal •Ha un tipo composto che vedremo in seguito – Array di const char •Il compilatore appende al valore un carattere speciale "terminatore" ('0') •La dimensione reale del valore è 1 + la dimensione apparente (c'è anche il carattere "terminatore") •Attenzione: "a" ha tipo e dimensione diversi da 'a'
  • 45.
    Espressioni e operatori •Operatori unari: sia applicano a un operando – -2 • Operatori binari: sia applicano a due operandi – a+b • Il valore di un'espressione dipende da precedenza e associatività degli operatori e può dipendere dall'ordine di valutazione delle sottoespressioni – Quanto vale : 5 + 10 * 20/2 ? • Nella valutazione intervengono anche le conversioni di tipo
  • 46.
    Precedenza e associatività • • • • • Un'espressionecon due o più operatori di dice composta La sua valutazione richiede di assegnare gli operandi agli operatori Si usano le regole di precedenza tra operatori diversi e le regole di associatività per operatori con la stessa precedenza – Precedenza: 3+4*5 vale 23, non 35 – Associatività: 20-15-3 vale 2, non 8 (associatività a sinistra) Le parentesi possono cambiare le regole – (3+4)*5 vale 35 – 20-(15-3) vale 8 Non solo operatori aritmetici: – – – – cin >> v1 >> v2; // read into v1 and then into v2 L'operatore >> è associativo a sinistra Il valore dell'espressione cin >> v1 è cin ! Equivale a (cin >> v1) >> v2;
  • 47.
    Ordine di valutazioneoperandi • In generale l'ordine di valutazione di sotto-espressioni con la stessa priorità NON è definito – int i = f1() + f2(); // f1, f2 sono funzioni • Non si sa quale tra le funzioni f1() e f2() venga valutata per prima • Non si devono fare ipotesi indebite – int i = 0; – cout << i << " " << ++i << endl; // undefined – ++i espressione che incrementa i e "torna" il nuovo valore – Potrebbe stampare 1 1 oppure 0 1 o qualsiasi altra cosa • Ci sono 4 operatori per cui invece è stabilito l'ordine di valutazione delle sotto-espressioni – AND logico (&&) OR logico (||) operatore condizionale (? :), operatore virgola (,)
  • 48.
    Operatori aritmetici • Precedenzae associatività (tutti associativi a sinistra) • PS: % indica il resto delle divisione tra due interi – Se m e n sono interi e n è diverso da 0, • (m/n)*n + m%n = m – Non si applica a tipi in virgola mobile precedenza
  • 49.
    Operatori logici erelazionali • Sono usati per costruire condizioni, cioè espressioni con valore di tipo bool precedenza
  • 50.
    La regola delcortocircuito • Gli operatori AND e OR in C e C++ hanno un ordine di valutazione prefissato (a differenza delle pure espressioni della logica) – Prima l’operando di sinistra POI quello di destra • Vale una regola detta del cortocircuito che evita di valutare inutilmente l’operando di destra – AND: se l’operando di sinistra vale false – OR: se l’operando di sinistra vale true • La regola del corto circuito ha applicazioni interessanti, per esempio quando si ispezionano sequenze di dati (lo vedremo in seguito)
  • 51.
    L’operatore di assegnamento •Anche = è un operatore! • L’operando di sinistra deve essere una variabile (più precisamente un lvalue), quello di destra un’espressione (più precisamente un rvalue) • Modifica lo stato del programma: assegna il valore dell’operando di destra all’operando di sinistra – – – – i=3 è un’espressione, il cui valore è l’operando di sinistra, cioè i! i=3; è un’istruzione! Per via del ; L’operatore = è associativo a destra x=y=3 equivale a x=(y=3) • Si assegna il valore 3 a y, il risultato dell’espressione è y • Si effettua l’assegnamento x=y che copia il valore di y in x, anche x vale 3 • L’inizializzazione NON è un assegnamento (mai ribadito abbastanza)
  • 52.
    Esempi int i =0, j = 0, k = 0; // initializations, not // assignment 1024 = k; // error: literals are rvalues i + j = k; // error: arithmetic expressions are rvalues k = 0; // result: type int, value 0 k = 3.14159; // result: type int, value 3
  • 53.
    ATTENZIONE • Possiamo usareassegnamenti come condizioni e viceversa? • Si, perché = è un operatore, proprio come == • Di solito però… succede quando si sta commettendo un errore...! – x = y == 3; – while ( 3 = x ) • Ma soprattutto... // Assegna a x il valore 0 o 1 e non modifica // Errore di sintassi (3 non è una variabile) – while(x = 3) ... È SEMPRE VERO!!! (per ogni valore precedente di x) – while (x = 0) ... È SEMPRE FALSO!!! (per ogni valore precedente di x) – while (x = y) ... equivale a scrivere • x = y; • while ( y != 0 ) ... • Ancora peggio... – while (! (x = 0)) ... NON TERMINA MAI !!!!
  • 54.
    I costruttori ditipi • A partire dai tipi predefiniti si possono creare nuovi tipi, che aggiungono proprietà speciali ai tipi predefiniti – – – – const reference pointer array • A questi si aggiungono costruttori di tipi definiti interamente dall'utente – class – struct (eredità del C, poco usato in C++)
  • 55.
    Il qualificatore const •Talora serve definire una variabile che non cambi valore • Per farlo si premette const alla dichiarazione – const int modelloauto = 159; // tipo const int – const float pigreco = 3.14159265; // tipo const float • Un tipo const è diverso dal corrispondente tipo base • Una variabile const – Si dichiara e inizializza obbligatoriamente – Non ammette assegnamento • • • • • int i = 42; const int ci = i; // ok: the value in i is copied into ci ci = 43; // error, no assignment const int k; // error: k is uninitialized const int j = ci; // ok: the value in ci is copied into j
  • 56.
    Visibilità delle variabiliconst • const float pigreco = 3.14159265; • Il compilatore sostituisce pigreco con il valore associato • Il compilatore deve conoscere il valore dell'inizializzatore • Se il programma è diviso in più file, la variabile const viene (ri)definita in ciascun file • Alternativamente, la variabile const può essere definita in un solo file e dichiarata come esterna nei file che ne fanno uso
  • 57.
    extern const • Perdefinire una variabile const una sola volta, si usa la keyword extern sia nella definizione che nelle dichiarazioni // file_1.cc definisce e inizializza una variabile const extern const float pigreco = 3.14159265; // file_1.h extern const float pigreco; // stessa const di file_1.cc • file_1.cc definisce e inizializza pigreco. Siccome la dichiarazione comprende un inizializzatore è una definizione. Però, siccome pigreco è const, si specifica extern per farla usare in altri file. • La dichiarazione in file_1.h è anch'essa extern. Significa che pigreco non è locale a tale file ma definita altrove • Altri file possono includere il file file_1.h e importare la definizione di pigreco
  • 58.
    Definizione di macro:#define • Esiste anche un altro modo di definire un valore una volta per tutte – la direttiva #define • #define PIGRECO 3.141592 – #define è una direttiva al preprocessore (come #include) – Non è terminata dal punto e virgola • una direttiva al precompilatore non è un’istruzione C++ – Non causa allocazione di memoria • PIGRECO è una "costante simbolica" – Il simbolo PIGRECO viene sostituito nel codice con il valore 3.14 … prima che il programma sia compilato – Si dice che PIGRECO è una macro-definizione (o semplicemente una MACRO) – Per convenzione, le costanti definite tramite macro sono interamente maiuscole (es: TIPICA_COSTANTE)
  • 59.
    Definizione di macro:#define • #define MIGLIO 1609.34 • #define YARDA 0.9144 • Si possono costruire MACRO a partire da altre MACRO, e una macro può anche usare uno o più parametri: – #define AREACERCHIO(X) (PIGRECO∗(X)∗(X)) – area = AREACERCHIO(4); – Diventa ⇒ area = (3.141592∗(4)∗(4)); – ma è un valore fisso !!
  • 60.
    Altre direttive alprecompilatore • Abbiamo già visto: #include <nome_libreria> – Serve a richiamare librerie – comanda al preprocessore di leggere anche da un altro file sorgente • Altra direttiva usata frequentemente: #undef VERO – Serve ad annullare la definizione di una MACRO – Da quel punto in poi la costante VERO non è più definita
  • 61.
    Esempio di programmamultifile /* File contenente funzioni di utilità sui cerchi * dichiarazione e definizione di una costante * riusabile : pigreco.cpp */ extern const float pigreco = 3.14159265; float area(float r){ return r*r*pigreco; } float perimeter(float r){ return 2*r*pigreco; }
  • 62.
    File di intestazionedella libreria /* * * * * File contenente solo le dichiarazioni delle funzioni di utilità sui cerchi e della costante riusabile: pigreco.h Usato dal compilatore per controllare la correttezza del programma */ extern const float pigreco; float area(float r); float perimeter(float r);
  • 63.
    Programma principale #include <iostream> #include"pigreco.h" using namespace std; int main() { float radius; cout << "Insert the value of the radius: " << endl; cin >> radius; cout << "Value of pigreco: " << pigreco << endl; cout << "Perimeter of circle: " << perimeter(radius) << cout << "Area of the circle: " << area(radius) << endl; return 0; } endl;
  • 64.
    I tipi definitidall'utente • La ragione principale del successo di C++ è che consente di estendere il sistema dei tipi in modo assai potente • La novità introdotta dal C++ è che i tipi definiti dall'utente si possono usare (quasi) come quelli predefiniti
  • 65.
    Requisiti • Si vuolecostruire un programma che gestisca le vendite di libri • Ogni libro è caratterizzato da un codice ISBN • Il programma deve gestire per ogni libro il numero totale di copie vendute, il ricavo totale e medio • L'utente inserisce sequenzialmente i dati di vendita per i diversi libri, il programma aggrega i dati per ciascun codice ISBN e stampa il dato aggregato • Deve quindi essere possibile: – Inserire e stampare i dati di un libro – Sommare più dati di vendita relativi allo stesso libro – Stampare i dati di vendita aggregati relativi a un libro
  • 66.
    Progettazione • Inizializza iltotale vendite a 0 • Leggi dati 1° transazione e aggiorna il totale • Finché ci sono transazioni – Leggi un'altra transazione; – Se la transazione letta è dello stesso libro di prima • sommala al totale corrente; – Altrimenti • stampa il totale corrente; // è cambiato il libro • inizializza il totale corrente del nuovo libro coi dati della transazione letta; • Stampa il totale di vendita dell'ultimo libro (perché serve??)
  • 67.
    Suddivisione del programma •Un file di libreria (Sales_item.h) conterrà la definizione del tipo che rappresenta il dato di vendita: Sales_item • Sales_item è un tipo classe, cioè un tipo definito dall'utente che comprende dati e operazioni relativi al dato di vendita • Il programma principale (Booksales.cpp) importa e usa Sales_item come se fosse un tipo predefinito – Sales_item item; // dichiara una var. di tipo Sales_item !
  • 68.
    Un primo esempiodi main() #include <iostream> #include "Sales_item.h" int main() { Sales_item book; // read ISBN, number of copies sold, and sales price std::cin >> book; // write ISBN, # of copies sold, total revenue, and average price std::cout << book << std::endl; return 0; } •book è trattata come una variabile di un tipo predefinito •Si applicano gli stessi operatori di I/O che a una variabile di tipo int •Gli operatori sono definiti nel tipo classe Sales_item
  • 69.
    L'esempio completo int main() { Sales_itemtotal; // variable to hold data for the next transaction // read the first transaction and ensure that there are data to process if (cin >> total) { Sales_item trans; // variable to hold the running sum // read and process the remaining transactions while (cin >> trans) { // if we're still processing the same book if (total.isbn() == trans.isbn()) total += trans; // update the running total else { // print results for the previous book cout << total << endl; total = trans; // total now refers to the next book } } cout << total << endl; // print the last transaction } else { // no input! warn the user cerr << "No data?!" << endl; return -1; // indicate failure } return 0; } if(cin >> x) while(cin >> x) La condizione è soddisfatta se l'acquisizione dell'input non ha dato errori Per terminare, basta premere CTRL-Z (carattere end of file)