Corso sul Linguaggio C ++
Transcript
Corso sul Linguaggio C ++
GIOVANNI CALABRESE Corsi Integrativi di Informatica Corso sul Linguaggio C ++ !2 CORSI INTEGRATIVI DI INFORMATICA Corso sul Linguaggio C++ Procedurale (ediz. 2010) ITIS “Leonardo da Vinci” RIMINI Questo testo è stato creato appositamente per il corso di approfondimento sul linguaggio C++ tenuto da me presso l’ITC Fraccacreta di San Severo (FG), con l’obiettivo di preparare gli alunni per le olimpiadi dell’informatica. La bibliografia a cui si fa riferimento è quella riportata nell’appendice 9. Appendici e Link (pag. 153). Si tratta di un corso intensivo che presuppone la conoscenza delle basi della programmazione e che mira soprattutto alla comprensione delle caratteristiche del linguaggio. Chi non ha mai studiato informatica e programmazione è opportuno che integri con un testo base sull’argomento. Per acquisire un livello di padronanza nell’uso del linguaggio C sufficiente ad affrontare i problemi delle selezioni per le olimpiadi di informatica è necessario esercitarsi a lungo, svolgendo, possibilmente, tutti gli esercizi presenti nel testo, sia gli esempi svolti che quelli proposti alla fine di ogni capitolo. Per la realizzazione pratica dei programmi proposti in questo corso si fa riferimento all’ambiente di sviluppo Dev-C++ distribuito con licenza GNU sul sito: http://www.bloodshed.net/devcpp.html. © E’ vietata la riproduzione con qualsiasi mezzo di questo testo senza il permesso dell’autore. !1 Sommario !i 1.FONDAMENTI DEL LINGUAGGIO C ..............................................................2 1.1.Caratteristiche e storia del linguaggio C.............................................................2 1.2.Differenze principali tra C e C++ .......................................................................3 1.3.Installazione del Dev-C++ ..................................................................................3 1.4.Gli elementi di un programma C++....................................................................4 1.5.Dal codice sorgente al codice eseguibile ............................................................5 1.6.le direttive al preprocessore ..............................................................................10 1.7.Le funzioni di I/O .............................................................................................11 1.8.Il debug dei programmi con Dev C e Dev Pas .................................................11 Note per l’esecuzione del debugger ........................................................................................................13 Note per la versione 4.9.9.2 ....................................................................................................................13 2.TIPI DI DATI, VARIABILI E COSTANTI, LE ESPRESSIONI .........................14 2.1.Calcolo dell’area di un rettangolo ....................................................................14 2.2.Definizione di variabili .....................................................................................16 2.3.Costanti .............................................................................................................18 2.4.I tipi di dati e le operazioni permesse ...............................................................19 Il tipo int..................................................................................................................................................20 Il tipo char ...............................................................................................................................................21 I tipi float e double ..................................................................................................................................21 Il tipo bool ..............................................................................................................................................21 L’operatore speciale sizeof......................................................................................................................22 2.5.Le espressioni ...................................................................................................22 Gli operatori di assegnamento ................................................................................................................23 Operatori aritmetici .................................................................................................................................24 Operatori relazionali ...............................................................................................................................25 Operatori logici .......................................................................................................................................25 Operatori sui bit ......................................................................................................................................26 Regole di valutazione di un’espressione .................................................................................................27 Operatori speciali ....................................................................................................................................28 Riassunto degli operatori del linguaggio C++ ........................................................................................30 2.6.La conversione di tipo ......................................................................................32 2.7.Le enumerazioni ...............................................................................................34 2.8.Esercizi .............................................................................................................35 Risposte esercizi ...............................................................................................................................36 3.LE ISTRUZIONI E LE STRUTTURE DI CONTROLLO ..................................38 3.1.La programmazione strutturata .........................................................................38 3.2.Le istruzioni semplici .......................................................................................40 3.3.La sequenza ......................................................................................................41 Area di validità delle variabili ..........................................................................................................42 Ciclo di vita delle variabili ...............................................................................................................42 3.4.La Selezione .....................................................................................................43 Problema: equazione di primo grado ......................................................................................................43 3.5.Selezione multipla ............................................................................................44 Problema: una semplice calcolatrice.......................................................................................................45 3.6.I cicli 46 Istruzioni do e while .........................................................................................................................46 Istruzione for ....................................................................................................................................47 Problema numeri primi ....................................................................................................................49 3.7.Documentazione ...............................................................................................49 3.8.Esercizi .............................................................................................................50 Solo selezione...................................................................................................................................51 !ii Cicli ..................................................................................................................................................52 Doppio ciclo .....................................................................................................................................55 Risposte esercizi ...............................................................................................................................55 4.I SOTTOPROGRAMMI ...................................................................................58 4.1.Le funzioni ........................................................................................................59 Problema scrivere una funzione per calcolare la potenza intera di un numero qualsiasi .................60 4.2.Il passaggio dei parametri .................................................................................61 Problema: scrivere un sottoprogramma per scambiare il contenuto di due variabili .......................62 4.3.I prototipi delle funzioni ...................................................................................63 4.4.Le principali funzioni presenti nelle librerie standard del linguaggio C ..........63 4.5.Meccanismo di esecuzione di un sottoprogramma e funzioni inline................66 4.6.Le variabili static ..............................................................................................69 4.7.Funzioni ricorsive .............................................................................................70 Problema: scrivere una funzione per il calcolo del fattoriale di un numero. ...................................71 4.8.Esercizi .............................................................................................................71 Ricorsione ........................................................................................................................................72 Risposte esercizi ...............................................................................................................................73 5.VETTORI, STRINGHE E PUNTATORI ...........................................................74 5.1.Cosa sono i vettori ............................................................................................74 5.2.A cosa servono i vettori ....................................................................................76 Problema: Far leggere una serie di numeri e visualizzare solo quelli superiori alla media .............77 5.3.Caricamento di un vettore con numeri a caso...................................................77 5.4.Ordinamento di un vettore ................................................................................78 Ordinamento per selezione ...............................................................................................................79 Ordinamento per inserzione .............................................................................................................80 Bubble sort .......................................................................................................................................81 Quick sort .........................................................................................................................................81 5.5.La ricerca in un vettore .....................................................................................82 5.6.Le stringhe di caratteri ......................................................................................83 Problema: Dato un numero di massimo tre cifre trasformarlo in lettere..........................................86 5.7.Le funzioni printf, scanf, puts e gets ................................................................87 printf .................................................................................................................................................88 scanf .................................................................................................................................................89 Input e output di stringhe di caratteri ...............................................................................................90 Problema: trasformare tutti i caratteri di una frase in maiuscolo .....................................................90 5.8.I puntatori .........................................................................................................91 5.9.Puntatori e vettori. Operazioni sui puntatori ....................................................93 Problema: Sostituire in una frase tutte le occorrenze di una parola con un'altra parola data in input.94 5.10.Sottoprogrammi, vettori e puntatori ...............................................................94 Problema: Assegnato nome e cognome generare i primi 6 caratteri del codice fiscale ...................95 5.11.Tipi particolari di puntatori .............................................................................96 Puntatori e funzioni .................................................................................................................................96 6. Esercizi.........................................................................................................................98 Programmi con i vettori .................................................................................................................100 Puntatori .........................................................................................................................................102 Risposte esercizi .............................................................................................................................102 6.STRUTTURE, UNION, MATRICI, TABELLE E L’ALLOCAZIONE DINAMICA DELLA MEMORIA ........................................................................................104 6.1.Strutture ..........................................................................................................104 6.2.Le union ..........................................................................................................106 !iii Problema: rappresentazione interna di una variabile .....................................................................106 6.3.Le matrici e le tabelle .....................................................................................106 Matrici ............................................................................................................................................107 Problema: Scrivere un programma per caricare due matrici n x n, calcolarne il prodotto righe per colonne e visualizzare la matrice risultato. ..................................................................................................108 6.4.Allocazione dinamica della memoria .............................................................109 Problema: Gestione di una pila. ............................................................................................................110 6.5.Assegnare pseudonimi ai tipi di dati ...............................................................111 6.6.Esercizi ...........................................................................................................112 Programmi con le matrici ...............................................................................................................112 7.I FILE SU DISCO ..........................................................................................114 7.1.Input output in generale ..................................................................................114 7.2.I file su disco ...................................................................................................115 Esempio 1 copiare il contenuto di un file in un altro file. ..............................................................117 Esempio2 – Stringhe strane. Problema della selezione regionale 2002. ........................................118 8.RACCOLTA TEST DELLE OLIMPIADI INFORMATICA (SOLO TEST LINGUAGGIO C) ...........................................................................................................120 8.1.Selezione scolastica ........................................................................................121 Anno 2008 (test somministrato il 23/11/2007) .....................................................................................121 Anno 2007 (test somministrato il 16/11/2006) .....................................................................................123 Anno 2006 (test somministrato il 18/11/2005) .....................................................................................124 Anno 2005 ............................................................................................................................................127 Anno 2004.............................................................................................................................................130 8.2.Selezione regionale .........................................................................................133 Prove regionali 2001 – il cassiere Camillo (CAM) .......................................................................133 La biblioteca degli smemorati (BIB) olimpiade regionale 2001 ....................................................134 Lo struzzo Simone (SIM) – olimpiade 2001 ..................................................................................134 Foto Stellare – olimpiadi 2004 ......................................................................................................135 La dieta di Poldo – olimpiadi 2004 ................................................................................................136 Giardinaggio – olimpiade 2004 ......................................................................................................137 Le pesate di bilancino (bilancino) olimpiade – 2006 .....................................................................138 Teste di serie (serie) – olimpiade 2006...........................................................................................138 Ritrovo a Brambillia (brambillia) – olimpiade 2006......................................................................139 Mappa antica – olimpiadi 2008 ......................................................................................................140 8.3.Soluzioni delle selezioni scolastiche ..............................................................141 Test 2008 ........................................................................................................................................141 Test 2007 ........................................................................................................................................142 Test 2006 ........................................................................................................................................142 Test 2005 ........................................................................................................................................142 Test 2004 ........................................................................................................................................142 8.4.Soluzione di alcuni problemi delle selezioni regionali ...................................143 Cassiere Camillo – 2001 ................................................................................................................143 Dieta di Poldo – olimpiadi 2004 ....................................................................................................143 Testa di serie – olimpiadi 2006 ......................................................................................................144 Mappa antica – olimpiadi 2008 ......................................................................................................145 9.APPENDICI E LINK .....................................................................................147 9.1.Conclusioni .....................................................................................................147 9.2.Come approfondire. ........................................................................................148 Libri ......................................................................................................................................................148 Il DEV-C++ ...........................................................................................................................................148 Olimpiadi dell’informatica ...................................................................................................................149 Siti per programmatori ..........................................................................................................................149 Guide on line .........................................................................................................................................149 Link per documentazione......................................................................................................................149 !iv 9.3.Tabelle presenti nel testo ................................................................................149 !v Corso sul Linguaggio C++ Edizione 2010 1 Capitolo !1 Corso sul Linguaggio C++ Edizione 2010 1.Fondamenti del linguaggio C Caratteristiche del linguaggio, creazione dei primi programmi . 1.1.Caratteristiche e storia del linguaggio C. Nel 1972, presso i Bell Laboratories, Dennis Ritchie progettava e realizzava la prima versione del linguaggio C. Ritchie aveva ripreso e sviluppato molti dei principi e dei costrutti sintattici del linguaggio BCPL, sviluppato da Martin Richards, e del linguaggio B, sviluppato da Ken Thompson, l'autore del sistema operativo Unix. Successivamente gli stessi Ritchie e Thompson riscrissero in C il codice di Unix. Il C si distingueva dai suoi predecessori per il fatto di implementare una vasta gamma di tipi di dati (carattere, interi, numeri in virgola mobile, strutture) non originariamente previsti dagli altri due linguaggi. Da allora ad oggi il C ha subito trasformazioni: la sua sintassi è stata affinata, soprattutto in conseguenza della estensione object-oriented (C++). Il C++, come messo in evidenza dallo stesso nome, rappresenta una evoluzione del linguaggio C: il suo progettatore (Bjarne Stroustrup) quando si pose il problema di trovare uno strumento che implementasse le classi e la programmazione ad oggetti, invece di costruire un nuovo linguaggio di programmazione, pensò bene di estendere un linguaggio già esistente, il C appunto, aggiungendo nuove funzionalità. In questo modo, contenendo il C++ il linguaggio C come sottoinsieme, si poteva riutilizzare tutto il patrimonio di conoscenze acquisito dai programmatori in C (linguaggio estremamente diffuso in ambito di ricerca) e si poteva fare in modo che tali programmatori avessero la possibilità di acquisire le nuove tecniche di programmazione senza essere costretti ad imparare un nuovo linguaggio e quindi senza essere costretti a disperdere il patrimonio di conoscenze già in loro possesso. Così le estensioni ad oggetti hanno fornito ulteriore linfa vitale al linguaggio C. Le principali caratteristiche del linguaggio C e C++ sono: - Si tratta di un linguaggio general purpose, ovvero può essere utilizzato per realizzare programmi di natura diversa (dai sistemi operativi, ai programmi gestionali e ai videogiochi) - Produce dei programmi efficienti. E’ stato progettato per scrivere più facilmente i programmi che costituiscono un sistema operativo i quali devono essere molto efficienti. Normalmente, prima della creazione del linguaggio C, i sistemi operativi venivano scritti in Assembler. Il linguaggio C è un linguaggio di alto livello (più vicino al linguaggio dell’uomo) che però mantiene anche delle caratteristiche tipiche dei linguaggi di basso livello (più vicini al linguaggio della macchina e quindi più scomodi da utilizzare per il programmatore). - Supporta sia il paradigma procedurale che il paradigma della programmazione orientata agli oggetti. Con il paradigma procedurale il programma consiste in una serie di istruzioni che dicono al computer come acquisire i dati in ingresso e produrre i risultati finali, mentre con il paradigma orientato agli oggetti il programma è costituito da una serie di oggetti software ognuno di quali ha determinate caratteristiche, il programmatore mette insieme e/o costruisce questi oggetti. !2 Corso sul Linguaggio C++ Edizione 2010 1.2.Differenze principali tra C e C++ Come detto il linguaggio C++ è un’estensione del linguaggio C e quindi tutti i programmi C possono essere compilati da un compilatore C++ Il principale miglioramento del linguaggio C++ rispetto al linguaggio C è stata l’introduzione dei costrutti necessari per realizzare programmi orientati agli oggetti. Nel seguito noi non ci occuperemo della programmazione ad oggetti ma solo di quella procedurale per la quale le differenze tra C e C++ sono limitate, eccone alcune: - I commenti. Con il linguaggio C i commenti vanno racchiusi tra i simboli /* ……*/, nel linguaggio C++ è stato introdotto anche il simbolo // che indica al compilatore che tutto quello che segue nella riga è un commento; - Dichiarazioni all’interno dei blocchi. Con il C++ sono consentite, con il C no. - Nuovi nomi di tipi. Sono stati introdotti nuovi nomi di tipi che semplificano la notazione. - E’ stato introdotto lo specificatore const. - Più funzioni possono utilizzare lo stesso nome, per distinguerle basta che abbiano un numero o tipo di parametri diverso (overloading di funzioni). - Si possono richiamare le funzioni con un numero di parametri inferiore a quello dichiarato, i parametri non passati vengono sostituiti con valori standard - Sono stati introdotti gli operatori new e delete per il controllo dell’allocazione dinamica della memoria. 1.3.Installazione del Dev-C++ Il linguaggio C è un linguaggio compilato, questo significa che dopo aver scritto il programma sorgente, per poterlo eseguire bisogna prima compilarlo, ovvero tradurlo in linguaggio macchina. Il programma risultante dopo questa traduzione è detto programma oggetto. La traduzione viene fatta da un altro programma che si chiama compilatore. Per poter scrivere ed eseguire i programmi in linguaggio C bisogna procurarsi un compilatore C. Sul server del laboratorio è presente l’ambiente di sviluppo Dev C++ versione 4.01 che comprende un compilatore C++ che è open source e distribuito con licenza GNU ed è quindi liberamente scaricabile e utilizzabile nel rispetto della licenza. Per gli aggiornamenti si può visitare il sito del produttore: http://www.bloodshed.net/c/index.html. Attualmente è disponibile anche una versione più recente, la versione 4.9.9.2, (presente anch’essa sul server nella cartella “Aggiornamenti”) ma, sebbene sia più comoda da utilizzare, è ancora una versione beta ed è diversa da quella ufficialmente utilizzata per le olimpiadi dell’informatica. Per installare il compilatore e il debugger (programma per facilitare la correzione degli errori) presenti sul server (insieme a molto altrro materiale) all’indirizzo \\192.168.2.198\CorsoCPP\, bisogna seguire le seguenti istruzioni. 1) 2) 3) 4) 5) Aprire la cartella DevCPP e Copiare la cartella C++ che si trova all’interno sul proprio computer; Decomprimere il file “devcpp4.zip” ed eseguire il file “setup.exe”; ciò installerà la versione 4.0 del Dev-C++. Sostituire il file “DevCpp.exe” contenuto nella directory di installazione del Dev-C++ (ad esempio “C: \Dev-C++”) con il file “DevCpp.exe” contenuto dentro l’archivio devcpp401.zip. Decomprimere il file insight5_win32.zip (che contiene il debugger per l’ambiente) e che si trova nella cartella Debugger; nell’archivio sono contenute due cartelle: “bin” e “share”. Copiare la directory “share” nella directory di installazione del Dev-C++ (ad esempio “C:\Dev-C++”), e copiare i file contenuti nella cartella “bin” nella cartella “bin” della directory di installazione del Dev-C++ (ad esempio C:\Dev-C++\bin) A questo punto avete installato l’ambiente di sviluppo. Conviene crearsi una cartella personale sul disco rigido del computer dove mettere tutti i programmi che scriverete e impostarla come cartella di default per l’ambiente di sviluppo. Per farlo prima create la cartella (per esempio la cartella c:\<vostro- !3 Corso sul Linguaggio C++ Edizione 2010 nome>\cpp); poi lanciate l’ambiente di sviluppo da start programmi!DevC++!Dev-C++; rispondete si alla richiesta se abbinare le estensioni .cpp, .dev ecc. al Dev-C++; dopo aver avviato l’ambiente di sviluppo fate click su Options!Environment Options sulla barra dei menù e nella scheda Preferences, impostate la Default directory con il percorso della directory che avete creato per i programmi C++ (vedi figura che segue). ! 1.4.Gli elementi di un programma C++ Un programma C++ è costituito dai seguenti elementi: Direttive al preprocessore Dichiarazioni di variabili globali, funzioni e prototipi di funzioni Programma principale (funzione main) ! Funzione 1 Funzione N !4 Corso sul Linguaggio C++ Edizione 2010 Tutti gli elementi sono facoltativi esclusa la funzione main (il programma principale) che deve sempre esserci. Le regole con cui deve essere scritto un programma o un’istruzione di un programma sono dette regole sintattiche in analogia per le regole del nostro linguaggio naturale. Per descrivere queste regole si utilizzano spesso, nei manuali dei linguaggi di programmazione, i grafi sintattici che rappresentano tutti i modi in cui si possono costruire le frasi valide del linguaggio. Per esempio, utilizzando un grafo sintattico per descrivere quanto detto prima, un programma C+ + deve essere costruito nel seguente modo: Programma C++ Funzione main() ! Direttive al preprocessore Funzioni e Prototipi di Va r i a b i l i globali D i c h i a r azioni altre funzioni Il programma Principale (così come qualsiasi funzione) deve avere la seguente struttura: [<Tipo restituito>] main ([elenco parametri]) { [istruzione1;] [istruzione2;] …….. [istruzione n;] } <Tipo restituito> indica il tipo di dati restituito dalla funzione, in mancanza di indicazione si sottintende int ovvero intero. Una delle istruzione della funzione è normalmente: return <valore> che fa terminare la funzione restituendo il valore indicato (se non viene inserita il compilatore inserisce automaticamente return 0). Il più semplice programma C è quindi il seguente: int main() { return 0 } Se una funzione non restituisce nessun valore si può anche indicare, come tipo, la parola chiave void che rappresenta un tipo di dato particolare. Questo è il modo in cui si dichiarano nel linguaggio C le procedure. Per esempio se il programma principale non deve restituire nessun valore si può scrivere: void main(){ } 1.5.Dal codice sorgente al codice eseguibile Vediamo ora come si realizza praticamente un programma C++. I passi da seguire per creare un programma eseguibile sono i seguenti: 1. Scrittura del programma sorgente; Apriamo l’ambiente di sviluppo Dev-C++ e scegliamo New Source File dal menù File. Verrà visualizzato il file default.txt che contiene la base per un qualsiasi programma C. A questo punto scriviamo il programma, per esempio quello della figura che segue. Alla fine salviamo il file usando la voce Save !5 Corso sul Linguaggio C++ Edizione 2010 Unit o Save Unita s .. dal menù File. Il programma verrà salvato con il nome che scegliamo e normalmente con l’estensione .cpp (verificare che la cartella sia quella giusta); 2. Compilazione e linking. Un programma scritto in linguaggio C non può essere eseguito così com’è perché il computer capisce solo il linguaggio macchina. Per eseguire un programma bisogna compilarlo (tradurlo in linguaggio macchina) e unirci i sottoprogrammi standard del linguaggio (operazione di link), dopo queste due operazioni viene creato un programma in linguaggio macchina eseguibile con estensione .exe, questo secondo programma può essere mandato in esecuzione. Per eseguire la compilazione e il linking bisogna scegliere Compile dal menù Execute. Se non vengono trovati errori verrà creato il programma eseguibile con lo stesso nome del programma sorgente ma con estensione “.exe”. Nella scheda Compiler che si trova nel riquadro posto nella parte inferiore della finestra del Dev-C++, vengono visualizzati i messaggi del compilatore. Se la compilazione è andata a buon fine verrà viaulizzato il messaggio <nome programma> compiled successfully, altrimenti vengono visualizzati gli errori trovati con l’indicazione della riga dove sono stati trovati; 3. Esecuzione. Scegliere Run dal menù Execute. Viene aperta la finestra di esecuzione dove va l’output del programma e il programma viene eseguito. Al termine la finestra di esecuzione viene chiusa automaticamente. Per esempio provate a scrivere il seguente programma: !6 Corso sul Linguaggio C++ Edizione 2010 ! Vediamo che cosa significa quello che abbiamo scritto. Dalla parola main, seguita da parentesi tonda aperta e chiusa, inizia l'esecuzione del programma. Il corpo del programma, che comincia dalla parentesi graffa aperta e finisce alla parentesi graffa chiusa, è composto da una serie di istruzioni. Le istruzioni cout << …. sono operazioni che riguardano l’invio di stringhe (sequenze di caratteri racchiuse fra doppi apici) verso una unità di output: nel nostro caso il monitor. Nell’esempio proposto cout sta ad indicare il canale di output e il simbolo << è l’operatore di output. L’istruzione cout << “abc”; si potrebbe così tradurre: inserisci nel canale di output la stringa specificata. Per quanto riguarda l’instradamento verso il canale, per il momento, si può pensare come ad una conduttura che porta al monitor e nella quale si inseriscono una di seguito all’altra le stringhe specificate. L’istruzione system("PAUSE") impedisce alla schermata di esecuzione di scomparire senza darci il tempo di vedere il risultato del nostro programma, questa istruzione manda in esecuzione il comando PAUSE del sistema operativo. Questo comando invia nel canale di output la stringa “Premere un tasto per continuare …” e aspetta fino a quando non viene premuto un tasto. Se aprite una finestra di comandi (Start!Programmi!Accessori!Prompt dei comandi) potete provare a scrivere il comando PAUSE e vederne l’effetto. Con la funzione system si !7 Corso sul Linguaggio C++ Edizione 2010 può mandare in esecuzione qualsiasi comando del sistema operativo o programma. Per esempio: • system (“CLS”) pulisce la finestra di output (CLS=Clear Screen). • system(“Color 1F”) imposta il colore blu come sfondo e il colore bianco come primo piano (il comando generale è color xy dove x e y sono due cifre esadecimali con x=colore di sfondo e y=colore di primo piano) Per utilizzare la funzione system nei nostri programmi dobbiamo inserire all’inizio #include <stdlib.h> Ogni istruzione deve terminare con un carattere di punto e virgola. Se paragoniamo un programma ad un testo, le istruzioni sono le frasi e ogni istruzione è costituita da una serie di parole dette token separate le une dalle altre da spazi o simboli (per esempio la punteggiatura). Così come il significato di una frase non cambia in base al numero di spazi tra le parole, anche il numero di spazi inserito tra le parole di una istruzione C++ è irrilevante, mentre cambia il significato della frase se si inserisce uno spazio tra i caratteri di una parola spezzandola. Per poter utilizzare cout, come le altre funzioni di entrata/uscita, si deve inserire all'inizio del testo la direttiva al preprocessore #include <iostream.h> che avverte il compilatore di includere i riferimenti alla libreria dei canali standard di input/ output (iostream sta per canali di input/output). Il C++ è un linguaggio case-sensitive ovvero distingue tra lettere maiuscole e minuscole; dunque occorre fare attenzione, se si scrive MAIN() o Main() non si fa riferimento a main(). Proviamo a compilare ed eseguire il programma che abbiamo scritto, se non ci sono errori nella finestra di esecuzione compare la scritta : abcdefghilmnopqrstuvzPremere un tasto per continuare … Se si desidera che ogni stringa venga prodotta su una linea separata, si deve inserire \n nel canale subito dopo la stringa e prima della chiusura dei doppi apici (\n è un carattere speciale che fa andare a capo il cursore sul monitor, si tratta del carattere new line), come nel seguente listato. #include <iostream.h> #include <stdlib.h> main(){ cout << "abc" << cout << "def" << cout << "ghi" << cout << "lmn" << cout << "opqrs" << cout << "tuvz" << system(“PAUSE”); } “\n”; “\n”; “\n”; “\n”; “\n”; “\n”; Eseguendo il programma si otterrà la visualizzazione delle seguenti stringhe di caratteri abc def ghi lmn !8 Corso sul Linguaggio C++ Edizione 2010 opqrs tuvz Premere un tasto per continuare …. In questo caso la prima stringa (abc) viene stampata su video a partire dalla posizione attuale del cursore. Se si vuole cominciare la stampa delle stringhe da una nuova riga basta inserire \n anche all’inizio o in qualsiasi punto anche all’interno della stringa da stampare come nei seguenti esempi: cout << “\n” << “abc” << “\n”; cout << “\nabc\ndef”; Qui prima si passa ad una nuova riga, poi si stampa la stringa specificata e quindi si posiziona il cursore in una nuova riga. In generale è bene tenere presente che l’effetto di ogni cout è quello di stampare a partire dalla posizione in cui si trovava il cursore (in generale a destra dell’ultima stampa). Per poter modificare tale comportamento è necessario inserire gli opportuni caratteri di controllo. In effetti la sequenza \n corrisponde ad un solo carattere, quello di nuova linea (newline). Esistono altri caratteri di controllo, tutti vengono indicati con una sequenza che inizia con la barra rovesciata(\), di seguito vengono riportati quelli che hanno utilizzo più frequente: Tabella 1. Sequenze di escape Sequenza Effetto \n porta il cursore all’inizio della riga successiva (ASCII 10 = new line) \t porta il cursore al prossimo fermo di tabulazione (ogni fermo di tabulazione è fissato ad 8 caratteri) (ASCII 9) \a (alert) fa un beep \f (form feed) nuova pagina (ASCII 12) \r (cariage return) ritorno del carrello nella stampa (ASCII 13) \’ stampa un apice \” stampa le virgolette \\ barra rovesciata \? Punto di domanda \b (Bakspace) Indietro di un carattere. (ASCII 8) \<cifreOttali> Il carattere corrispondente nel codice ASCII al numero ottale \x<cifreEsadec Il carattere corrispondente nel codice ASCII al numero esadecimale > La sequenza costituita dalla barra rovesciata seguita da uno o più caratteri viene detta sequenza di escape e corrisponde ad un solo carattere. Utilizzando una sequenza di escape si può inserire anche il carattere corrispondente ad un qualsiasi codice ASCII in due modi: utilizzan!9 Corso sul Linguaggio C++ Edizione 2010 do la barra rovesciata (\) seguita dal codice ASCII scritto in ottale oppure la barra rovesciata e la x (\x) seguita dalle cifre esadecimali del codice ASCII (per esempio \x41 corrisponde al carattere ‘A’). Quando si fanno delle modifiche al programma sorgente per provarlo bisogna compilarlo ed eseguirlo ma attenzione a chiudere l’eventuale finestra di un esecuzione precedente perché, se è ancora aperta, il programma eseguibile è bloccato e il compilatore non può sostituirlo con quello nuovo. Purtroppo, il dev-c++ non segnala nessun errore in questo caso, semplicemente rimanda in esecuzione il vecchio programma. 1.6.le direttive al preprocessore A differenza di altri linguaggio, la compilazione di un programma in linguaggio C prevede una prima fase detta precompilazione, svolta da un apposito modulo detto preprocessore. Il Preprocessore è normalmente incluso nel compilatore. Nella fase di precompilazione il programma viene preparato per la successiva compilazione. E’ possibile inserire in un programma C delle istruzioni che verranno eseguite dal preprocessore, queste istruzioni sono dette direttive al preprocessore o dichiarative di precompilazione, tutte le direttive al preprocessore hanno la seguente struttura: #<nome direttiva> <dichiarazione della direttiva> con i grafi sintattici: direttive al preprocessore # ! nome direttiva dichiarazione direttiva Una delle direttive più utilizzate è la direttiva include per esempio: #include <iostream.h> oppure #include “iostream.h” Questa direttiva dice al preprocessore di inserire nel file sorgente il file “iostream.h” che è un file che contiene i riferimenti alle funzioni standard di Input e Output. Se si usano le parentesi angolari (<…>) il preprocessore ricercherà il file nelle cartelle di sistema (che si possono modificare intervenendo nella scheda Directories visualizzata con la voce di menù Options!Compiler Options), se si usano i doppi apici cercherà prima nella cartella corrente. Se invece di inserire la direttiva si copiasse il contenuto del file “iostream.h” nel programma sorgente il risultato della compilazione sarebbe lo stesso. I file con estensione .h sono file di intestazione e contengono le dichiarazioni necessarie per richiamare, all’interno del codice sorgente, i sottoprogrammi inclusi in librerie esterne. Molte librerie di sottoprogrammi sono incluse nell’ambiente di sviluppo e, inoltre, qualsiasi programmatore può creare ulteriori librerie personali. Il programmatore che ha creato la libreria deve anche creare uno o più file di intestazione che dovranno essere inclusi nei programmi che utilizzeranno la libreria. Con l’introduzione nel linguaggio C++ di nuove funzionalità, come i template e i namespace, che non esistevano nel linguaggio C, le librerie standard sono state riscritte. Nei programmi C++, comunque, si possono usare sia le vecchie versioni delle librerie che le nuove. In genere i file da includere per le nuove versioni hanno lo stesso nome dei vecchi con una ‘c’ davanti e senza l’estensione .h. Per esempio <stdio.h> diventa <cstdio> e <stdlib.h> diventa <cstdlib>. Se si decide di utilizzare le nuove versione delle librerie bisogna anche specificare !10 Corso sul Linguaggio C++ Edizione 2010 che nel programma si userà il namespace std che è quello in cui sono inclusi tutti i nomi di funzione e gli identificatori delle librerie standard. Per esempio si può scrivere: #include <cstdlib> #include <iostream> using namespace std; invece di #include <stdlib.h> #include <iostream.h>. Attenzione, il nuovo file <iostream> richiama al suo interno tutte le librerie standard (quelle che iniziano con “st”) per cui nella maggior parte dei programmi si può anche includere solo la libreria <iostream>: #include <iostream> using namespace std; Nel seguito ho utilizzato la vecchia sintassi che è compatibile con tutte le versioni del linguaggio. 1.7.Le funzioni di I/O Fra le istruzioni più importanti di un programma ci sono quelle di input/output cioè quelle che servono per far leggere i dati da elaborare (input) e visualizzare i risultati dell’elaborazione (output). Per ora utilizzeremo due sole istruzioni con sintassi semplificata, l’istruzione cout, che abbiamo già visto, per l’output e l’istruzione cin per l’input. La sintassi è la seguente: per l’output cout espressione << per l’input cin variabile >> cin sta per console input e dice al computer mandare i dati provenienti dall’unità di input standard (normalmente la tastiera) dentro una o più variabili (se ci sono più variabili, durante l’esecuzione bisogna inserire più dati con un ritorno a capo oppure uno spazio tra uno e l’altro). Il simbolo >> può essere tradotto con “memorizza in” e dice al computer di prelevare un dato dalla console di input e memorizzarlo nelle variabile. 1.8.Il debug dei programmi con Dev C e Dev Pas Dopo aver installato il compilatore C o pascal e il debugger seguendo le note di installazione riportate sopra, normalmente il compilatore viene impostato automaticamente per generare un programma eseguibile il più possibile efficiente. Purtroppo per poter eseguire il debug è necessario inserire nel programma eseguibile riferimenti al programma sorgente che aumentano le dimensioni del file eseguibile e lo rendono meno efficiente. Quando il compilatore viene installato viene automaticamente impostato per non inserire queste informazioni nel file eseguibile e quindi il debug non funziona! Per farlo funzionare bisogna seguire la seguente procedura: !11 Corso sul Linguaggio C++ Edizione 2010 1. Scegliere Options - Compiler options dal menu ! 2. Spuntare la casella "Generate debugging information" dalla scheda "Linker" della finestra delle opzioni del compilatore e fare click su OK ! 3. Compilare il programma (ricompilare se era stato già compilato) ed eseguire il programma facendo click sull'apposita icona della barra degli strumenti oppure sulla voce del menù Execute oppure con F8. ! 4. Comparirà la finestra del debug con una copia del programma sorgente. Facendo click su Run (o sull'icona corrispondente della barra degli strumenti ! ) il programma si avvierà, comparirà la finestra del programma (la console per i programmi non windows) e la prima istruzione eseguibile verrà evidenziata nella finestra del debug. !12 Corso sul Linguaggio C++ Edizione 2010 ! 5. Dalla finestra del sorgente è possibile: inserire e togliere punti di interruzione facendo clik con il mouse a sinistra dell'istruzione corrispondente (un punto di interruzione viene visualizzato con un quadratino nero); eseguire un'istruzione alla volta facendo click su l’icona Step (! ) della barra degli strumenti oppure su Next ( ! ), la differenza tra le due si nota solo se l’istruzione è una chiamata ad un sottoprogramma (con Step si passa alla prima istruzione del sottoprogramma, con Next verrà eseguito l’intero sottoprogramma e si passa all’istruzione successiva); far proseguire il programma fino al prossimo punto di interruzione (! le variabili locali (! ), visualizzare ), ecc. Note per l’esecuzione del debugger Se si vuole eseguire un programma utilizzando il debug bisogna avere le seguenti accortezze: 1. la cartella che contiene il file sorgente deve trovarsi sullo stesso volume dove è installato il dev-pas o il dev-c; 2. prima di eseguire il debug accertarsi di aver compilato il programma con l’opzione “Generate debugging information” attivabile dal menù Options!Compiler Options; 3. se si utilizzano dei file in input questi devono trovarsi nella cartella BIN contenuta nella cartella di installazione del del C (per es: C:\Dev-C++\Bin). Note per la versione 4.9.9.2 L’ultima versione del DEV C, presenta dei bug per quanto riguarda i nomi dei file. Se si salva il programma con nomi di file lunghi che contengono caratteri speciali come spazi e parentesi tonde, possono verificarsi problemi. Per esempio se si salva un programma con il nome “Prova programma” e poi si salva nella stessa cartella un altro programma con nome “Prova programma (1)” quando si manda in esecuzione il secondo parte l’eseguibile del primo invece del secondo. Si raccomando di usare nomi corti e non contenenti caratteri speciali. !13 Corso sul Linguaggio C++ Edizione 2010 2 Capitolo 2.Tipi di dati, variabili e costanti, le espressioni 2.1.Calcolo dell’area di un rettangolo Un qualsiasi programma, in genere, legge dei dati e produce dei risultati. Tutti i dati utilizzati, sia quelli iniziali che quelli intermedi e finali vengono conservati in memoria. I dati di un programma si distinguono in due categorie: variabili e costanti. I dati variabili sono quelli che possono cambiare da un’esecuzione all’altra dello stesso programma o durante una stessa esecuzione, i dati costanti, invece, assumono sempre lo stesso valore ogni volta che si esegue il programma. Supponiamo di voler calcolare l'area di un rettangolo i cui lati hanno valori interi. Entrano in gioco due variabili, la base e l'altezza, il cui prodotto è ancora un valore intero, l'area appunto. Il programma potrebbe essere il seguente: #include <iostream.h> #include <stdlib.h> // Calcolo area di un rettangolo int main() { int base, altezza; int area; cout << "Calcolo AREA RETTANGOLO \n \n"; cout << "Valore base: "; cin >> base; cout << "\nValore altezza: "; cin >> altezza; area = base*altezza; cout << "\nBase: " << base << " Altezza: " << altezza; cout << "\nArea: " << area << "\n\n"; } system("PAUSE"); return 0; Per rendere evidente la funzione espletata dal programma si è inserito un commento: // Calcolo area rettangolo Il doppio simbolo // indica inizio di commento. Tutto ciò che segue fino alla fine della riga non viene preso in considerazione dal compilatore: serve solo a scopo documentativo. I commenti possono estendersi su più linee e apparire in qualsiasi parte del programma. Naturalmente, per quanto notato prima, il doppio // deve precedere ogni commento in una nuova !14 Corso sul Linguaggio C++ Edizione 2010 riga. Se il commento si estende su più righe, può essere più comodo adottare un altro sistema: fare precedere il commento da /* e inserire */ alla fine del commento. Tutto ciò che appare nelle zone così racchiuse non viene preso in considerazione dal compilatore e non ha alcuna influenza sul funzionamento del programma, è però importantissimo per chi legge il programma: è infatti nelle righe di commento che viene specificato il senso delle istruzioni che seguiranno. Cosa, questa, non immediatamente comprensibile se si leggono semplicemente le istruzioni del linguaggio. Subito dopo main() sono presenti le dichiarazioni delle variabili intere necessarie: int base, altezza; int area; La parola chiave int specifica che l'identificatore che lo segue si riferisce ad una variabile di tipo intero; dunque base, altezza e area sono variabili di questo tipo. Anche le dichiarazioni così come le altre istruzioni devono terminare con un punto e virgola. Nel nostro esempio alla dichiarazione del tipo della variabile corrisponde anche la sua definizione che fa sì che le venga riservato uno spazio in memoria centrale. Il nome di una variabile la identifica, il suo tipo ne definisce la dimensione e l'insieme delle operazioni che vi si possono effettuare. Le dichiarazioni delle variabili dello stesso tipo possono essere scritte in sequenza separate da una virgola: int base, altezza, area; Dopo la dichiarazione di tipo sono specificati gli identificatori di variabile, che possono essere in numero qualsiasi, separati da virgola e chiusi da un punto e virgola. L’istruzione cout << "Calcolo AREA RETTANGOLO \n \n"; visualizza su video la stringa di caratteri "Calcolo AREA RETTANGOLO” seguita da due righe vuote. Le due istruzioni cout << "Valore base: "; cin >> base; servono per leggere il valore della base. Prima viene visualizzata la frase “Valore base: “, poi il sistema aspetta che l’operatore batta un numero sulla tastiera e lo trasferisce nella variabile base. La forma grafica data al programma è del tutto opzionale; una volta rispettata la sequenzialità e la sintassi, la scrittura del codice è libera. In particolare più istruzioni possono essere scritte sulla stessa linea, si possono inserire spazi e righe vuote dove si desidera. Non bisogna dimenticare, però, di inserire il carattere ; (punto e virgola) dopo ogni istruzione. Lo stile grafico facilita enormemente il riconoscimento dei vari pezzi di programma e consente una diminuzione di tempo nelle modifiche, negli ampliamenti e nella correzione degli errori. Analogamente la riga successiva serve per far leggere il valore dell’altezza e inserirlo nella variabile altezza. L’istruzione area = base*altezza; !15 Corso sul Linguaggio C++ Edizione 2010 è un’istruzione di assegnazione. Essa dice al computer di calcolare il valore dell’espressione a destra dell’uguale e assegnare il risultato alla variabile a sinistra del simbolo =; cioè inserisce nello spazio di memoria riservato a tale variabile il valore indicato. L'operatore asterisco effettua l'operazione di prodotto tra la variabile che lo precede e quella che lo segue, è dunque un operatore binario. Nel linguaggio C++ è possibile assegnare lo stesso valore a più variabili contemporaneamente. Per esempio base = altezza = 5; In questo caso prima verrebbe assegnato il valore 5 alla variabile altezza e quindi, il risultato dell’assegnazione (cioè 5), viene assegnato alla variabile base. Le ultime istruzioni visualizzano su video i valori delle variabili in input e il risultato. 2.2.Definizione di variabili Tutte le variabili utilizzate in un programma devono essere preventivamente definite. La definizione di una variabile è un’istruzione che serve per creare la variabile in memoria. In generale la definizione di variabili ha la seguente forma: Modificatore della modalità di memorizzazione Modificatore di tipo Tipo di dato , Identificatore Valore iniziale = Opzionalmente si può premettere ad una definizione di variabile la modalità di memorizzazione che indica al computer come e dove memorizzare la variabile. Le modalità di memorizzazione sono le seguenti: Modalità memorizzazione d i Dichiara al compilatore che l’elemento va memorizzato ….. auto Nelle celle della memoria centrale dell’elaboratore (è quella di default) register Negli elementi hardware dell’elaboratore che costituiscono la memoria più veloce. In genere i compilatori cercano di memorizzare queste variabili nei registri interni della CPU o nella memoria cache. extern È definito in un altro file sorgente static Nella memoria centrale ma il valore non viene mai cancellato durante l’esecuzione del programma volatile Nella memoria centrale, ma il valore può essere modificato, oltre che dal programma anche direttamente dall’hardware. Questa modalità viene utilizzata in casi particolari come quando si devono gestire i dati provenienti o da inviare ad un MODEM o ad altre periferiche. Per esempio: !16 Corso sul Linguaggio C++ Edizione 2010 static int a dice al computer di creare una variabile di tipo intero in uno spazio che non deve essere cancellato quando il sottoprogramma che contiene la definizione termina (normalmente tutte le variabili di un sottoprogramma vengono cancellate quando il sottoprogramma termina l’esecuzione). Oltre alla modalità di memorizzazione si può inserire in una definizione di variabile un modificatore di tipo, i principali modificatori di tipo sono i seguenti: modificatore di tipo Descrizione const Indica al compilatore che il dato definito è un dato costante che quindi non potrà subire variazione durante l’esecuzione del programma signed Il valore è un numero con il segno unsigned Il valore è un numero senza segno Short Riduce il numero di byte riservati a contenere il dato Long Aumenta il numero di byte riservato a contenere il dato Per esempio la definizione short int num; dichiara una variabile che occupa solo due byte (numeri da -32768 a + 32767); short unsigned int num; dichiara una variabile sempre di due byte ma senza segno e quindi i valori possono andare da 0 a 65536 Il tipo di dato indica al sistema la dimensione della variabile e l'insieme delle operazioni che si possono effettuare su di essa. La dimensione può variare rispetto all'implementazione; molte versioni del C++, come quelle sotto il sistema operativo MS DOS per esempio, riservano per gli int uno spazio di due byte, il che permette di lavorare su interi che vanno da -32768 a +32767, altre implementazioni, come per esempio quelle per ambiente Windows, riservano uno spazio di quattro byte permettendo valori compresi fra -2.147.483.648 e +2.147.483.647. Tra le operazioni permesse fra int vi sono: la somma (+), la sottrazione (-), il prodotto (*) e la divisione (/). Nel seguito esamineremo i principali tipi di dato. Ogni variabile deve avere un nome che identifica la variabile all’interno del programma e per questo viene detto identificatore. Esistono delle regole da rispettare nella costruzione degli identificatori: devono iniziare con una lettera o con un carattere di sottolineatura _ e possono contenere solo lettere, cifre e _ (non possono contenere simboli di punteggiature, segni di operazione e altri caratteri speciali). Per quanto riguarda la lunghezza occorre tenere presente che soltanto i primi trentadue caratteri sono significativi, anche se nelle versioni del C meno recenti questo limite scende a otto caratteri. Sarebbe comunque opportuno non iniziare il nome della variabile con il carattere di sottolineatura ed è bene tenere presente che le lettere accentate, permesse dalla lingua italiana, non sono considerate lettere ma segni grafici e le lettere maiuscole sono considerate diverse dalle rispettive minuscole. Oltre a rispettare le regole precedentemente enunciate, un identificatore non può essere una parola chiave del linguaggio, né può essere uguale ad un nome di funzione in libreria o scritta dal programmatore. !17 Corso sul Linguaggio C++ Edizione 2010 In generale è inoltre bene dare alle variabili dei nomi significativi, in modo che, quando si debba intervenire a distanza di tempo sullo stesso programma, si possa facilmente ricostruire l'uso che si è fatto di una certa variabile. A differenza di altri linguaggi di programmazione, nel linguaggio C, l’istruzione di definizione di una variabile può essere utilizzata anche per inizializzarla ovvero per assegnargli il valore iniziale. Per esempio: int somma = 0, prod=1; crea la variabile somma e ci mette dentro il valore 0, poi crea la variabile prod e ci mette 1. 2.3.Costanti Nel paragrafo 2.1 abbiamo esaminato il programma per calcolare l’area di un rettangolo. Supponiamo ora di voler scrivere un programma per calcolare l’area del cerchio che sappiamo si calcola con la formula area = πr2, dove π è un numero fisso che corrisponde approssimativamente a 3,14. Quindi per calcolare l’area del cerchio bisogna indicare al computer anche il valore di π che è un dato costante del problema. Il programma potrebbe essere il seguente: #include <iostream.h> #include <stdlib.h> #define PIGRECO 3.14 // Calcolo area di un cerchio int main() { float raggio, area; cout << "Calcolo AREA Cerchio \n \n"; cout << "Raggio: "; cin >> raggio; area = PIGRECO*raggio*raggio; cout << "\nArea: " << area << "\n\n"; } system("PAUSE"); return 0; In questo caso le variabili raggio e area sono state dichiarate di tipo float perché questo tipo può contenere anche numeri con la virgola, inoltre è stata inserita la direttiva al preprocessore #define PIGRECO 3.14 Questo dice al preprocessore che ogni volta che trova la serie di caratteri PIGRECO deve sostituirli con il numero 3.14. In effetti se nel programma sorgente togliamo la direttiva define e sostituiamo l’istruzione area=PIGRECO*raggio*raggio con area = 3.14 * raggio * raggio il programma eseguibile sarebbe esattamente lo stesso. In pratica una costante è una valore fisso nel programma e noi possiamo indicarlo nelle istruzioni o scrivendo il valore o definendo un nome simbolico che rappresenta il valore. I valori costanti che si possono inserire in un programma C++ possono essere di vario tipo: • Numeri decimale interi (per esempio 10, 30, -56, 4545445 …) • Numeri ottali interi (per esempio 064, 010, … ) iniziano sempre con uno zero e contengono le cifre da 0 a 7; • Numeri esadecimali interi (0x23F, 0x30, 0xBB …) iniziano sempre con 0x e contengono le cifre da 0 fino a F; !18 Corso sul Linguaggio C++ Edizione 2010 • • • Numeri decimali con la virgola o in virgola mobile (per esempio 3.14, -2.0, -0.1, 6.567E+7, 0.450123e-3 …). Per rappresentare la virgola si usa il punto; Un carattere singolo (per esempio ‘a’, ‘B’, ‘7’…), deve essere racchiuso tra due apici; Una stringa di caratteri (“Mario”, “Buongiorno mondo”, ….), la stringa è un gruppo di caratteri racchiuso tra virgolette (“); tutte le stringhe terminano con il carattere speciale \0 (codice ascii 0) che viene inserito automaticamente dal compilatore; Quando un certo valore viene utilizzato in modo ricorrente è opportuno rimpiazzarlo con un nome simbolico; inoltre spesso conviene definire le costanti per aumentare la leggibilità di un programma perché in alcuni casi il nome di un dato indica più chiaramente, a chi legge il programma, il significato dell’istruzione. Per esempio se abbiamo codificato le mansioni svolte dagli impiegati di una azienda con dei numeri e 1 significa operaio, 2=impiegato, 9=dirigente l’istruzione mansione=DIRIGENTE è più chiara di mansione =9 per sostituire la costante 9 con la parola DIRIGENTE dobbiamo inserire la direttiva #define DIRIGENTE 9 nel programma prima dell’uso della costante. Il nome di una costante può essere qualsiasi identificatore valido in C++, comunque è uso comune utilizzare esclusivamente caratteri maiuscoli per le costanti e caratteri minuscoli per le variabili per distinguere chiaramente le une dalle altre. In sintesi, l'uso delle costanti migliora due parametri classici di valutazione dei programmi: flessibilità e possibilità di manutenzione. Con il linguaggio C++, oltre alla direttiva define (presente nel linguaggio C) per definire le costanti si può utilizzare il modificatore di tipo const e quindi si può anche scrivere: const float PIGRECO=3.14; 2.4.I tipi di dati e le operazioni permesse Una delle caratteristiche che differenziano il linguaggio C rispetto agli altri linguaggi è la maggiore disponibilità di tipi di dati e di operatori. Per quanto riguarda i tipi di dati abbiamo già visto la possibilità di utilizzare i modificatori di tipo e di modalità di memorizzazione che di fatto amplia la gamma dei tipi disponibili. Negli esempi che abbiamo visto finora abbiamo utilizzato due tipi di dato, numeri interi (tipo int) e numeri con la virgola (tipo float). Ogni linguaggio di programmazione mette a disposizione dei programmatori un insieme di tipi di dati che il programmatore può utilizzare come contenitori per i dati dei suoi programmi. Nel linguaggio C++ i tipi di dato fondamentali sono: !19 Corso sul Linguaggio C++ Edizione 2010 void bool elementari char int float Tipi di dato double enum array strutturati strutture union puntatori ! Il tipo int Il tipo int viene utilizzato per memorizzare numeri interi relativi. Il numero viene scritto nella memoria del computer in binario, i numeri negativi (non ammessi se si utilizza il modificatore di tipo unsigned) vengono rappresentati con il complemento a 2. A seconda del modificatore di tipo utilizzato abbiamo: short int signed short int numeri da - 32768 a +32 767, occupa 2 byte, unsigned short int Numeri da 0 a 65535 occupa 2 byte long int signed long int numeri da -2147483648 a +2147483647 occupa 4 byte Unsigned long int Numero da 0 a 4294967295, 4 byte int La dimensione può coincedere con short int o con long int a seconda dell’implementazione. In genere quelle sotto il sistema MS-DOS corrispondevano a short int, quelle sotto Windows come quella che utilizziamo noi (Dev-C++) corrispondono a long int Le operazioni permesse sono: la somma (+), la sottrazione (-), il prodotto (*), e la divisione (/), il modulo o resto della divisione (%), l’assegnazione (=), l’incremento (++) e il decremento (--), oltre alle combinazioni delle quattro operazioni con l’assegnazione (+=, -=, *=, / =). !20 Corso sul Linguaggio C++ Edizione 2010 Il tipo char Una variabile di tipo char può memorizzare un solo carattere. In realtà nella memoria viene memorizzato il codice ASCII del carattere e quindi un numero intero da 0 a 255. Per questo motivo una variabile char occupa un solo byte ed è compatibile con il tipo intero. Per questo tipo di variabili sono definite tutte le operazioni definite per i numeri interi, in più è possibile assegnare alla variabile anche un singolo carattere, per esempio, visto che il codice ASCII della lettera ‘B’ è 66, le due istruzioni seguenti sono equivalenti:: e char lett=’B’; char lett = 66; Una variabile di tipo char può essere utilizzata anche per contenere numeri interi senza segno da 0 a 255 oppure numeri interi con segno da -128 a + 127, a seconda se si definisce come unsigned char o signed char ed è compatibile con i tipi numerici, ma se viene inviata in output con cout verrà visualizzato sempre il carattere corrispondente al codice ASCII memorizzato e non il numero. Come detto prima sulle variabili di tipo char sono definite tutte le operazioni definite con il tipo intero, quindi le quattro operazioni principali (+, -, *, /) ma anche incremento e decremento (++, --), il modulo (%) e le combinazioni delle quattro operazioni con l’assegnazione (+=, -=. *=, /=). Quindi è corretta sintatticamente un’istruzione del tipo: lett=lett +2; Dopo questa operazione, supponendo che la variabile lett sia stata creata con l’istruzione precedente di questo paragrafo, il codice ASCII presente nella variabile viene aumentato di 2 e quindi la lettera contenuta non sarà più ‘B’ ma ‘D’. Da notare che le costanti di tipo char, ovvero costituite da un solo carattere, si racchiudono tra due apostrofi (‘), mentre le costanti stringa sono sempre racchiuse utilizzando il doppio apice (“), per esempio ‘A’, ‘k’ sono costanti di tipo char mentre “Mario” è una costante di tipo stringa. Oltre al tipo char, in molte versioni del linguaggio, è stato inserito anche il tipo wchar_t che occupa 2 bytes (16 bit) invece di uno e viene utilizzato per memorizzare i caratteri con il codice Unicode invece del codice ASCII. Il codice Unicode permette di rappresentare un numero di caratteri molto maggiore di 255, praticamente tutti i caratteri esistenti nelle lingue del mondo. I tipi float e double I due tipi float e double servono per memorizzare i numeri reali. La rappresentazione interna è quella binaria virgola mobile. Ogni variabile di tipo float occupa 4 bytes e può contenere numeri fino a 10±38 ma di tutte le cifre del numero solo le sette più significative sono memorizzate esattamente. Il tipo double occupa 8 bytes, la rappresentazione interna è sempre binario virgola mobile, può memorizzare numeri fino a 10±308 con esatte le 15 cifre più significative. Per questi tipo non si può utilizzare i modificatori short,long, signed e unsigned, in pratica i numeri vengono memorizzati sempre con il segno e il tipo float corrisponde a un ipotetico short double, solo per il tipo double si può anche utilizzare il modificatore long con il DEV-C++, le variabili di tipo long double occupano 12 bytes. Il fatto che i numeri sono memorizzati in binario virgola mobile permette di memorizzare numeri molto grandi e anche molto piccoli in modo abbastanza preciso e in poco spazio ma crea anche un problema, infatti, i numeri frazionari che in decimale hanno un numero finito di cifre spesso in binario diventano periodici e quindi con infinite cifre, per questo motivo anche un numero come 0.1 non può essere memorizzato esattamente in questo tipo di variabili. Il tipo bool Le variabili di tipo bool occupano un solo byte e possono contenere solo i due valori: booleani true e false. Per questo tipo sono definite solo le operazioni logiche ovvero && (AND), || (OR), ! (NOT). !21 Corso sul Linguaggio C++ Edizione 2010 È importante notare che queste operazioni sono diverse dalle corrispondenti operazioni che operano sui bit e che si applicano a tutte i tipi di variabili ovvero & (AND), | (OR), e ~(inversione dei bit). Un esempio di dichiarazione di variabile bool è il seguente: bool finito = false; Nel linguaggio C il tipo bool non esiste, in sua vece si può utilizzare il tipo int infatti, come qualsiasi altro dato, anche il risultato di un espressione logica è un numero binario e precisamente il numero 0 corrisponde al valore falso mentre il numero 1 o un numero diverso da zero corrisponde al valore vero. Questo significa che, sia nel linguaggio C che nel linguaggio C++, si possono utilizzare come variabili logiche anche le variabili intere. L’operatore speciale sizeof Per ottenere esattamente il numero di byte occupati da una variabile o da un tipo di dati si può ricorrere all’operatore speciale sizeof ovvero “misura di”. La sintassi è la seguente: sizeof(<TipoDato>) oppure sizeof(<NomeVariabile>) L’esempio che segue è un programma che mostra la quantità di memoria occupata da alcune variabili elementari.: #include <iostream.h> #include <stdlib.h> int main() { /* visualizza il numero di bytes assegnati in memoria ad alcune variabili */ int i; short int si; long int li; char c; wchar_t wc; float f; bool b; double d; long double ld; cout << "bytes occupati da char: " << sizeof(c)<< "\n"; cout << "bytes occupati da float: " << sizeof(f)<< "\n"; cout << "bytes occupati da double: " << sizeof(d)<< "\t long double: " << sizeof(ld) <<"\n"; cout << "bytes occupati da bool: " << sizeof(b)<< "\n"; cout << "bytes occupati da int: " << sizeof(i)<< "\t long int: " << sizeof(li) << "\t short int: " << sizeof(si)<<"\n"; cout << "bytes occupati da wchar_t: " << sizeof(wc) << "\n" ; system("PAUSE"); return 0; } Mandandolo in esecuzione con il DEV-C++ si otterrà questo risultato: ! 2.5.Le espressioni Un espressione è una sequenza di variabili, costanti e operatori, eventualmente racchiusi a gruppi tra parentesi tonde () per modificare l’ordine dei calcoli. Per esempio: x = a * 3 / (somma - 5.3) Nell’esempio x, a e somma sono variabili, 3 e 5.3 sono costanti, gli altri segni, escluse le parentesi che servono solo per modificare l’ordine dei calcoli, sono operatori. Un’espressione può anche essere definita come una sequenza di operazioni elementari dove ogni operazione è !22 Corso sul Linguaggio C++ Edizione 2010 definita da un segno detto operatore e da uno o più valori che possono essere variabili o costanti detti operandi. Nel linguaggio C sono definiti molti più operatori rispetto agli altri linguaggi. Le espressioni hanno molte più possibilità e questa è una delle caratteristiche che lo rende preferibile per alcune applicazioni. Per esempio sono stati introdotti operatori come quello dell’incremento o decremento di una variabile che, in genere, sono presenti in tutti i linguaggi macchina ma non nei linguaggi ad alto livello. In questi linguaggi operazioni come a++ o a-- del linguaggio C, vengono sostituite con istruzioni del tipo a=a+1 o a = a-1, le quali però, tradotte in linguaggio macchina, in genere, danno luogo a programmi meno efficienti. Gli operatori del linguaggio C++ si possono raggruppare nelle seguenti categorie: • Operatori di assegnamento • Operatori aritmetici • Operatori relazionali • Operatori logici • Operatori sui bit • Operatori speciali Gli operatori di assegnamento L’operatore principale di assegnamento è il carattere =. Per esempio: base = 3; Con questa istruzione viene inserito 3 all’interno della variabile con nome ‘base’, questo significa che si inserisce il numero 3 nello spazio di memoria riservato alla variabile base. x = y; In questo caso il valore della variabile y vene copiato nella variabile x, le due variabili continueranno ad occupare spazi di memoria separati e indipendenti. Nella maggior parte dei casi l’istruzione di assegnamento ha la seguente sintassi: <identificatore> = <espressione> e serve per inserire il risultato dell’espressione a destra del simbolo = nella variabile con nome <identificatore>. Per il linguaggio C++ questa è un’operazione che produce un valore come risultato. Il valore risultante corrisponde al valore assegnato alla variabile. Per questo motivo, in questo linguaggio, sono possibili le assegnazioni multiple del tipo: prezzo = importo = q * pu; in questo caso viene prima calcolato il prodotto delle variabili q e pu, il risultato viene assegnato alla variabile importo, lo stesso risultato viene assegnato anche alla variabile prezzo. Esistono anche altri operatori di assegnazione, precisamente gli operatori +=, -=, *=, /=, %=, <<=, >>=, &=, !=, ^=. Si tratta della combinazione di operazioni aritmetiche o logiche con l’assegnazione. Sono state introdotte in C perché possono essere tradotte in modo efficiente in linguaggio macchina. Per esempio le istruzioni: km += 37; k1 += k2; a += (b/2); equivalgono rispettivamente a: km = km+37; k1 = k1+k2; a = a+(b/2); Chi è pratico di programmazione avrà notato che questa è l’istruzione tipica degli accumulatori. Ma l’operatore = si può anche utilizzare insieme ad altri operatori aritmetici, per esempio: km -= 6; // toglie 6 ai km percorsi ovvero km = km - 6 lato *= 2; // moltiplica il lato per 2 ovvero lato = lato * 2 volume /= 3; // divide il volume per 3 ovvero volume = volume / 3 !23 Corso sul Linguaggio C++ Edizione 2010 quindi, più in generale vale la seguente sintassi: <identificatore> <operatore>= <espressione> corrisponde a <identificatore> = <identificatore> <operatore> <espressione> La sintassi completa per l’assegnamento è <lvalue> <operatore di assegnamento> <rvalue> dove <lvalue> sta per left value,ovvero valore di sinistra e può essere un identificatore ma anche un elemento di un vettore o un’espresssione che da come risultato un puntatore dereferenziato; <operatore di assegnamento> è uno degli operatori che abbiamo visto (=, +=, -=, *=, /=, %=, <<=, >>=, &=, !=, ^=); <rvalue> (right value) è una qualsiasi espressione che restituisce un valore compatibile con il tipo di <lvalue>. Operatori aritmetici Naturalmente sono presenti nel linguaggio C++ gli operatori aritmetici disponibili negli altri linguaggi per le operazioni fondamentali: addizione (+), sottrazione(-), moltiplicazione (*), divisione (/), resto della divisione (%). Questi operatori vengono detti binari perché l’operazione viene compiuta su due operandi, per esempio: b+c a * 10 5/3 7-c in generale per far eseguire l’operazione al computer si scrivono i due operandi con il segno di operazione (l’operatore) in mezzo. Gli operatori binari non cambiano il valore dei due operandi ma memorizzano il risultato. L’operazione % corrisponde al resto della divisione e si può eseguire solo con numeri interi, per esempio: 10 % 3 da come risultato 1 e 20 % 5 da come risultato 0. Oltre agli operatori visti sopra sono disponibili nel C++ anche l’operatore ++ e -- detti rispettivamente di incremento e decremento. Questi due operatori si applicano ad un solo operando modificandone il valore per cui vengono detti unari. Il loro effetto è rispettivamente quello di incrementare o decrementare di 1 il valore dell’operando. Per esempio dopo l’esecuzione del seguente pezzo di programma: int a=10, b=-20; a++; b--; nella variabile a ci sarà il valore 11 e nella variabile b il valore -21. Per aggiungere uno alla variabile z si può scrivere in due modi: ++z; z++; cioè mettere l'operatore ++ prima o dopo del nome della variabile. Le due notazioni vengono dette rispettivamente prefissa e postfissa In generale, le due forme sono equivalenti. La differenza importa solo quando si scrive una espressione che contiene z++ o ++z. Scrivendo z++, il valore di z viene prima usato poi incrementato: int x,z; z = 4; x = z++; // due variabili intere // z vale 4 // anche x vale 4 ma z vale 5 !24 Corso sul Linguaggio C++ Edizione 2010 Difatti, prima il valore di z (4) è stato assegnato ad x, poi il valore di z è stato incrementato a 5. Scrivendo ++z, il valore di z viene prima incrementato e poi usato: int x,z; z = 4; x = ++z; // due variabili intere // z vale 4 // ora x vale 5 come z Difatti, prima il valore di z (4) è stato incrementato a 5, poi il nuovo valore di z (5) è stato assegnato ad x. Bisogna stare attenti e cercare di evitare che in una stessa espressione compaia più volte la stessa variabile con applicate operazioni di incremento o decremento postfisse perché i risultati dipendono da quando il compilatore fa eseguire l’incremento o decremento, per esempio dopo l’esecuzione delle seguenti istruzioni: int b=5, a ; a=b++ + b++ ; in b ci sarà il valore 7 e in a ci sarà il valore 10 perché i due incrementi di b verranno eseguiti dopo l’addizione, mentre dopo l’esecuzione delle seguenti istruzioni: int b=5; bool a; a= b++ < b; in a ci sarà il valore vero perchè il compilatore Dev-C++ fa eseguire l’incremento di b prima di eseguire il confronto <. Operatori relazionali Gli operatori relazionali sono operatori binari, servono per confrontare due valori e producono un risultato booleano ovvero il valore true o false a seconda se il risultato del confronto è vero o falso. Sono i seguenti: > maggiore >= maggiore o uguale < minore <= minore o uguale == uguale != diverso Per esempio: 5 >= 3 da risultato true; 2 == (10 / 5) da risultato true; 7 != 7 da risultato false; Operatori logici Gli operatori logici richiedono come operandi valori o espressioni booleane e il risultato è ancora un valore logico di tipo vero o falso. Gli operatori logici sono: || OR Risultato falso solo se entrambi gli operandi sono falsi && AND Risultato vero solo se entrambi gli operandi sono veri ! NOT inverte il valore di verità dell’unico operando Gli operatori || e && non valutano l’operando di destra se non è necessario, ovvero se il primo operando è vero il risultato di || sarà vero indipendentemente dal valore del secondo operando e quindi non serve valutarlo, nel caso del && se il primo operando è falso il risultato sarà falso senza bisogno di valutare il secondo operando. Per esempio: (5 > 1) || (3>6) è vero mentre (5>1) && (3 > 6) è falso !25 Corso sul Linguaggio C++ Edizione 2010 Operatori sui bit A tutte le variabili viene assegnato uno spazio di memoria dove viene memorizzato il valore contenuto nella variabile. Il valore è sempre rappresentato tramite una serie di bit. Ogni bit è una cifra binaria e quindi può assumere solo i due valori 0 e 1 che nell’algebra booleana rappresentano rispettivamente i valori falso e vero, per cui il valore di una qualsiasi variabile può essere visto come una sequenza di valori vero e falso. Le operazioni sui bit sono operazioni effettuate sui singoli bit che costituiscono il valore degli operandi considerati indipendentemente uno dall’altro. Tutte queste operazioni, come pure quella di incremento e decremento, sono in genere disponibili in tutti i linguaggi macchina e quindi possono essere tradotte in modo molto efficiente in questo linguaggio avvalorando la tesi che vuole il linguaggio C più vicina al linguaggio macchina rispetto agli altri linguaggi di alto livello. Gli operatori sui bit sono: Operator e Descrizione & AND bit a bit, il risultato è 1 solo se entrambi i bit sono 1 | OR bit a bit, il risultato è 1 solo se almeno uno dei due bit è 1 ^ XOR (OR esclusivo) bit a bit, il risultato è 1 solo se uno solo dei due bit è=1 ~ Complemento a 1 ovvero inversione di tutti i bit >> Shift a destra. Spostamento a destra di tutti i bit con propagazione del bit del segno (il primo bit più a sinistra), in pratica corrisponde a dividere il numero binario per 2x dove x è il numero dei bit di scorrimento. Se non si vuole la propagazione del segno bisogna utilizzare le variabili unsigned. << Shift a sinistra ovvero spostamento a sinistra di tutti i bit e riempimento dei bit che si liberano a destra con zeri. In pratica corrisponde a moltiplicare il numero per 2x dove x è il numero dei bit di scorrimento. Per esempio supponiamo di aver definito due variabili con l’istruzione short int a=25,b=20, considerato che il tipo short int ha una rappresentazione interna in binario a 16 bit con complemento a 2 per i numeri negativi, il contenuto delle due variabili corrisponderà ai due numeri 25 e 20 scritti in binario e sarà il seguente: a = 0000000000011001 b = 0000000000010100 per cui si avrà che: a & b ! 0000000000010000 = 16 a | b ! 0000000000011101 = 29 a ^ b ! 0000000000001101 = 13 ~a ! 1111111111100110 = -26 ~b ! 11111111111111101011 = -21 a >> 2 ! 0000 0000 0000 0110 = 6 (risultato dello spostamento a destra di due bit) a << 2 ! 0000 0000 0110 0100 = 100 (risultato dello spostamento a sinistra di due bit) !26 Corso sul Linguaggio C++ Edizione 2010 Per verificarlo basta scrivere il seguente programma: #include <iostream.h> #include <stdlib.h> int main() { short int a=25, b=20 ; cout << " a = " << a << "\t b = "<< b << endl; cout << " a & b = " << (a & b) << "\t a | b = " << (a | b) << "\t a ^ b = " << (a ^ b) << endl; cout << "~a = " << (~a) << "\t ~b = " << (~b) << endl; cout << " a << 2 = "<<(a << 2)<<"\t a >> 2 = "<<( a >> 2)<< endl; system("PAUSE"); return 0; } Si noti che invece della sequenza di escape \n si può utilizzare la costante predefinita endl per inviare in output il carattere new line, mandandolo in esecuzione si otterrà il seguente output: ! Regole di valutazione di un’espressione Le espressioni possono essere combinazione di operatori e operandi anche complesse ma comunque portano sempre ad un risultato univoco. Per capire come il computer calcola una espressione bisogna sapere che i calcoli vengono effettuati dall’unità aritmetico-logica (ALU) che è una specie di calcolatrice contenuta nell’unità centrale di elaborazione (CPU) di ogni computer. L’unità aritmetico-logica può eseguire una sola operazione alla volta e quindi per calcolare un’espressione bisogna eseguire in sequenza le varie operazioni memorizzando i risultati parziali così come faremmo noi con una calcolatrice normale. Se per esempio consideriamo l’espressione 5 + 3 * 6, il risultato sarà diverso se eseguiamo prima l’addizione e poi la moltiplicazione o viceversa prima la moltiplicazione e poi l’addizione. Nel primo caso esce 48 nel secondo esce 23. Il programmatore deve conoscere l’ordine con cui il computer eseguirà i calcoli, per questo in ogni linguaggio di programmazione sono state fissate delle regole che indicano come verrà calcolato il valore di una qualsiasi espressione, stabilendo esattamente l’ordine con cui devono essere applicati gli operatori agli operandi. Le regole per il linguaggio C++ sono le seguenti: 1. L’impiego di parentesi. Se si utilizzano le parentesi tonde, le espressioni racchiuse all’interno delle parentesi vengono calcolate prima di eseguire i calcoli posti fuori dalle parentesi. Il compilatore traduce l’espressione in modo da far calcolare prima le espressioni racchiuse nelle coppie di parentesi più interne, poi sostituisce tutto il blocco che inizia con “(“ fino a “)” con il risultato dell’espressione, e procede man mano fino a quelle più esterne; 2. Livello di precedenza degli operatori. Gli operatori sono divisi in gruppi con livelli di precedenza diverso, in mancanza di parentesi vengono prima eseguite prima le operazioni con livello più alto, poi le altre in ordine fino a quelle di livello più basso. Per conoscere il livello di precedenza delle operazioni si veda la Tabella 2 che segue, dove sono riportati tutti gli operatori divisi in gruppi in base al livello di priorità. Per esem!27 Corso sul Linguaggio C++ Edizione 2010 pio le moltiplicazioni e le divisioni vengono eseguite prima delle addizioni e sottrazioni, quindi l’espressione precedente 5 + 3 * 6 produce il risultato 23; 3. Le regole di associatività. Quando c’è una serie di operandi e operatori con lo stesso livello di precedenza, si seguono le regole di associatività. Per la maggior parte degli operatori vale l’associatività a destra, cioè le operazioni vengono eseguite partendo da quella più a sinistra fino a quella più a destra, fanno eccezione gli operatori di assegnamento per i quali vale l’associatività a sinistra. Quindi, quando viene calcolata un espressione scritta in C++ vengono prima calcolate le espressioni racchiuse tra parentesi, poi vengono eseguiti in calcoli in ordine di precedenza degli operatori, se ci si trova con una sequenza di operandi e operatori con lo stesso livello di precedenza si segue la regola dell’associatività, questo significa che se si trova un espressione del tipo: operando1 op1 operando2 op2 operando3 dove <op1> e <op2> sono operatori con lo stesso livello di priorità allora il computer segue la regola dell’associatività, se l’associatività è a destra, come avviene normalmente per la maggior parte delle operazioni, si procede da sinistra verso destra e precisamente si calcolerà: (operando1 op1 operando2) op2 operando3 se invece l’associatività è a sinistra (come avviene per l’operatore =) si procederà da destra verso sinistra e quindi si calcolerà: operando1 op1 (operando2 op2 operando3) Operatori speciali Vedremo solo due operatori speciali: ? utilizzato con tre operandi (ternario), una espressione logica e due espressioni qualsiasi dello stesso tipo, produce il risultato di una delle due espressioni scegliendolo in base al valore dell’espressione logica; , utilizzato con due operandi (binario), ha il più basso livello di priorità, inferiore anche agli operatori di assegnamento, è associativo a destra, fa calcolare dal computer in sequenza le due espressioni, rendendo possibile l’utilizzo del risultato della prima nella seconda; La sintassi dell’operazione ? è la seguente: <espressione logica>?<espressione1>:<espressione2> si utilizza quando vogliamo sceglier tra due risultati diversi in base ad una condizione. Per esempio se lo sconto è del 10% quando si acquista una quantità di prodotto che va da 1 a 5 mentre è del 20% se si acquistano più di 5 unità del prodotto, per assegnare il valore alla variabile sconto possiamo utilizzare la seguente espressione: sconto = quant > 5 ? 20 : 10; Si noti che nell’espressione precedente non servono le parentesi perché in base alla precedenza degli operatori (vedi Tabella 2 sotto) viene eseguita prima l’operazione >, poi l’operazione ? ed infine l’operazione =. Se i possibili sconti fossero stati tre, per esempio 5% fino a due pezzi, 10% da tre pezzi a cinque e 20% oltre i 5 pezzi, avremmo potuto calcolare lo sconto sempre con una sola istruzione nel modo seguente: sconto = quant > 5 ? 20 : (quant >2 ? 10 :5); La sintassi dell’operazione , (virgola) è la seguente: <espressione1>,<espressione2> potendosi ripetere l’operazione quante volte si vuole si possono scrivere anche espressioni del tipo: <espressione1>,<espressione2>,<espressione3>, …….,<espressioneN> !28 Corso sul Linguaggio C++ Edizione 2010 L’effetto di queste operazioni è semplicemente quello di far calcolare al computer le espressioni nell’ordine partendo dalla prima più a sinistra fino all’ultima a destra. Se qualcuna di queste espressioni contiene operazioni di assegnamento, nelle seguenti verranno eventualmente utilizzati i valori delle variabili così come modificate delle espressioni precedenti. Per esempio, con l’istruzione: k = (i=10, j=20, i+j); alla variabile k verrà assegnato il valore 30. Si noti che in questo caso l’utilizzo delle parentesi è indispensabile per far eseguire l’operazione , (virgola) prima dell’assegnamento (operazione =), senza parentesi a k sarebbe stato assegnato il valore 10. !29 Corso sul Linguaggio C++ Edizione 2010 Riassunto degli operatori del linguaggio C++ Tabella 2. Gli operatori disponibili nel linguaggio C++ raggruppati in ordine di precedenza da quelli con precedenza maggiore fino a quelli con precedenza minore (le linee più marcate segnano la divisione tra un gruppo e l’altro). !30 Corso sul Linguaggio C++ Edizione 2010 Simbol o Rappresenta l’operazione …. Sintassi :: visibilità . Selezione elemento <oggetto>.<elemento> -> Selezione elemento <puntatore>-><elemento> [] Indicizzazione <identificatore>[<espressione>] () Chiamata di funzione <nome_funz.>(<lista parametri>) () Conversione di tipo sizeof Dimensione di un oggetto sizeof Dimensione di un tipo di dati <nome_calsse>::<elemento> <tipo dati>(<espressione>) sizeof(<espressione>) sizeof(<tipo dati>) ++ Incremento prefisso ++<variabile> ++ Incremento postfisso <variabile>++ -- Decremento prefisso --<variabile> -- Decremento postfisso <variabile>-- ~ Inversione dei bit ~<espressione> ! NOT - Cambiamento di segno -<espressione numerica> + Più unario +<espressione numerica> & Indirizzo di &<identificatore> * Dereferenziazione *<espressione> Allocazione dinamica new <tipo dato> new delete !<espressione logica> Deallocazione dinamica delete <puntatore> delete[] Deallocazione di un array delete[] <puntatore> () Conversione di tipo nella notazione cast (<tipo dato>)<espressione> .* Selezione indirizzo elemento <oggetto>.*<puntatore_elemento> ->* Selezione indirizzo elemento <puntatore>->*<puntatore_elemen.> * Moltiplicazione <espressione>*<espressione> / Divisione <espressione>/<espressione> % Resto della divisione <espressione>%<espressione> + Addizione <espressione>+<espressione> - Sottrazione <espressione>-<espressione> !31 Corso sul Linguaggio C++ Edizione 2010 Simbol o Rappresenta l’operazione …. Sintassi << Shift a sinistra <espressione> << <numero bit> >> Shift a destra <espressione> >> <numero bit> < Minore di <espressione> < <espressione> <= Minore o uguale a <espressione> <= <espressione> > Maggiore di >= Maggiore o uguale a <espressione> >= <espressione> == Uguale a <espressione> == <espressione> != Diverso da <espressione> != <espressione> & And bit a bit <espressione> & <espressione> ^ XOR (OR esclusivo) bit a bit <espressione> ^ <espressione> | OR bit a bit <espressione> | <espressione> && AND logico <espr.logica> && <espr.logica> <espressione> > <espressione> || OR logico ? Espressione condizionale <espr.logica>?<espr1>:<espr2> = Assegnazione <identificatore>=<espressione> *= /= Assegnazione composta %= += -= <<= >>= &= |= ^= <identificatore><operatore>=<espressione > , <espr.logica> || <espr.logica> Virgola (esegue le due espressioni in sequenza) <espressione1>,<espressione2> 2.6.La conversione di tipo Abbiamo visto che una espressione può consistere in più calcoli e che il computer esegue questi calcoli uno alla volta memorizzando i risultati intermedi in variabili temporanee che poi utilizza nei calcoli successivi. Il tipo di queste variabili temporanee dipende dall’operazione e in genere è sempre dello stesso tipo degli operandi utilizzati. Per esempio consideriamo il seguente programma: int main() { int uno=1, due=2; float tre; tre = uno/due; cout << tre <<endl; !32 Corso sul Linguaggio C++ Edizione 2010 } system("PAUSE"); return 0; Ci aspetteremmo di vedere in output il risultato 0.5, visto che la variabile tre è di tipo float, invece verrà visualizzato 0. Questo dipende dal fatto che con il linguaggio C++, il calcolo uno/due, produce un risultato di tipo int essendo i due operandi uno e due entrambi di tipo int. Il risultato sarà quindi 0 e non 0.5. Il problema si verifica quando in un’espressione gli operandi sono di tipo diverso, infatti ci sono operazioni che permettono, entro certi limiti, l’utilizzo di operandi di tipo diverso, per esempio possiamo eseguire operazioni aritmetiche con un operando di tipo double e un altro di tipo int oppure, come nel programma precedente, possiamo assegnare ad un variabile di tipo float un valore intero. Quando gli operandi di un’operazione sono di tipo diverso, il linguaggio C++ sceglie per il risultato temporaneo dell’operazione, il tipo più capiente tra quello degli operandi in modo da evitare perdita di informazioni, e, prima di fare il calcolo, converte l’altro operando nel tipo scelto. Si dice in questo caso che viene effettuata una conversione implicita di tipo detta cast implicito. Il tipo double è più capiente del tipo float ed entrambi sono più capienti del tipo int perché qualsiasi numero intero può essere memorizzato in una variabile di tipo float o double (che accettano rispettivamente numeri fino a 1038 e 10308) mentre se spostiamo una variabile di tipo float in una intera perderemo la parte dopo la virgola. Ogni volta che si assegna un valore di un tipo più capiente ad una variabile meno capiente si può avere una perdita di informazioni, per esempio se assegniamo un valore double ad una variabile float è molto probabile che ci sarà una diminuzione del numero di cifre significative corrette. Se mettiamo in ordine i tipi numerici dal meno capiente al più capiente abbiamo la seguente sequenza: char!short int!int!long int!float!double!long double per esempio dopo l’esecuzione delle seguenti istruzioni: int uno=1, float due=2.0, tre; tre = uno/due; alla variabile tre verrà assegnato correttamente il valore 0.5 perché il computer quando esegue l’operazione uno/due convertirà automaticamente il valore di uno in float, eseguirà una divisione tra numeri con la virgola e memorizzerà il risultato temporaneo in una variabile di tipo float. È anche possibile indicare al computer il tipo di dati in base al quale deve essere svolta l’operazione usando il cosiddetto cast esplicito la cui sintassi è: (<tipo dati>)<espressione> Per esempio per far funzionare correttamente il programma precedente, senza modificare il tipo delle variabili, si può modificare l’istruzione per la divisione nel seguente modo: int main() { int uno=1, due=2; float tre; tre = (float) uno/due; cout << tre <<endl; system("PAUSE"); return 0; } !33 Corso sul Linguaggio C++ Edizione 2010 premettendo la parola (float) tra parentesi all’espressione, si forza il computer ad eseguire i calcoli con il tipo float. È possibile convertire i dati da un formato ad un altro anche utilizzando le funzioni di conversione la cui sintassi è: <tipo dati>(<espressione>) per esempio: int(10.5*3) → 31 invece di 31.5, char(6*10+5) produce il numero 65 in uno spazio di un solo byte. Ma in questo caso l’espressione viene calcolata normalmente e, solo dopo, il risultato viene convertito nel formato specificato. Quindi se nel programma precedente avessimo scritto float(uno/due) invece di (float)uno/due avremmo ottenuto come risultato 0 invece di 0.5, perché l’espressione uno/due tra due numeri interi da 0 e questo risultato, trasformato in float, è sempre 0. 2.7.Le enumerazioni Fra i tipi elementari introdotti nel paragrafo 2.4 c’è anche il tipo enum. In realtà non si tratta di un vero tipo di dati ma di un insieme di possibili tipi che il programmatore può definire. In pratica il programmatore può definire un nuovo tipo attraverso un elenco di identificatori che rappresentano i possibili valori che questo tipo può assumere. Per esempio: enum colori {giallo, arancione, rosso, verde, blu}; // (1) In questo modo viene creato un nuovo tipo di dati che si chiama colori e che può assumere solo cinque valori diversi a cui vengono assegnati i nomi giallo, arancione, rosso, verde, blu. Dopo aver definito il tipo colori, come per qualsiasi altro tipo, per utilizzarlo dobbiamo definire le variabili, per esempio: colori a,b; a e b diventano variabili di tipo colori. A queste variabili, nel programma, si potrà assegnare solo valori di tipo colori. a = rosso; b = verde; // (2) In realtà il compilatore associa ogni identificatore dell’elenco fornito dal programmatore con un numero intero e nel programma considera questi identificatori come costanti dichiarate dal programmatore. Nell’esempio dell’istruzione (1), le parole giallo, arancione, rosso, verde, blu, diventano costanti esattamente come se fossero state definite con l’istruzione: const int giallo=0, arancione=1, rosso=2, verde=3, blu=4; Quindi le variabili di tipo colori sono in realtà delle variabili di tipo intero a cui, però, si possono assegnare solo i numeri da 0 a 4. Con l’istruzione (2) viene assegnato il numero 2 alla variabile a e il numero 3 alla variabile b. Se si visualizza in output un variabile di tipo colori verrà visualizzato il numero contenuto al suo interno e non il nome del colore associato a quel numero! Le operazioni ammesse con le variabili di tipo dichiarato attraverso l’istruzione enum sono le stesse ammesse con il tipo intero, quindi ++, --, +, -, *, /, %. Inoltre queste variabili sono compatibili con il tipo intero. !34 Corso sul Linguaggio C++ Edizione 2010 Abbiamo visto che il computer assegna automaticamente i numeri da zero in poi agli identificatori definiti nelle enumerazioni ma il programmatore può anche decidere lui quali numeri assegnare, per esempio: enum mesi {gennaio=1, febbraio, luglio=7, agosto}; mesi mese1, mese2; si avrà gennaio = 1, febbraio =2, luglio = 7, agosto =8 inoltre, si possono anche dichiarare le variabili con la stessa istruzione enum: enum mesi {gennaio=1, febbraio, luglio=7, agosto} mese1,mese2; 2.8.Esercizi 1) Calcolare le seguenti espressioni scritte nel linguaggio C++ a) 3*(15-2*5)/(4*10/8) b) 2+5*(40 / (2*4) /2)+(10/3) [Risultato:3; 15;] 2) Scrivere le espressioni logiche per: • Vedere se un numero x è positivo • Vedere se un numero x è pari; • Vedere se un numero x è compreso nell'intervallo [A, B] chiuso; • Vedere se un numero x è esterno all'intervallo [A, B] chiuso; • Vedere se un numero intero x è multiplo di 5 e, contemporaneamente, è dispari; 3) Calcolare ili risultato delle seguenti espressioni logiche (1 per "vero", 0 per "falso") 5>2; 10<3; 5>7 || 3>4; 10<2 || 5>4; 7<1 || 4<5; 3>0 && 5>2; 8>10 && 5>2; 7>8 != 7>10; 3>2 != 2<3; 5<5 == 4>3; 4) Sia dato il seguente pezzo di programma: long int d = 1267894; float media; int k = 10; media = d/k; cout << (float) media; qual è il valore che viene stampato? 5) Indicare il valore assunto dalla variabile f nelle seguenti espressioni: int a=1, b=2, c=3, d=4, e=5, f; a) f = 5 + --a - e / b++; b) f = ++a - c-- + e/--b; c) f = d + c * b-- + e / a++; 6) Indicare il valore assunto dalla variabile c nelle seguenti espressioni: int a=5, b=6, c; a) c = (a == b) ? ++a : ++b; b) c = (a++ == b) ? b++ : b--; c) c = (++a == b) ? ++b : --b; 7) Determinare quale è la relazione che assume valore vero quando x è esterno all'intervallo [A,B] e y è interno allo stesso intervallo? a) (x<A) && (x>B) && (y>=A) && (y<B); b) ((x<A)||(x>B)) && ((y>=A)&&(y<=B)); c) ((x<A)||(x>B)) && ((y>=A)||(y<=B)); !35 Corso sul Linguaggio C++ Edizione 2010 d) e) f) ((x<A)||(x>B)) || ((y>=A)||(y<=B)); ((x<A)&&(x>B)) && ((y>=A)||(y<=B)); ((x<A)||(x>B)) || ((y>=A)&&(y<B)); 8) Sia data una variabile dichiarata come char c;. Ipotizzando che essa contenga un carattere compreso tra '0' e '9', come si trasferisce in una variabile intera v il valore decimale della cifra rappresentata da c? a) v = atoi(c); (atoi- ascii to int- trasforma una stringa contenente cifre in un numero int - come val() del Basic- ma il parametro deve essere un puntatore) b) v = (int) c ; c) v = c - '0'; d) v = c ; e) nessuna delle precedenti 9) Quale dei seguenti valori di a e b produce il valore vero per la condizione: (a > 0) && ((b < 0) || (b > 1)) Risposte: a)a = 5; b = 0 b)a = 5; b = 2 c)a = -1; b = 5 d)a = 1; b = 1 10) Data una misura di tempo espressa in ore minuti e secondi descrivere un algoritmo che la trasformi in secondi. 11) Descrivere un algoritmo per trasformare una misura espressa in secondi in un'altra espressa in ore minuti e secondi. 12) Dato un numero n compreso tra 1 e 365, considerandolo come giorno n-esimo dell'anno, descrivere un algoritmo per determinare a quale giorno della settimana corrisponde sapendo che il primo giorno dell'anno era domenica. (output un numero da 1 a 7, 1=lunedì 7 = domenica.) 13) Assegnata la paga oraria di un operaio calcolare la paga settimanale sapendo che si lavora 7 ore al giorno, il sabato si lavora solo 4 ore pagate però il doppio e la domenica non si lavora. 14) Trovare l’algoritmo per calcolare il numero medio di medici per paziente in un giorno qualsiasi dell’anno in un ospedale di cui si conosce il numero dei medici, il numero totale dei pazienti che circolano in un anno e il numero medio di giorni di degenza di ogni paziente. 15) Convertire lire in Euro (1 euro = 1936,27 lire) 16) Data una misura espressa in metri trovare l’equivalente in piedi e yarde sapendo che: 3 piedi = 1 yarda = 0,91439 metri 17) Sapendo che si può passare dai gradi Celsius ai gradi Fahrenheit con la seguente formula: gradi Fahrenheit = gradi Celsius * 9/5 + 32 Scrivere un programma per trasformare una misura della temperatura da gradi Celsius a Fahrenheit e uno per fare il contrario. Risposte esercizi Esercizio 4) = 126789 Esercizio 5) a) = 3; b) = 4; c) = 15 !36 Corso sul Linguaggio C++ Edizione 2010 Esercizio 6): a) = 7; b) = 6; c) = 7 Esercizio 7) = b) Esercizio 8) = c) c-‘0’ Esercizio 9) = b) !37 Corso sul Linguaggio C++ Edizione 2010 3 Capitolo 3.Le istruzioni e le strutture di controllo Un programma C++ è un insieme di istruzioni. Le istruzioni possono essere di due tipi: • Istruzioni eseguibili, corrispondono ad un azione eseguita dal computer; • Istruzioni di dichiarazione, non corrispondono a nessuna operazione ma servono per definire il modello dei dati o gli oggetti utilizzati dal programma oppure danno indicazioni al compilatore sulle funzioni utilizzate nel programma. Nel programma sorgente è possibile alternare a piacere istruzioni dichiarative ed eseguibili purché quando si utilizza un oggetto quest’ultimo sia stato già definito in precedenza. Le istruzioni possono essere anche classificate nel seguente modo: Di assegnazione Espressioni semplici chiamata di Di dichiarazione e definizione Istruzioni del C++ return break Di salto continue goto blocco strutturate selezione if ... else switch while cicli do…whilee for …. ! 3.1.La programmazione strutturata Abbiamo visto che il linguaggio C++ permette di implementare sia i programmi realizzati con il modello procedurale che quelli realizzati secondo il modello della programmazione object oriented, entrambi, però, prevedono la traduzione di algoritmi nel linguaggio di programma!38 Corso sul Linguaggio C++ Edizione 2010 zione, anche se nella programmazione procedurale, questa è l’operazione base che realizza l’attività del “programmare”. Sappiamo che un algoritmo è la descrizione di un procedimento ovvero di una serie finita di azioni che opera su un’insieme di dati in ingresso per produrre un insieme di risultati. Questa descrizione deve essere non ambigua ovvero un qualsiasi esecutore che esegue l’algoritmo, se parte dagli stessi dati, deve eseguire esattamente le stesse azioni e deve arrivare esattamente agli stessi risultati. Un algoritmo, inoltre, rappresenta sempre un procedimento generale che si può eseguire più volte cambiando i dati in input. Le azioni che verranno svolte, possono essere diverse a seconda dei dati iniziali. Quindi, nell’algoritmo, oltre alla descrizione chiara e univocamente interpretabile delle azioni da eseguire, viene anche indicato l’ordine con cui queste azioni devono essere eseguite, inoltre ci può essere l’indicazione che una o più azioni devono essere ripetute e/o eseguite solo in alcuni casi. Queste indicazioni non sono descrizioni di azioni che l’esecutore deve compiere ma piuttosto servono a indicare esattamente la sequenza con cui le azioni devono essere eseguite e quando devono essere eseguite. Se l’algoritmo viene descritto con un diagramma di flusso (detto anche diagramma a blocchi) le azioni sono descritte in rettangoli mentre la sequenza di esecuzione (flusso) e le condizioni che determinano l’esecuzione o meno delle azioni sono rappresentate dalle frecce e dai rombi, dando luogo a schemi grafici diversi quando si cambiano le indicazioni relative a come si susseguono le azioni. I vari modi con cui mettere insieme le azioni per formare un algoritmo vengono definiti da alcuni autori1 modelli di composizione o schemi di composizione ma sono noti in generale come strutture di controllo della programmazione. I principali modelli di composizione sono: La sequenza, la selezione e la ripetizione: La sequenza consiste nella descrizione di una serie di azioni che devono essere eseguite una dopo l’altra nell’ordine indicato, per esempio: Inizio ! Azione 1 … Azione 2 Azione N Fine La selezione consiste nella descrizione di una o più azioni che devono essere eseguite solo se si verifica una determinata condizione, per esempio: La ripetizione o ciclo consiste nella descrizione di una o più azioni che devono essere rip e t u t e u n n umero di v o l t e variabile in base ad zione fissata. no si una condiPer esempio: condizione condizione Se cons i d eriamo tutti i si d i agrammi condi flusso A ) B ) che si no condizione possono realizzare u t i l i zzando solo g l i schemi A) Ripetizione precondizionale B) Ripetizione postcondizionale di composizione di sequenza, selezione e ripetizione sopra indicati e supponiamo che un azione rappresentata nello schema possa essere o un’azione elementare che l’esecutore capisce o un azione complessa corrispondente ad una serie di azioni elementari rappresentabile sempre con uno dei tre schemi base su indicati, otteniamo un sottoinsieme di tutte le descrizioni di algoritmi possibili. Questo sottoinsieme è quello degli schemi di flusso strutturati. 1 A. Andronico e altri, Manuale di Informatica, Zanichelli 1979 !39 Corso sul Linguaggio C++ Edizione 2010 In base al famoso teorema enunciato dai due matematici italiani Böhm e Jacopini2, un qualsiasi algoritmo può sempre essere descritto utilizzando uno schema di flusso strutturato e quindi usando solo le strutture di controllo della sequenza, selezione e ripetizione. Anzi il teorema afferma che basta anche solo la struttura della sequenza, quella della selezione ad una via e un solo tipo di ciclo (per esempio quello postcondizionale). Per far eseguire un algoritmo da un computer bisogna descriverlo utilizzando un linguaggio che possa essere, in qualche modo, compreso dal computer. Hanno tale caratteristica i linguaggi di programmazione. Ogni azione elementare dell’algoritmo deve corrispondere ad un’istruzione elementare del linguaggio di programmazione utilizzato. Per comunicare al computer gli schemi di composizione delle azioni si utilizzano le istruzioni di controllo. Il teorema di Böhm-Jacopini ci assicura che in un linguaggio, per poter tradurre un qualsiasi algoritmo, bastano le istruzioni di controllo per la sequenza, selezione e ripetizione purché all’interno di queste istruzioni si possa inserire una qualsiasi altra istruzione (elementare o di controllo). I linguaggi di programmazione che possiedono le istruzioni di controllo con le caratteristiche su menzionate sono detti linguaggi strutturati. Si è visto che l’utilizzo di un linguaggio strutturato rende più semplice individuare gli errori nei programmi perché permette di evitare l’utilizzo dell’istruzione di salto (l’istruzione goto). Quest’ultima istruzione era molto utilizzata nei primi linguaggi di programmazione e permette la traduzione di un qualsiasi schema, ma rende molto più complesso, per il programmatore, tenere sotto controllo il flusso del programma ovvero la sequenza delle azioni che verranno eseguite in tutti i casi possibili. Il primo linguaggio strutturato fu il Pascal, oggi tutti i linguaggi moderni di programmazione sono linguaggi strutturati. I programmi che utilizzano solo le istruzioni di controllo per i tre schemi base visti sopra sono detti programmi strutturati3. Il linguaggio C (e di conseguenza anche il linguaggio C++) è un linguaggio strutturato. Nei prossimi paragrafi vedremo le istruzioni utilizzate per tradurre le tre strutture di controllo fondamentali della programmazione in questo linguaggio. 3.2.Le istruzioni semplici La principale istruzione semplice è quella di assegnazione che abbiamo già visto. Abbiamo già visto anche le istruzioni per la dichiarazione di variabili, le istruzioni per dichiarare e definire oggetti di altro tipo le vedremo man mano che studieremo gli oggetti in questione. Ogni istruzione semplice finisce sempre con il carattere ; (punto e virgola), esiste anche l’istruzione nulla costituita dal solo carattere ; ; Se viene inserita in un programma serve a dire al computer che il quel punto non deve fare niente. In questo paragrafo vedremo le istruzioni di salto: break continue return goto Normalmente le istruzioni di un programma vengono eseguite una di seguito all’altra, nell’ordine in cui sono scritte, inoltre è possibile utilizzare le istruzioni di controllo come quelle di selezione per far eseguire alcune istruzioni si e altre no, oppure quelle di ripetizione per far ripetere più volte una serie di istruzioni. 2 vedi http://it.wikipedia.org/wiki/Teorema_di_Jacopini-Bohm. 3 Vedi http://it.wikipedia.org/wiki/Programmazione_strutturata. !40 Corso sul Linguaggio C++ Edizione 2010 In ogni istante è in esecuzione una sola istruzione del programma ed esiste nella CPU un puntatore che indica la posizione della prossima istruzione che sarà eseguita, questo puntatore si chiama program counter. Con le istruzioni di salto è possibile modificare la sequenza con cui dovrebbero essere svolte le istruzioni del programma e, quindi, tutte le istruzioni di salto producono la modifica del program counter. Le istruzioni break e continue servono per modificare la normale sequenza con cui vengono eseguite le istruzioni in un blocco interno ad una selezione o ad un ciclo e le vedremo nei prossimo paragrafi. L’istruzione return determina la fine dell’esecuzione di un sottoprogramma e fa tornare il punto di esecuzione al programma chiamante. Come abbiamo già visto return serve anche per terminare il programma principale main. L’istruzione goto (go to = vai a) è detta istruzione di salto incondizionato e produce come effetto il trasferimento del punto di esecuzione ad una posizione definita dal programmatore. La sintassi è: goto <etichetta> Questo comando ordina al computer di portare il punto di esecuzione nella posizione in cui il programmatore ha posizionato l’etichetta. Una etichetta è un qualsiasi identificatore valido seguito dal carattere : (duepunti). Per esempio: inizio: contatore++; ………. goto inizio; L’uso di questa istruzione, come detto nel paragrafo precedente, può e dovrebbe essere evitato perché rende difficile il controllo del programma durante l’esecuzione. Al suo posto si possono utilizzare le istruzioni strutturate elencate nei prossimi paragrafi. 3.3.La sequenza Per dire al computer di eseguire una serie di istruzioni in un determinato ordine nel linguaggio C si utilizza il blocco: { <istruzione1>;<istruzione2>; ………[<istruzioneN]; } In pratica si scrivono le istruzioni nell’ordine in cui devono essere eseguite (se si trattta di istruzioni semplici devono finire con il carattere ; -punto e virgola-) e si racchiude l’elenco tra parentesi graffe, un tale insieme di istruzioni si dice blocco. Le istruzioni interne ad un blocco possono essere istruzioni elementari o istruzioni di controllo. Se si utilizza il linguaggio C++ si possono inserire in un blocco anche istruzioni di dichiarazione. Per esempio tutte le istruzioni della funzione main nei programmi che abbiamo visto sono un blocco. Un blocco può contenere al suo interno una qualsiasi istruzione di controllo e quindi anche altri blocchi. Per esempio: int main() { int i=1; { int j=20; k=30; …… } i++; …….. return 0; } } blocco interno il blocco contenente tutte le istruzioni della funzione main contiene al suo interno un altro blocco che inizia con le dichiarazioni int j=20; k=30. Il raggruppamento delle istruzioni in blocchi è importante per l’area di validità delle variabili, infatti ogni variabile dichiarata in un blocco può essere utilizzata solo nelle istruzioni del !41 Corso sul Linguaggio C++ Edizione 2010 blocco stesso e in quelle di tutti i blocchi contenuti in esso che seguono la dichiarazione, ma non può essere utilizzata al di fuori del blocco. Quindi nell’esempio precedente la variabile i può essere utilizzata sia nel blocco che inizia dopo di int main() che in quello interno, mentre le variabili j e k possono essere utilizzate solo nelle istruzioni racchiuse tra le due parentesi graffe del blocco interno. Area di validità delle variabili Le variabili dichiarate in un blocco si dicono variabili locali appunto perché possono essere utilizzate solo all’interno del blocco. È possibile però definire anche variabili globali che valgono in tutti i blocchi, per farlo bisogna dichiarare le variabili fuori da qualsiasi blocco, subito dopo le direttive al preprocessore, per esempio: #include <iostream.h> // variabili globali int totale=0; int main() { float prezzo; int q; } la variabile totale è una variabile globale mentre le variabili prezzo e q sono variabili locali della funzione main. Ciclo di vita delle variabili Tutte le variabili vengono create quando viene eseguita la dichiarazione ma le variabili globali vengono cancellate solo alla fine del programma mentre, normalmente, le variabili locali vengono cancellate quando termina il blocco in cui sono state dichiarate. È possibile evitare la cancellazione delle variabili locali alla fine del blocco utilizzando il modificatore della modalità di memorizzazione static che abbiamo visto nel paragrafo 2.2. Per esempio consideriamo il seguente programma: int main() { int i; for (i=1; i<10; i++) /*ripete 9 volte il blocco sottostante */ { static int contatore=1; cout << contatore << ";" ; contatore++; } cout << endl; system("PAUSE"); return 0; } Sapendo che l’istruzione for è una delle istruzioni di controllo per i cicli che vedremo fra poco e che in questo caso ha semplicemente l’effetto di far ripetere 9 volte le istruzioni del blocco interno, il risultato in output sarà: 1;2;3;4;5;6;7;8;9; Premere un tasto per continuare ... Mentre se si toglie la parola static nella dichiarazione della variabile contatore il risultato sarà: 1;1;1;1;1;1;1;1;1; Premere un tasto per continuare ... perché alla fine del blocco la variabile verrà cancellata e quindi, quando il blocco viene ripetuto, la variabile contatore viene ricreata con il valore 1. Anche le variabili globali possono essere definite static, in questo caso non viene modificato il ciclo di vita, perché tutte le variabili globali vengono cancellate alla fine del programma, ma viene modificata l’area di validità ed ha senso quando il programma è costituito da più moduli ovvero più file sorgenti, in quanto la variabile sarà visibile solo nel modulo dove è !42 Corso sul Linguaggio C++ Edizione 2010 stata definita mentre normalmente le variabili globali sono visibili in tutti i moduli dal momento in cui sono state definite in poi. 3.4.La Selezione Il linguaggio C++ prevede due istruzioni per la selezione: l’istruzione if per la selezione semplice a una o due vie e l’’istruzione switch per la selezione a più vie. La sintassi dell’istruzione if è la seguente: if ( espressione logica ) Istruzione1 else ! Istruzione2 durante l’esecuzione il computer valuta l’espressione logica, se il risultato è vero (diverso da zero) verrà eseguita l’istruzione1 altrimenti, se è presente la clausola else, verrà eseguita l’istruzione2. Se non c’è la clausola else la selezione è ad una sola via, altrimenti è a due vie. L’istruzione o le istruzioni presenti dentro l’if possono essere sia istruzioni semplici che strutturate, quindi possono essere anche un blocco ({ ……}) oppure un’altra istruzione if, in questo caso si dice che la seconda istruzione if è annidata nella prima. Il blocco deve obbligatoriamente essere utilizzato se le istruzioni da eseguire nel caso vero o nel caso falso sono più di una. Per esempio: If (a > b) max = a ; oppure if (a > b) {tmp=a ; a=b ; b=tmp ;} sono entrambe selezioni ad un via, l’istruzione max=a nel primo caso e il blocco costituito da tre istruzioni nel secondo caso, verranno eseguiti solo se a>b. Le seguenti istruzioni if sono esempi di selezione a due vie. If (b == 0) cout << "Indetermintata !"; else cout << "Impossibile!"; If (a >10) {e++ ; a =\ 10;} else {e-- ; a=*10 ;} Problema: equazione di primo grado Scrivere un programma per far risolvere al computer una qualsiasi equazione di primo grado in una variabile. Analisi del problema Si definisce equazione di primo grado in una variabile una qualsiasi equazione matematica riconducibile alla forma: ax+b=0 dove a e b sono due numeri qualsiasi. Risolvere un'equazione significa trovare tutti i numeri che sostituiti alla x verificano l'uguaglianza. Si può dimostrare che Se a<> 0 allora esiste una sola soluzione : x = -b/a Se a= 0 e b<>0 allora l'equazione non ammette soluzioni Se a=0 e b=0 allora le soluzioni sono infinite Noi ci limiteremo alle equazioni nel campo dei numeri reali. Per risolvere un equazione di primo grado è necessario conoscere il valore di a e di b. !43 Corso sul Linguaggio C++ Edizione 2010 Per questo la prima cosa che il computer deve fare e procurarsi i valori dei coefficienti a e b. Dopo, se a è diverso da zero, deve calcolare e visualizzare la soluzione, altrimenti visualizza la frase "Equazione impossibile!" se b è diverso da zero oppure "infinite soluzioni" se b è uguale a zero. Il semplice programma che fa fare queste cose al computer è il seguente: #include <iostream.h> #include <stdlib.h> // Programma Eqaz1 : Soluzione delle equazioni di primo grado int main() { float a,b,x; cout << "Inserisci i due coefficienti: "; cin >> a; cin >> b; if (a==0) if (b==0) cout << "Equanzione indetrminata!\n"; else cout << "Equazione impossibile!\n"; else {x= -b/a; cout << "\nLa soluzione è: " << x << endl;} system("PAUSE"); return 0; } Si noti che quando due istruzioni if sono annidate, come nell’esempio precedente, il primo else che si trova si riferisce sempre all’ultimo if, perché prima di proseguire l’istruzione if esterna bisogna completare quella interna. Quello che segue è il diagramma a blocchi corrispondente al programma. Inizio Scrivi “Inserisci i coefficienti:” Leggi a,b a == 0 Si x = -b/a b == 0 Scrivi x Scrivi “Equazione impossibile!” Si Scrivi “Equazione indeterminata!" Fine ! 3.5.Selezione multipla La selezione si utilizza quando in un algoritmo c’è un gruppo di azioni che deve essere eseguito solo in un determinato caso. Se i casi possibili, e i relativi gruppi di azioni, sono due utilizza la selezione a due vie. Se i casi sono più di due si possono utilizzare le istruzioni if annidate: If (caso == 1) { \\ primo gruppo di istruzioni ………… } !44 Corso sul Linguaggio C++ Edizione 2010 else if (caso == 2) { \\ secondo gruppo di istruzioni ………… } else if (caso == 3) { \\ terzo gruppo di istruzioni ………… } ma per questo tipo di algoritmi la maggior parte dei linguaggi di programmazione mette a disposizione anche un’istruzione apposita, quella per la selezione multipla, che per il linguaggio C++ è l’istruzione switch. La sintassi è la seguente: istruzione switch ( switch espressione numerica ) { blocco switch } blocco switch case costante : istruzione default ! : istruzione L’espressione numerica in base alla quale viene effettuata la scelta deve essere di tipo intero (quindi può essere anche di tipo char). Il computer valuterà l’espressione e poi farà proseguire il programma dal punto in cui c’è scritto case x: con x = risultato dell’espressione. Se non c’è nessuna costante nel blocco switch che corrisponde al risultato dell’espressione verranno eseguite le istruzioni poste dopo la parola chiave default, se non c’è nemmeno la clausola default il controllo passa all’istruzione successiva al blocco switch. La traduzione di questo costrutto in linguaggio macchina è più efficiente rispetto alla traduzione degli if annidati. Se fra i vari gruppi di istruzioni possibili ne deve essere eseguito solo uno, bisogna utilizzare anche l’istruzione break che fa saltare il punto di esecuzione all’istruzione successiva al blocco switch saltando quindi tutte le istruzioni rimanenti inserite nel blocco. Per esempio se vogliamo tradurre con l’istruzione switch le istruzioni if annidate viste all’inizio di questo paragrafo dobbiamo scrivere: switch (caso) { case 1: // primo gruppo di istruzioni ………… break case 2: // secondo gruppo di istruzioni ………… break case 3: // terzo gruppo di istruzioni ………… } Problema: una semplice calcolatrice Vogliamo fare un programma per realizzare una semplice macchina calcolatrice che fa le 4 operazioni fondamentali. Il computer deve leggere due numeri, chiedere quale operazione effettuare (+, -, *, /) e visualizzare il risultato. Il programma potrebbe esser il seguente: !45 Corso sul Linguaggio C++ Edizione 2010 #include <iostream.h> #include <stdlib.h> /* programma SCalc: Una semplice calcolatrice */ int main() {double a,b; char op; cout << "Inserisci il primo operando: "; cin >> a; cout << "Inserisci il secondo operando: "; cin >> b; cout << "Quale operazione (+ - * /)? "; cin >> op; switch (op) { case '+': cout << "Risultato = " << a+b << endl; break; case '-': cout << "Risultato = " << a-b << endl; break; case '*': cout << "Risultato = " << a*b << endl; break; case '/': cout << "Risultato = " << a/b << endl; break; default : cout << "Errore! bisogna inserire uno dei segni + - * /\n"; } system("PAUSE"); return 0; } Mandandolo in esecuzione e provando, per esempio, la moltiplicazione tra i due numeri 5 e 3 si ottiene: Inserisci il primo operando: 5 Inserisci il secondo operando: 3 Quale operazione (+ - * /)? * Risultato = 15 Premi un tasto per continuare ... 3.6.I cicli Il principale vantaggio che ha un computer rispetto ad un essere umano nell’eseguire un compito è la velocità di esecuzione. Un personal computer, per esempio, è in grado di eseguire qualche centinaio di milioni di operazioni aritmetiche al secondo, cosa che nessun essere umano è in grado di fare. Però per fargli eseguire una operazione bisogna dargli il comando opportuno e per fargli eseguire più operazioni bisogna scrivere un programma dove sono elencati i comandi per ciascuna operazione. Per fortuna anche se il computer esegue milioni di operazioni al secondo non è necessario scrivere programmi con milioni di istruzioni per ogni secondo di elaborazione, infatti la maggior parte dei programmi contiene relativamente poche istruzioni che però vengono fatte eseguire più volte, in alcuni casi anche milioni o miliardi di volte. Per far risolvere un problema complesso al computer bisogna spesso progettare un procedimento che preveda la ripetizione meccanica di una serie di azioni semplici e questo è un modo di affrontare i problemi a cui noi umani non siamo abituati. Per questo motivo le istruzioni per i cicli, cioè quelle che servono per far ripetere una serie di azioni al computer, sono molto importanti in ogni linguaggio di programmazione. Il linguaggio C ha tre istruzioni per i cicli: l’istruzione do (ripetizione postcondizionale), l’istruzione while (ripetizione precondizionale) e l’istruzione for (ripetizione con contatore). Istruzioni do e while Entrambe le istruzioni do e while servono per far ripetere una serie di azioni fin quando la condizione prefissata è vera, appena la condizione diventa falsa il ciclo termina. La differenza sta solo nel momento in cui la condizione viene valutata, nel caso del do la condizione viene controlla solo dopo l’esecuzione dell’istruzione del ciclo mentre nel caso del while prima. Per questo motivo il ciclo do viene detto postcondizionale mentre il ciclo while viene detto precondizionale. La condizione che viene valutata ogni volta nel ciclo può essere una qualsiasi espressione logica e quindi anche un espressione con risultato intero (0 = falso, un numero diverso da zero = vero). Le azioni eseguite dal computer nei due casi sono quelle esplicitate nel seguente diagramma a blocchi: !46 Corso sul Linguaggio C++ Edizione 2010 istruzione si condizione si istruzione no condizione no A) ciclo while (precondizionale) ! B) Ciclo do (postcondizionale) La sintassi delle due istruzioni è la seguente: Istruzione do istruzione do while ( condizione ) ; Istruzione while while ! ( condizione istruzione ) Per esempio con il seguente programma int main() { int i=5; do cout << i-- << "\t"; while (i>0); system("PAUSE"); return 0; } l’istruzione cout << i-- < “\t” viene eseguita mentre la condizione i>0 è vera e quindi viene ripetuta fin quando il valore di i arriva a zero. Il risultato in output sarà: 5 4 3 2 1 Premere un tasto per continuare In questo caso se avessimo usato l’istruzione while invece di do il risultato non sarebbe cambiato. Cambia qualcosa tra do e while nei casi in cui la condizione è vera già la prima volta che viene eseguita l’istruzione perché in questo caso con while il ciclo non viene eseguito mentre con do viene eseguito sempre almeno una volta. Istruzione for Il più comune tipo di ciclo è quello in cui si fa ripetere dal computer una serie di azioni un numero prefissato di volte. Questo tipo di ciclo si può realizzare utilizzando un contatore che parte da zero e le istruzioni do e while con la condizione per far continuare il ciclo impostata nel seguente modo: contatore < numero volte che il ciclo deve essere ripetuto Questo tipo di ciclo viene detto ciclo con contatore o ciclo enumerativo, considerato che è un tipo di ciclo molto utilizzato, la maggior parte dei linguaggi di programmazione ha un’istruzione apposita per esso: l’istruzione for. La sintassi dell’istruzione for per il linguaggio C++ è la seguente: Istruzione for for ( inizializzazione C o n d izione ; ! che corrisponde al seguente diagramma a blocchi: !47 ; i n c r emento ) istruzione Corso sul Linguaggio C++ Edizione 2010 inizializzazione condizione falsa vera incremento istruzione ! prosegue il programma questo significa che, prima di tutto viene eseguita l’istruzione di inizializzazione, poi inizia il ciclo e viene valutata la condizione (quindi si tratta di un ciclo precondizionale), poi, se la condizione è vera, viene eseguita l’istruzione e l’incremento che è l’ultima operazione del ciclo. Per esempio se vogliamo far scrivere dieci volte su video la parola “ciao” basta inserire la seguente istruzione for: for (int i=0; i<10; i++) cout << "ciao\n" ; il computer prima crea la variabile i con zero dentro, poi inizia il ciclo in cui controlla se i<10 e, fin quando è vero, scrive ciao su video e incrementa i. Da notare che quando l’istruzione di inizializzazione comprende anche la dichiarazione della variabile come nell’esempio precedente, la variabile sarà creata appositamente per il ciclo e poi cancellata alla fine del ciclo, quindi si tratterà di una variabile locale con area di validità e ciclo di vita ristretta alle sole istruzioni del ciclo. Se le istruzioni del ciclo sono più di una bisogna inserire un blocco al posto dell’istruzione del ciclo. Per esempio supponiamo di voler scrivere il programma per far calcolare la media di n numeri letti da tastiera, il programma può chiedere “Quanti numeri?” e poi eseguire un ciclo per leggere i numeri e sommarli. Nel ciclo devono essere inserite più istruzioni, quelle per leggere un numero e quella per sommare il numero letto all’accumulatore: #include <iostream.h> #include <stdlib.h> // Programma media. Calcola la media di una serie di numeri in input int main() { float somma=0, num, media; int i,n; cout << "Quanti numero? "; cin >> n; for (i=0; i<n; ) {cout << "inserisci il numero " << ++i << ": "; cin >> num; somma += num;} media = somma / n; cout << "La media e' = " << media << endl; system("PAUSE"); return 0;} In questo caso l’istruzione di incremento è stata omessa perché conveniva includerla tra le istruzioni del ciclo: cout << "inserisci il numero " << ++i << ": « ; si noti l’uso della notazione prefissa (++i) che produce in output il risultato dell’incremento. Mandando in esecuzione il programma con tre numeri in input (3, 5, 6) si ottiene in output : Quanti numeri ? 3 Inserisci il numero 1: 3 Inserisci il numero 2: 5 Inserisci il numero 3: 6 La media e’ = 4.66667 Premere un tasto per continuare ... !48 Corso sul Linguaggio C++ Edizione 2010 Con tutti i tipi di cicli si possono usare anche le istruzioni di salto break e continue. L’istruzione break interrompe l’esecuzione del ciclo e fa proseguire il programma con l’istruzione successiva al ciclo. L’istruzione continue interrompe l’iterazione corrente senza completare le istruzioni presenti nel corpo del ciclo e, a differenza di break, fa proseguire il ciclo con l’iterazione successiva, nel caso del ciclo for il punto di esecuzione passa all’istruzione di incremento che è l’ultima istruzione del ciclo, nel caso dei cicli do e while passa al controllo della condizione. Problema numeri primi Visualizzare i numeri primi compresi tra due limiti a e b dati in input. I numeri primi sono quelli divisibili solo per 1 e per se stesso. Si può dimostrare che per vedere se un numero è primo basta controllare che non è divisibile per nessun numero compreso tra 2 e la radice del numero. Per fare questo possiamo utilizzare un ciclo ma se ad un certo punto troviamo un divisore è inutile continuare il ciclo fino alla fine perché già sappiamo che il numero non è primo. Il programma è il seguente: #include <iostream.h> #include <stdlib.h> #include <math.h> int main(){ /* programma numprimi. Visualizza tutti i numeri primi compresi tra due numeri a e b dati in input*/ int a,b,p,i; cout << "Inserisci il limite inferiore: "; cin >> a; cout << "Inserisci il limite superiore: "; cin >> b; for (p=a; p<=b; p++) {for (i=2;i<= sqrt(p);i++) //se c'è un divisore <= sqr(p) ferma il ciclo if (!(p % i)) break; // se il ciclo non è stato fermato il numero p è primo if (i > sqrt(p)) cout << p << " - "; } system("PAUSE"); return 0; } Per utilizzare la funzione sqrt (square root= radice quadrata) bisogna includere il file di header math.h. 3.7.Documentazione Il linguaggio C++ include poche operazioni e istruzioni base, molte altre sono disponibili in librerie esterne in genere fornite insieme al compilatore del linguaggio, per utilizzarle bisogna collegare le librerie che le contengono. Per esempio l’operazione radice quadrata ( x =sqrt(x)) o la potenza (xy = pow(x,y)) sono incluse in una libreria a parte e devono essere dichiarate prima di essere utilizzate. Per inserire la dichiarazione di tutte le funzioni matematiche basta includere all’inizio del programma il file di intestazioni math.h. Per farlo si può utilizzare la direttiva: #include <math.h> La quantità di funzioni esterne incluse nelle librerie fornite insieme al linguaggio è molto elevata, un elenco di quelle standard del linguaggio C, fornite insieme al compilatore g++ incluso nel pacchetto dev-C++, si può trovare sul sito ufficiale dell’organizzazione gnu: http://www.gnu.org/software/libc/manual !49 Corso sul Linguaggio C++ Edizione 2010 Per avere informazioni sulle peculiarità del linguaggio C++ implementato nel compilatore g+ + della GCC (Gnu Compiler Collection) incluso nel DEV C++ si deve visitare il sito: http://www.gnu.org/software/gcc/onlinedocs/ purtroppo solo in lingua inglese. Anche su Wikipedia trovate molte informazioni sul linguaggio C++ e sui compilatori disponibili. Trovate informazioni e spiegazioni anche per le librerie standard del C++ all’indirizzo: http://en.wikipedia.org/wiki/C%2B%2B_Standard_Library Per approfondire la conoscenza del linguaggio C++ un ottimo manuale è quello di Bruce Eckel, Thinking in C++, in due volumi, disponibile gratuitamente sul sito: http://www.mindview.net/, 3.8.Esercizi 18) Individuare gli errori di sintassi nelle seguenti istruzioni: a) cin >> a+2: b) cout >> “Prova”; c) FOR(a=1, a>10, a++); d) a=b=c 19) Siano dati i seguenti cicli: a) for (i = 0; i<n; i=i+2) ; b) for (i = 0; i<n; i++) {i++; i++;} c) i=n/2; while (i < n) i *=2; d) i=n; do i-=n/2; while ( i >=0); Completare la seguente tabella indicando quante volte verrà eseguito il ciclo ed il valore della variabile i alla fine dello stesso supponendo che la variabile n = 10 Ciclo N° di volte Valore di i a) b) c) d) 20) Completare la tabella indicando cosa conterrà la variabile C dopo l'esecuzione delle seguenti istruzioni if annidate tenendo conto dei valori iniziali di A e B indicati nelle prime due colonne: if (A + B >10) if (A > 10 && B < 10) C=A - B; ELSE C=A - B - 100; else if (A < 0 || B < 0) C= A + B; ELSE C= A - B + 100; A B 10 20 -10 -20 C !50 Corso sul Linguaggio C++ Edizione 2010 5 4 60 -10 21) Cosa fa il seguente frammento di programma? int i=0; while(i<50) {if (i%2) cout << i << “\t”; i++;} 22) Considerate il seguente frammento di programma: t=-1; for (i=1; i<=n; i++) if (f(i)) t=i; if (t>=0) cout << t ; Quale delle seguenti affermazioni è corretta? a) Il programma cerca e stampa il più piccolo intero x fra 1 e n tale che f(x)!=0; se tale intero non esiste, il programma entra in un ciclo infinito. b) Il programma cerca e stampa il più grande intero x fra 1 e n tale che f(x)!=0; se tale intero non esiste, il programma entra in un ciclo infinito. c) Il programma cerca e stampa il più piccolo intero x fra 1 e n tale che f(x)!=0; se tale intero non esiste, il programma non stampa nulla. d) Il programma cerca e stampa il più grande x fra 1 e n tale che f(x)!=0; se tale intero non esiste, il programma non stampa nulla. 23) Cosa scrivono in output i seguenti cicli? a) for (int i=1; i<7;i++) cout << i %5<<’ ‘; …………………………………………. b) for (int i=1; i<7;i++) cout << i*2-1 <<’ ‘; …………………………………………. c) int i=10; do {cout << i<<’ ‘; i -=5;} while (i>0); ……………………………………… d) int j=5,i=5; while(i>0) {cout << j <<’ ‘; if (i%2) j +=i else j-=i; i--;} ….…………….. 24) Considerate il seguente frammento di codice: float i,f; ... f=1/50.0; for (i=0.0; i!=1.0; i+=f) printf("A"); Quale dei seguenti effetti ha il ciclo for indicato? a)Stampa 50 volte il carattere A; b)Stampa 51 volte il carattere A; c)Stampa 49 volte il carattere A; d) Il ciclo potrebbe non terminare, stampando infinite volte il carattere A; e)Il compilatore segnala un errore; Solo selezione 25) Data la misura dei tre lati di un triangolo dire se è isoscele scaleno o equilatero. 26) Un lanificio acquista lana dai pastori della zona. La lana acquistata è di tre tipi diversi (che si acquistano a prezzi diversi), il prezzo inoltre cambia in base all'umidità, se questa supera il 30% il prezzo di acquisto si riduce del 15%. Fare un programma che aiuti l'impiegato a calcolare la somma da versare ai pastori che portano la lana. 27) Una cooperativa agricola vende ai propri soci il vino che produce al prezzo di € 1 al litro se bianco, 1.5 se rosso e 2 se D.O.C. E' possibile prendere il vino sfuso o imbottigliato, in questo secondo caso il prezzo aumenta ulteriormente di €. 0.20 a litro. Descrivere un programma per calcolare il prezzo del vino ogni volta che viene un cliente. !51 Corso sul Linguaggio C++ Edizione 2010 28) Un commerciante, in un mese, ha acquistato merce per X lire e ne ha venduto per Y lire. Sapendo che l'aliquota IVA sulla merce trattata è il 20% calcolare l'IVA da versare o l'IVA a credito a fine mese. 29) Fare un programma per calcolare l'IRPEF su un reddito assegnato. L’IRPEF, in base alla finanziaria 2007, si calcola così: a. b. Se il reddito è inferiore o uguale a 15.000€ si calcola il 23% del reddito se supera i 15.000 ma é inferiore a 28.001 si calcola il 23% sui primi 15.000 (=€.3450) + il 27% sulla parte eccedente i 15.000€. c. Se supera i 28.000 ma è inferiore ai 55001€ l’imposta dovuta è data da 6960€ (pari al massimo dello scaglione precedente) + il 38% sulla parte di reddito eccedente 28.000€ d. d. d a 5 5 . 0 0 0 € f i n o a 7 5 . 0 0 0 € l ’ i m p o s t a è p a r i a € 1 7 . 2 2 0 (15000*0,23+13000*0,27+27000*0,38) + il 41% della parte di reddito eccedente 55.000€ e. e. oltre € 75.000 l’imposta è pari a 25420€ + il 43% della parte di reddito che supera 75000 euro. graficamente gli scaglioni possono essere rappresentati così: 27% 23% 15000 ! 38% 43% 41% 28000 55000 75000 43% 30) Far calcolare il prezzo di una quantità assegna di vino che costa € 2.1 al litro supponendo che se si acquista una quantità >= 20 litri si ha diritto al 10% di sconto. 31) Data in input una cifra qualsiasi, dopo averla arrotondata alle decine (per eccesso o per difetto a seconda che il resto superi 5 o no), indicare quanti biglietti da 10€, quanti da 50 € e quanti da 100€ sono necessari per pagare la somma inserita in input utilizzando il minor numero possibile di biglietti. Cicli 32) Dato in input un numero intero N calcolare N fattoriale. 33) Dato in input un numero qualsiasi x e un numero intero n positivo, calcolare il risultato di xn. 34) Assegnati n e d calcolare la somma dei primi n termini della progressione geometrica di ragione d: 1 + d + d2 + d3 + …. + dn 35) Assegnati n e d calcolare la somma dei primi n termini della progressione aritmetica di ragione d: 1+ 2d+ 3d+ 4d+ …+ nd 36) Calcolare il prodotto di due numeri A e B interi effettuando solo addizioni. 37) Calcolare il quoziente e il resto della divisione tra due numeri interi A e B effettuando solo sottrazioni. 38) Dato in input un numero intero qualsiasi controllare se è primo oppure no e visualizzare la risposta. 39) Dati due numeri interi A e B trovare il minimo comune multiplo. !52 Corso sul Linguaggio C++ Edizione 2010 40) Dati due numeri interi A e B trovare il massimo comun divisore con l’algoritmo di Euclide. Euclide ha dimostrato che, detti A e B due numeri interi con A>=B, se A è divisibile per B allora MCD(A,B) = B altrimenti MCD(A,B) = MCD(B, A%B). In pratica si può cercare il MCD tra due numeri più piccoli. Ripetendo questa operazione fino a quando i due numeri diventano divisibili si trova il Massimo Comun Divisore. 41) Dato in input un numero intero N visualizzare la somma dei suoi divisori. 42) Calcolare la somma dei numeri interi da 1 a n (n in input): 1+ 2 + 3 + 4 + ….. + n (verificare che corrisponde a n*(n+1)/2) 43) Far calcolare la somma dei primi n termini (N in input) della seguente successione: 1+ ½+ 1/3+ ¼ + 1/5 + 1/6 + 1/7+ ……+ 1/n 44) Far calcolare la somma dei primi n numeri dispari e verificare che corrisponde sempre a n*n 45) Scrivere le istruzioni per far visualizzare i primi n termini (n in input) delle seguenti successioni di numeri: a) 2 4 6 8 10 12 14 ... b) 1 -3 5 -7 9 -11 13 ... c) 1 2 9 4 25 6 49 8… d) 2 3 4 5 8 9 16 17 32 33… 46) Far visualizzare su video un menù con una serie di voci tipo quelle dell’esempio sottostante, chiedere la scelta e controllare che venga inserito un valore valido, visualizzare l’ozione corrispondente alla scelta, far terminare il programma quando viene scelta la funzione corrispondente a Fine. Possibili scelte 1) Funzione 1 2) Funzione 2 3) Funzione 3 4) Funzione 4 5) Fine Quale scegli? 47) Far calcolare la somma dei quadrati dei primi n numeri interi e verificare che corrisponde a n*(n+1)*(2*n+1)/6 48) Far calcolare la somma della seguente successione di numeri interi: 1*N + 2*(N-1)+ 3*(N-2)+ …… +(N-1)*2 + N*1 si tratta di sommare una serie di prodotti di due numeri dove il primo fattore varia da 1 a N mentre il secondo diminuisce da N a 1. (Questa somma, qualunque sia N è sempre = N*(N +1)(N+2)/6 ) 49) Far Calcolare la somma della seguente successione di numeri interi: 1*2 + 2*3 + 3*4 + 4*5 + …….. + (n-1)*n il primo fattore va da 1 a n-1 mentre il secondo da 2 a N. (Questa somma è sempre uguale a n*(n+1)(2*n-2)/6) !53 Corso sul Linguaggio C++ Edizione 2010 50) Calcolare la somma della seguente successione e verificare che corrisponde a n*(n +1)*(2*n+1)/3 n*(n+1) + (n-1)*(n+2)+ (n-2)*(n+3)+ ……….+ 2*(n+n-1) + 1*(n+n) 51) Far visualizzare la tabella dei codici ASCII. 52) Calcolare la somma della successione: Nel ciclo fermarsi quando l’ultimo termine sommato è inferiore ad un numero E dato in input. in( x) = y − y2 2 + y3 3 − y4 4 + y5 5 + .... supponendo di avere in input x, con 0 <x < 2 e che y = x-1. Fermare il ciclo quando il valore assoluto dell’ultimo termine sommato è minore dell’errore E dato anch’esso in input. Il risultato è il logaritmo naturale di x approssimato con un errore massimo = E. 53) Dato in input un numero intero n trasformarlo in binario. 54) Far visualizzare i primi n termini della successione di Fibonacci: 1; 1; 2; 3; 5; 8; 13; 21; ….. (ogni numero è la somma dei due precedenti e i primi due sono entrambi = 1). 55) Dato in input un numero intero n e trasformarlo nel sistema di numerazione a base b (b in input) 56) Dato un numero x scritto in un sistema a base b (x e b in input) trasformarlo in decimale. 57) Scrivere il programma che, dato in input x, calcoli la somma della seguente successione: sen( x) = x − x3 3! + x5 5! − x7 7! + x9 9! + .... come si vede alcuni i termini vengono sommati o sottratti alternativamente. Il risultato, se si continua all’infinito, è la funzione trigonometrica sen(x). Considerato che non si può continuare all’infinito, fermarsi quando il valore assoluto dell’ultimo termine sommato è minore dell’errore E dato in input (si può dimostrare che la somma di tutti i restanti termini è sempre minore del valore assoluto dell’ultimo termine sommato). 58) Come nell’esercizio precedente scrivere il programma che, dato in input un numero reale cos(x) = 1 − x2 2! + x4 4! − x6 6! + x8 8! + .... x, calcoli la somma della seguente successione: Sommando gli infiniti termini di questa successione si calcola la funzione coseno(x). Nel ciclo fermarsi quando il valore assoluto dell’ultimo termine è minore dell’errore E dato in input. 59) Come per gli esercizi precedenti far calcolare la somma della seguente successione (x in input): !54 Corso sul Linguaggio C++ Edizione 2010 ex = 1 + x + x2 2! + x3 3! + x4 4! + x5 5! + .... in questo caso viene calcolato ex, fermarsi quando si somma il termine n, tn = xn/n! con n>x e abs(tn)*(n/(n-x))< E, dove E è l’errore massimo dato in input. 60) Scrivere un programma che dato in input un numero qualsiasi lo visualizzi invertendo l’ordine delle cifre. Doppio ciclo 61) Far visualizzare la tavola pitagorica fino ad un numero N assegnato. 62) Far visualizzare i primi n numeri primi (può essere utilizzato il programma per controllare se un numero è primo oppure no). 63) Far calcolare X elevato a N utilizzando solo addizioni (si possono utilizzare i programmi per fare la potenza e la moltiplicazione). 64) Far visualizzare tutti gli elementi della serie di Fibonacci (vedi esercizio 54) che siano anche numeri primi (composto dai due programmi Fibonacci + controllo numeri primi). 65) Scomporre un numero qualsiasi in fattori primi. 66) Assegnata una frazione A/B qualsiasi ridurla ai minimi termini 67) Cercare e far visualizzare tutti i numeri perfetti minori o uguali ad un numero N dato in input. I numeri perfetti sono quelli per i quali la somma dei divisori (escluso il numero considerato) è uguale al numero stesso, per esempio 6 (6=1+2+3) oppure 28 (28=1+2+4+7+14). 68) Scrivere un programma che faccia funzionare un computer come un registratore di cassa. Deve permettere di inserire i prezzi dei prodotti acquistati da ciascun cliente e stampare lo scontrino. A fine giornata deve visualizzare il totale incassato, il numero degli scontrini emessi, l'importo delle vendite diviso per quattro categorie merceologiche: Alimentari, Detersivi, Salumeria, Altro. 69) Assegnato N far visualizzare tutti i numeri primi minori di N che fanno parte della serie di Fibonacci. 70) Far leggere una serie di numeri in input e fare la media solo dei numeri primi. 71) Far generare tutte le possibili combinazioni di risultati di 4 partite di calcio. (es; 1111, 111X, 1112, 11X1, 11XX, 11X2, 1121 ecc. Si tratta delle permutazioni con ripetizione di 3 elementi a 4 a 4. Si può, per esempio, usare il sistema di num. ternario incrementando una variabile e trasformandola in ternario). Risposte esercizi Esercizio 18): a) dopo >> ci deve essere una variabile e non un’espressione b) dopo cout è permesso solo l’operatore << !55 Corso sul Linguaggio C++ Edizione 2010 c) Il linguaggio C++ è case sensitive, FOR non è la stessa cosa di for, inoltre per separare l’istruzione di inizializzazione dalla condizione e dall’istruzione di incremento bisogna usare il punto e virgola (;) e non la virgola. d) manca il punto e virgola finale Esercizio 19) Ciclo N° di volte Valore di i a) 5 10 b) 4 12 c) 1 10 d) 3 -5 Esercizio20) A B C 10 20 -110 -10 -20 -30 5 4 101 60 -10 70 Esercizio 21): scrive su video tutti i numeri dispari minori di 50. Esercizio 22) esatta d) Esercizio 23) a) b) c) d) for (int i=1; i<7;i++) cout << i %5<<’ ‘; …1 2 3 4 0 1 ……….……. for (int i=1; i<7;i++) cout << i*2-1 <<’ ‘; ………………1 3 5 7 9 11 ………………………….. int i=10; do {cout << i<<’ ‘; i -=5;} while (i>0); …………10 5…………………………………… int j=5,i=5; while(i>0) {cout << j <<’ ‘; if (i%2) j +=i else j-=i; i--;} …5 10 6 9 7……………….. . Esercizio 24) esatta d): il tipo double ha una rappresentazione interna in binario virgola mobile e un numero con la virgola tipo 1/50 potrebbe diventare periodico quando viene trasformato in binario - anzi 1/50 è periodico in binario perché non danno luogo a numeri binari periodici solo le frazioni che si possono rappresentare con un denominatore che è una potenza del 2 - e quindi ha infinite cifre per cui non può essere rappresentato esattamente, questo significa che sommando 50 volte 1/50 scritto in binario in maniera non esatta, probabilmente non otterremo 1 e quindi il ciclo potrebbe non finire mai perché non si verifica mai la condizione i=1.0 che fa terminare il ciclo. Esercizio 40) Il cuore del programma per trovare il MCD potrebbe essere il seguente: if (A < B) {r=A; A=B; B=r;} // ordina i due numeri r=A%B; while (r > 0) {A=B; B=r; r=A%B;} MCD = B; Esercizio 46) Il programma potrebbe essere il seguente: int main(){ short int scelta=0; /* ripete la visualizzazione del menù fino a quando non si sceglie la funzione FINE */ do { // visualizza il menù system("CLS"); // cancella la finestra di output cout << endl << "\t\tPossibili scelte" << endl; cout << "\t\t1) Funzione 1"<< endl; cout << "\t\t2) Funzione 2"<< endl; cout << "\t\t3) Funzione 3"<< endl; cout << "\t\t4) Funzione 4"<< endl; cout << "\t\t5) Fine"<< endl; !56 Corso sul Linguaggio C++ Edizione 2010 cout << "\n\n\t\t\tQuale Scegli (1-5)?" ; cin >> scelta; // chiede la scelta // visualizza messaggio di errore se la scelta non è valida if (scelta <1 || scelta > 5) { cout << "\n\t\tErrore! Inserire un numero da 1 a 5!\n"; system("PAUSE"); } else { // esecuzione della funzione corrispondente switch (scelta) { case 1: cout <<"E' stata scelta la funzione 1\n"; break; case 2: cout <<"E' stata scelta la funzione 2\n"; break; case 3: cout <<"E' stata scelta la funzione 3\n"; break; case 4: cout <<"E' stata scelta la funzione 4\n"; break; } if (scelta < 5) system("PAUSE"); } } while (scelta != 5); return 0;} Esercizio 51) Si può sfruttare la compatibilità tra il tipo di dati char e il tipo int più il fatto che l’istruzione cout visualizza una variabile char come carattere e una int come numero. Il programma potrebbe essere il seguente: int i; char x; for (i=1; i<=255; i++) {x = i; cout << i << '=' << x << '\t';} cout << endl; !57