Imparare C++

162
I LIBRI di Roberto Allegra IMPARARE C++ DALLE FONDAMENTA ALLA PROGRAMMAZIONE AVANZATA. IL MANUALE SEMPLICE PER LAVORARE CON IL LINGUAGGIO PIÙ USATO AL MONDO

description

Guida base cpp ( ita )

Transcript of Imparare C++

  • 2006 Edizioni MasterTutti i diritti riservati

    IMPARARE

    C++Il c++ il padre di ogni linguaggio diprogrammazione. Leleganza, la portabilit, laflessibilit ne fanno uno strumento in gradodi controllare ogni aspetto del ciclo di vita diunapplicazione. Il fascino che questo linguaggioesercita su ogni programmatore dovutoprincipalmente alla sua assoluta capacit diessere controllabile in ogni elemento. Tantaflessibilit induce a muoversi attraverso terreniinesplorati il cui unico limite la fantasia. Viceversa, tanta potenza necessita diunassoluta padronanza dei meccanismi di basecos come di quelli avanzati. Roberto Allegra simuove agevolmente attraverso un universotanto sconfinato quanto affascinante, illustrandogli aspetti di base ma arrivando a trattare ancheargomenti complessi utili per i programmatoripi smaliziati.

    Le basi del linguaggio ed i costrutti fondamentali Il controllo del flusso e i costrutti di selezione Lavorare con un linguaggio tipizzato Programmazione procedurale e ad oggetti

    I LIBRIdi

    i LIb

    ri d

    iIM

    PAR

    AR

    E

    C+

    +

    Roberto Allegra

    IMPARAREC++

    DALLE FONDAMENTA ALLA PROGRAMMAZIONE AVANZATA. IL MANUALESEMPLICE PER LAVORARE CON IL LINGUAGGIO PI USATO AL MONDO

    Copertina C++ 1-02-2006 12:46 Pagina 1

  • Frontespizio 31-08-2005 17:26 Pagina 2

  • i libri di

    Roberto Allegra

    IMPARARE

    C++A Giabim, che sa perch

    Frontespizio 1-02-2006 11:44 Pagina 1

  • Frontespizio 1-02-2006 11:44 Pagina 2

  • I libri di ioPROGRAMMO/Imparare C++ 3

    Introduzione IMPARAREC++

    INTRODUZIONEQuesto libro ti introdurr nel mondo del C++: un linguaggio tantoapprezzato e diffuso che non umanamente possibile tenere ilconto dei compilatori che lo supportano, n dei milioni di pro-grammatori che lo utilizzano, n dei libri scritti a riguardo, fra cuiquello che hai fra le mani in questo momento. Alcuni di questi testisono veramente ben strutturati, chiari, e completi - puoi consultarelappendice per una bibliografia essenziale. Programmare inC++, terza edizione edito da Addison Wesley [1], in particolare,pi familiarmente noto come lo Stroustrup, (dal nome diBjarne Stroustrup: autore del libro, nonch creatore del C++), decisamente il riferimento assoluto: il Testo Sacro del programma-tore C++. difficile, per, che tu riesca ad apprezzarlo appieno sesei completamente digiuno di C++ o, ancor peggio, di program-mazione: si tratta, infatti, di una Bibbia da un migliaio di paginescritte in modo molto dettagliato, e il C++ noto per molti meri-tatissimi pregi, ma non certo per essere un linguaggio semplice daimparare. Intendiamoci subito: un errore comune sopravvalutarnela difficolt e la curva di apprendimento. Come ogni prodottopotente e complesso, va affrontato per gradi maturando un po allavolta la consapevolezza necessaria per sfruttarlo appieno. quindiovvio che la finalit di questo testo non certo quella di rendertiun guru capace di districarsi nei mille sentieri aperti dallaconoscenza del C++. Ma alla fine della lettura sarai gi produtti-vo nelluso del linguaggio, conoscerai le sue potenzialit, e avrai lapossibilit di esplorarlo con una mentalit corretta. Io presumersempre che tu non abbia mai programmato prima, anche se prob-abilmente qualche riga di codice lavrai gi scritta. Forse hai addirit-tura sviluppato per anni in qualche altro linguaggio, e ti sei deciso amigrare a C++, per superare vincoli e limiti, avvicinandoti per laprima volta a quel modello OOP (Object Oriented Programming vedi capitolo 6) di cui hai gi sentito parlare. In questo libro tro-verai molte risposte, e qualche domanda. In sintesi:

    intro 1-02-2006 12:35 Pagina 3

  • Introduzione

    I libri di ioPROGRAMMO/Imparare C++4

    IMPARAREC++

    Il capitolo 1 ti illustrer la storia di questo linguaggio: da doveviene, perch si usa, e quali sono i programmi necessari per com-inciare a scrivere applicazioni.Il capitolo 2 insegna ad usare i dati primitivi del linguaggio, ecombinarli in espressioni.Il capitolo 3 mostra come gestire il flusso dellesecuzione conselezioni, cicli e salti.Il capitolo 4 tratta la creazione di nuovi tipi, come indirizzarlimediante puntatori e riferimenti, ed introduce la memoria dinamica.Il capitolo 5 spiega come utilizzare il C++ per una program-mazione di stampo procedurale, controllando lo spazio dei nomiattraverso i namespaces.Il capitolo 6 segna la differenza sostanziale fra C e C++, ovverolapproccio Object Oriented. Imparerai cosa si intende per OOP ecome il C++ definisce gli oggetti tramite classi.Il capitolo 7 specificher come il C++ applica questi principi: sen-tirai parlare di ereditariet multipla, funzioni virtuali e polimorfismo.

    Questi argomenti costituiscono la base imprescindibile per program-mare in maniera corretta ed efficiente in C++. Nel seguito di questolibro approfondir, invece, gli aspetti pi avanzati: la programmazionegenerica, le eccezioni, la libreria standard, e alcuni esempi concreti. Conuna pratica molto inusuale nei manuali italiani, ma del tutto naturale inaltre lingue (ad esempio, in inglese), ho deciso di darti del tu. Spero chequesto mi abbia aiutato nel fermo proposito di mantenere sempre unapproccio semplice e hands-on, fuggendo i tecnicismi pi insidiosi, ei pomposi e caricaturali pigli cattedratici del tipo si badi che al let-tore sar lasciato come esercizio.In definitiva, ho voluto intendere questo libro come unintroduzione

    indolore e alla mano di uno dei linguaggi che pi suscitano (a ragione)un sacro timore reverenziale. A questo proposito: se dovessi avere seg-nalazioni o problemi nel seguire il libro, puoi provare a contattarmiallindirizzo [email protected].

    intro 1-02-2006 12:35 Pagina 4

  • I libri di ioPROGRAMMO/Imparare C++ 5

    Per iniziareCapitolo 1 IMPARAREC++

    PER INIZIARENessun linguaggio nasce perfetto, n spunta fuori dal nulla, e ilC++ non fa eccezione. Cominciarne lo studio senza avere presen-ti le coordinate in cui ci si muove porta ad una comprensione raf-fazzonata, che scaturisce (nel migliore dei casi) in quelle affer-mazioni dogmatiche e domande raccapriccianti che i neofiti mani-festano spesso nei newsgroup o nei forum dedicati al C++.Ho ritenuto che la forma pi leggera per trattare largomento fosseproprio una serie di domande di tal fatta (una FAQ):

    COME SI PRONUNCIA C++?La pronuncia corretta s pls pls, allinglese. Molti, in Italia,lo chiamano comunque ci pi pi.

    QUANDO STATO INVENTATO?Il C++ stato pregettato nei primi anni 80, da Bjarne Stroustrup,con il nome iniziale di C with classes (C con classi). Nel corsodi questo ventennio, ovviamente, il C++ ha subito molti cambia-menti! Qui ci dedicheremo alle basi del linguaggio, ma se sei inter-essato alla sua evoluzione potresti trovare pi utile una lettura dellibro suggerito in Bibliografia al numero [2], piuttosto che perder-ti nel marasma degli standard.

    COSA VUOL DIRE C++?Il C [3] il linguaggio creato da Dennis Ritchie intorno agli anni 70, an-cor oggi diffusissimo e molto apprezzato per le sue doti di efficienza edespressivit. Proprio per questo Stroustrup ha pensato di fondare su di es-so il proprio linguaggio.Come vedrai presto, in C e nei suoi derivati, loperatore++ indica loperazione di incremento. Il nome C++,quindi, vuole suggerirelidea di un C migliorato. In effetti il C++ unestensione del C, lin-guaggio su cui si basa interamente. I compilatori commerciali, in genere,permettono di scrivere sia in C che in C++, e si pu sperare che in futuroi due linguaggi diventeranno pienamente compatibili.

    capitolo 1 1-02-2006 12:41 Pagina 5

  • I libri di ioPROGRAMMO/Imparare C++6

    IMPARAREC++

    ALLORA DEVO CONOSCERE IL C PERIMPARARE IL C++?Aiuterebbe, ma non necessario. Anzi: io parto dallassunto che tunon conosca alcun linguaggio informatico (vedi introduzione).Il Csi rivolge ad uno stile di programmazione procedurale, e molti suoiaspetti (vedi domanda successiva) vengono completamente ride-finiti dal C++: non avere una forma mentis inquinata dalla pro-grammazione in C, quindi, potrebbe rivelarsi un vantaggio.

    IN COSA MIGLIORATO RISPETTO AL C?Oltre a controlli molto pi stretti sui tipi, e ad alcune differenze disintassi, il C++ aggiunge diverse parole chiave, ha un meccanismopi sicuro ed intuitivo per la memoria dinamica, e, soprattutto, sup-porta luso delle classi e della programmazione generica. La dif-ferenza fondamentale, in effetti, che per programmare in C++ richiesta una conoscenza della programmazione a oggetti e deiproblemi ad essa correlati.

    QUINDI C++ UN LINGUAGGIOORIENTATO AD OGGETTI?C++ non puramente orientato agli oggetti, perch permetteuna programmazione di stampo procedurale, come quella del C. Iltipico programmatore C++, tuttavia, vede la possibilit di sceglieredi non aderire pienamente ad un rigido sistema prefissato come ungran vantaggio (vedi domanda successiva).Il C++ pu essere definito pi propriamente come un linguaggioa paradigma multiplo (procedurale, a oggetti, e generico).

    PERCH DOVREI SCEGLIERE C++ E NONUN ALTRO LINGUAGGIO AD OGGETTICOME JAVA, C#, O EIFFEL?Il C++ considerato un linguaggio un po pi complicato rispettoa Java e C#, e limpianto ad oggetti non regge il confronto con la

    capitolo 1 1-02-2006 12:41 Pagina 6

  • I libri di ioPROGRAMMO/Imparare C++ 7

    Per iniziareCapitolo 1 IMPARAREC++

    purezza offerta dal design by contract di Eiffel. Le ragioni prin-cipali di tale differenza sono a mio avviso tre:

    Il rapporto con la macchina: C++ pu diventare, alloc-correnza, un linguaggio di basso livello capace di sporcarsi lemani direttamente con il processore, e tutta la sua architettura progettata per la vicinanza al calcolatore. Questo si traducein flessibilit e prestazioni che in linguaggi di pi alto livellosono semplicemente irraggiungibili.C++ non un linguaggio a oggetti, ma un linguaggiomultiparadigma, pertanto presenta unofferta pi vasta eflessibile al programmatore, che deve per possedere unapadronanza dei concetti e del linguaggio tali da saperscegliere quella pi vantaggiosa nel suo caso specifico.C++ non impone lo stile da usare nella programmazionecon delle regole sintattiche apposite. Questo aspetto vieneusato dai detrattori del C++ come motivo di condanna (nonavere vincoli rigidi pu portare a codice poco leggibile), maviene invece spesso elogiato da quei programmatori che nonvogliono sentirsi soffocati da regole coercitive ed inflessibili.

    HO SENTITO PARLARE DI MFC, MANAGEDEXTENSIONS, CLI MA QUANTI C++ESISTONO?Uno solo, ovviamente. Ma a differenza di linguaggi come VB,Delphi o C#, il C++ non un linguaggio proprietario. Di con-seguenza, ogni societ libera di costruire il proprio compilatore(per le architetture pi svariate), realizzarlo come vuole, e vender-lo restando attinente, si spera sempre, ad uno standard che per-metta la piena compatibilit con quelli degli altri. Diverse case pro-pongono anche delle librerie (come le Microsoft FoundationClasses o MFC) per permettere o semplificare la program-mazione visuale, o per inserire il linguaggio in un Framework pi

    capitolo 1 1-02-2006 12:41 Pagina 7

  • Capitolo 1

    I libri di ioPROGRAMMO/Imparare C++8

    IMPARAREC++ Per iniziare

    ampio, come il caso delle managed extensions in C++.NET. Noi,qui, ci limiteremo al C++ standard (ISO), che non supporta laprogrammazione visuale, ma che permette di costruire applicazioniconsole pienamente funzionanti, e costituisce la base per tutte leestensioni immaginabili.

    ESISTONO TANTI COMPILATORI. QUALEDOVREI SCEGLIERE? E QUANTOCOSTANO?Esistono compilatori migliori e peggiori, generici o ottimizzati peril singolo processore, per PC e per sistemi embedded, gratuiti o apagamento.In questo libro ci baseremo su GCC (e la sua versione MinGW perWindows), fermo restando che tutto ci che diremo sar compati-bile per altri compilatori aderenti allo standard ISO.

    Setup dellambiente di lavoroAvendo un compilatore e impostando le variabili di ambiente inmaniera corretta, possibile creare programmi col sempliceNotepad di windows, o con uno dei diecimila editor che le dis-tribuzioni Linux mettono mediamente a disposizione. Nel corsodegli anni, per, sono state sviluppate delle tecniche per renderela vita pi comoda a chi scrive codice, e siccome quella delProgrammatore una categoria pigra per definizione, oggi nonmolti sarebbero disposti a rinunciare ai vizi offerti da un bellIDE(Integrated Development Environment - ambiente di svilup-po integrato). Ogni programmatore ha il suo IDE preferito (o i suoipreferiti), secondo parametri soggettivi che vanno dalla fede dap-partenenza (pro o anti windows, etc), alla presenza/mancanzadella caratteristica X (sempre irrinunciabile). Se non hai un IDEinstallato nel tuo computer, ti consiglio Code::Blocks(http://www.codeblocks.org/): un ambiente di sviluppo opensource, cross platform, che si aggancia a vari compilatori. Se lavori

    capitolo 1 1-02-2006 12:41 Pagina 8

  • I libri di ioPROGRAMMO/Imparare C++ 9

    Per iniziareCapitolo 1 IMPARAREC++

    sotto il Sistema Operativo Windows, dovrai scaricare la versionecomprensiva di compilatore MinGW (Minimalist Gnu forWindows: un porting del compilatore GCC). Sbrigate le formalitdellinstallazione, dovresti trovarti davanti alla schermata rappre-sentata in (figura 1.1).

    Come vedremo ben presto, questo ambiente ci permetter di scri-vere codice, compilarlo, eseguirlo e testarlo (tutto in un uno!) LIDEprovveder a svolgere per noi tutti i passi di quel processo chedeve essere percorso per poter tradurre il codice in un fileeseguibile. Per avere una visione corretta di come funziona il C++,per, dovremo partire guardando in dettaglio proprio il funziona-mento cruciale di questi passaggi normalmente nascosti.

    1.2 IL PROCESSO DI SVILUPPOOra che abbiamo impostato tutto, vediamo come funziona il processoche consente ad uno sviluppatore C++ di ottenere la sua applicazionecome risultato finale. I passi sono diversi e spesso si compongono diulteriori suddivisioni interne. Nei paragrafi che seguono analizzeremo ilproblema secondo il punto di vista schematizzato nella (figura 1.2).

    Figura 1.1: LIDE Code ::Blocks.

    capitolo 1 1-02-2006 12:41 Pagina 9

  • Capitolo 1

    I libri di ioPROGRAMMO/Imparare C++10

    IMPARAREC++ Per iniziare

    1.2.1 CREAZIONE DI UN PROGETTOPer prima cosa il programmatore pianifica e schematizza il suo lavoro,spendendo tutto il tempo necessario, individuando i bisogni e gli obiettividellapplicazione che dovr costruire. un passo fondamentale e spessofin troppo trascurato: vedremo questa fase pi in dettaglio nel quintocapitolo. Per ora, per noi il tutto si traduce soltanto nel creare un nuovoprogetto in Code::Blocks.Un progetto serve ad avere sotto controllo i moltifiles diversi che sar necessario scrivere, e che verranno poi riassemblatiin un secondo tempo. Per cominciare, allora, apri Code::Blocks e clicca suNuovo/Progetto.Come puoi vedere in (figura 1.3), ti verr richiesto qualetipo di progetto vuoi realizzare. In questo libro noi analizzeremo una so-

    Figura 1.3: Creazione di un nuovo progetto in Code::Blocks

    Figura 1.2: Il processo di sviluppo di unapplicazione, dalla progettazione al linking.

    capitolo 1 1-02-2006 12:41 Pagina 10

  • I libri di ioPROGRAMMO/Imparare C++ 11

    Per iniziareCapitolo 1 IMPARAREC++

    la tipologia, ovverosia le applicazioni di stampo console-based. Nel cam-po Nome, scrivi:CiaoMondo.Ti verr presentata una schermata conun piccolo programma gi scritto. Qui comincia il secondo (e pi impeg-nativo) passo dello sviluppatore.

    1.2.2 SCRITTURA DEL CODICEIl progetto ha creato automaticamente un file, chiamato main.cpp,in cui memorizzato il codice predefinito. Non c nulla di magicoin quanto si trova in questa finestra. Possiamo e dobbiamo modi-ficare il codice sorgente a nostro piacimento! Proviamo a cancel-lare tutto, e scrivere quanto segue:

    //Il nostro primo programma!

    #include

    using namespace std;

    int main()

    {

    cout

  • Capitolo 1

    I libri di ioPROGRAMMO/Imparare C++12

    IMPARAREC++ Per iniziare

    maniera pi tecnica: il C++ un linguaggio di alto livello. Tende,cio, a trattare i problemi in unottica molto astratta, vicina almodello umano di rappresentazione dei problemi (come vedremo:oggetti, classi, strutture), ma decisamente lontana da quella delprocessore, che opera invece con lunghe sequenze di istruzionisemplici. Per trasformare quello che abbiamo scritto in qualcosa dicomprensibile per lelaboratore, bisogna far ricorso ad un compi-latore: un traduttore che trasforma i files sorgente (quelli scrit-ti in C++, con estensione cpp), in files oggetto (con estensioneobj, oppure o). Alla fine della compilazione, quindi, otterremo unaserie di files oggetto, tradotti in linguaggio macchina. Code::Blocks usaautomaticamente il compilatore che gli viene fornito: nella fattispecieGCC (oppure MinGW) chiamandolo attraverso la riga di comando, pro-prio come si farebbe a mano.Fortunatamente lIDE si occupa anche di inizializzare i riferimenti ai filee alle librerie standard, rendendo la richiesta di compilazione semplicequanto la pressione di un tasto (o un click). Alla fine del processo dicompilazione necessario ricucire i vari files .obj, risolvendone leinterdipendenze e le chiamate alle librerie esterne. Il programma chesvolge questo compito si chiama linker, e viene richiamato automati-camente dallIDE dopo la fase di compilazione. Il risultato , finalmente,il file eseguibile tanto agognato! Prova a compilare il codice premendoi tasti CTRL+F9: verranno creati i files oggetto e il linker comporr ilbinario risultante, che puoi eseguire premendo CTRL+F10. Spesso vor-rai realizzare entrambi i passaggi in una volta sola: baster premere iltasto F9.

    1.3 CIAO, MONDO!Il risultato dellesecuzione visibile in (figura 1.4). Lapplicazione girasulla console testuale, che in Windows viene aperta per loccasione nel-la finestra di emulazione MS-DOS.A questo punto, come promesso, ripren-diamo in mano il codice, spiegandolo riga per riga. Non mia intenzionefarti capire adesso il significato pi intimo di ogni istruzione, ma indis-

    capitolo 1 1-02-2006 12:41 Pagina 12

  • I libri di ioPROGRAMMO/Imparare C++ 13

    Per iniziareCapitolo 1 IMPARAREC++

    pensabile che tu ti faccia unidea su come strutturato un programma tipi-co. Nel corso dellanalisi vedremo anche alcuni concetti chiave, come icommenti, il preprocessore e luso degli stream predefiniti.

    1.3.1 IL NOSTRO PRIMO PROGRAMMALa prima riga ci mostra una caratteristica tipica degli editor di codice:il syntax highlighting, ovverosia la diversa colorazione del testo a se-conda del relativo valore sintattico. Code::Blocks colora questa primaistruzione di grigio, colore che riserva ai commenti. Un commento unariga di codice che non sar presa in considerazione dal compilatore, mache utile come promemoria per i programmatori che leggerannoquanto scritto: saper commentare efficacemente il proprio lavoro uno dei punti che distinguono i buoni programmatori dalla massa de-gli scrittori di codice. Il C++ permette due tipi di commenti. Quel-lo usato per questa linea (una doppia sbarra) il commento a rigasingola, che ordina al compilatore di saltare tutto ci che si trova al-la destra delle sbarre, fino al primo ritorno a capo. Un secondo tipo dicommento (mutuato dal C) quello a blocchi, che inizia con la se-quenza /* e termina con */, e che pu estendersi su pi righe.

    Figura 1.4: Output dellesecuzione di

    capitolo 1 1-02-2006 12:41 Pagina 13

  • Capitolo 1

    I libri di ioPROGRAMMO/Imparare C++14

    IMPARAREC++ Per iniziare

    1.3.2 #INCLUDE Questa seconda riga segnalata in verde. Tale colore indica le di-rettive rivolte al preprocessore: un programma richiamato durante ilprimissimo stadio della compilazione, e che ha lo scopo di sostitui-re i riferimenti a macro e file header esterni, con i valori relativi (non-ch di saltare i commenti). Listruzione #include , in particolare,serve ad includere (cio a copiare letteralmente) un file qualsiasi. I fi-les inclusi si chiamano headers (intestazioni), e hanno lo scopo didichiarare funzioni e variabili esterne. Sono, in altre parole, la por-ta di accesso che permette al nostro programma di usare le fun-zionalit presenti in librerie esterne. Il file in questione (iostream,che puoi trovare nella sottocartella include/c++/3.x.x), fa parte del-la libreria standard del C++, e contiene le definizioni necessarie a ge-stire i flussi di ingresso e di uscita (vedremo meglio la definizione distream pi avanti): includerlo ci permetter di usare due variabiliesterne fondamentali:

    std::cout: serve a scrivere sul canale di uscita primario (soli-tamente, la finestra della console) std::cin: serve a ricevere dal canale dingresso primario (soli-tamente, la tastiera).

    1.3.3 USING NAMESPACE STD;Anche se vedremo bene il concetto pi avanti, per ora puoi vedere inamespace come dei contenitori che racchiudono nomi di variabili efunzioni, in maniera da distribuire il codice opportunamente e risol-vere ambiguit (un po come le directory usate dai sistemi operati-vi). E possibile accedere ad una variabile o ad una funzione dichia-rata in un namespace attraverso il simbolo ::. Cin e cout, ad esem-pio, fanno parte del namespace std, in cui sono racchiusi nomi e fun-zioni della libreria standard del C++, e pertanto andrebbero richia-mati, ogni volta, scrivendo std::cin e std::cout. Ma, se torni aguardare il codice, vedrai che non labbiamo fatto!

    capitolo 1 1-02-2006 12:41 Pagina 14

  • I libri di ioPROGRAMMO/Imparare C++ 15

    Per iniziareCapitolo 1 IMPARAREC++

    Il merito proprio dellistruzione using namespace std;, cheindica al compilatore di dare per scontato che vogliamoriferirci a variabili appartenenti al namespace std: cirende tutto pi comodo.

    1.3.4 INT MAIN() { }Questa riga indica linizio della funzione principale (main), detta an-che punto dingresso dellapplicazione. Scriverla obbligatorio, esarebbe insensato fare altrimenti, dal momento la funzione mainspecifica quel codice che dovr essere eseguito allavvio del pro-gramma.Questa riga, inoltre, specifica un valore di ritorno di tipo intero (int).La funzione principale, cio, dovr restituire, alla fine dellesecuzio-ne, a un valore intero, che viene in genere usato dal sistema, o dal pro-gramma chiamante, per sapere se lesecuzione andata a buon fi-ne, o meno. Le parentesi graffe che racchiudono il blocco distruzio-ni che seguono sono anchesse obbligatorie, e servono ad indicarelinizio e la fine della funzione.

    1.3.5 COUT

  • Capitolo 1

    I libri di ioPROGRAMMO/Imparare C++16

    IMPARAREC++ Per iniziare

    (Ciao, mondo!!!): una sequenza di caratteri, che va inizializzata sem-pre fra virgolette.

    Endl un manipolatore esterno appartenente a std, e serve ad inserire unasequenza di fine riga (andare a capo). Per finire, non va dimenticato ilpunto e virgola, che serve ad indicare che listruzione terminata.

    1.3.6 RETURN 0;Infine, lultima riga: dal momento che abbiamo dichiarato che la fun-zione main deve restituire un valore intero, dobbiamo mantenere lapromessa. Come vedremo, la parola chiave return serve proprio aquesto scopo: interrompe lesecuzione della funzione, e restituisce ilvalore indicato come parametro. In questo caso, 0, che indica une-secuzione andata a buon fine.

    1.4 CONCLUSIONIIn questo primo capitolo abbiamo accennato a moltissime cose,che vedremo molto pi compiutamente in futuro. Se quindi ti staipreoccupando perch credi di non aver capito cos perfettamenteuno stream, o un tipo di dato, voglio tranquillizzarti: sarebbe stra-no il contrario. Ci che necessario per continuare, che tu abbia:

    Installato correttamente lIDE Code::Blocks, e fatto girare il tuoprimo programma.

    Imparato chiaramente il processo: scrittura -> compilazione -> esecuzione

    Imparato come creare un nuovo progetto, come scrivere codi-ce, e la struttura tipica di un programma C++.

    Imparato come usare la variabile cout per scrivere dati sulloschermo

    capitolo 1 1-02-2006 12:41 Pagina 16

  • I libri di ioPROGRAMMO/Imparare C++ 17

    Per iniziareCapitolo 1 IMPARAREC++

    Imparato come usare la variabile cin per bloccare il flusso del-lesecuzione.

    Dal prossimo capitolo imparerai a scrivere applicazioni pi comp-lesse.

    1.5 PROGETTI ED ESERCIZIAlla fine di ogni capitolo, ti proporr esercizi e progetti da realizza-re da solo. In questo caso, date le scarse conoscenze finora accu-mulate, cominciamo con un compito molto semplice:

    Scrivere un programma che stampi su tre linee distinte, le pa-role casa dolce casa

    Estensione: Di quante istruzioni di tipo cout

  • capitolo 1 1-02-2006 12:41 Pagina 18

  • I libri di ioPROGRAMMO/Imparare C++ 19

    Dati ed espressioniCapitolo 2 IMPARAREC++

    DATI ED ESPRESSIONIPer ora siamo in grado di usare la console alla stregua di una mac-china per scrivere: alla fine di questo capitolo sapremo costruire pro-grammi ben pi complessi. Impareremo i tipi di dato fondamentali,la possibilit di ricevere input dallutente, lelaborazione di espressioni.Probabilmente tutto ci rende questo capitolo il pi importante eimpegnativo. Se sei a digiuno di programmazione, prenditi moltotempo per studiarlo bene e svolgere gli esercizi proposti.

    2.1 TIPI DI DATO FONDAMENTALILa programmazione OOP, le cui basi teoriche vedremo nel capito-lo 5, si basa sul concetto espresso nella citazione: ogni ente rap-presentabile attraverso degli oggetti, che possono essere visticome una composizione di dati pi semplici. Alla base di ognioggetto, quindi, direttamente o meno, ci sono sempre dei tipiatomici, chiamati primitivi, che il C++ fornisce per la descrizionedi dati fondamentali (caratteri, numeri interi, numeri decimali evalori logici): avviarsi sulla strada del C++ senza conoscerlisarebbe un grave errore! Qui di seguito definisco i vari tipi di dato,e nel paragrafo successivo vedremo come questi possono essereusati per i nostri scopi pratici.

    2.1.1 Variabili: le variabili booleane (dichiarate con la parola chiavebool, pronuncia bul), si usano per indicare un dato che pu averesolo due stati: true (vero), oppure false (falso). Per rappresentarei dati bool sarebbe sufficiente un bit, tuttavia (a meno di ottimiz-zazioni particolari) i calcolatori sono costretti a riservar lorocomunque lunit minima di allocazione, che solitamente 1 byte.Costanti: Nella conversione a intero, true equivale a 1 e falseequivale a 0: in effetti, in C++ - cos come in C - la verit logica siesprime con ogni valore diverso da 0.

    capitolo 2 1-02-2006 12:41 Pagina 19

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++20

    IMPARAREC++ Dati ed espressioni

    2.1.3 INTERO Valori: Gli interi (parola chiave int) rappresentano i numeri natu-rali in un raggio che varia a seconda del numero di bytes riservatia questo tipo di dato (minimo 2, in genere 4). possibile allungareo accorciare tale raggio con dei modificatori:short (o short int), indica di utilizzare, se possibile, un raggio picorto.long (o long int), indica di utilizzare, se possibile, un raggio pilungoNormalmente gli interi sono signed, e, anche se solitamente non una pratica molto ben vista, possibile dichiarare un unsignedint, short o long, per utilizzare anche il bit riservato al segno.Costanti: Le costanti di tipo intero possono essere scritte sem-plicemente indicando il numero corrispondente. anche possibilescrivere il numero in esadecimale, anteponendo i due caratteri 0x,e in ottale, anteponendo il numero 0. Il numero 64, ad esempio,pu essere scritto in ottale come 0100, e in esadecimale come0x40.

    2.1.4 DECIMALEValori: I numeri decimali vengono rappresentati in C++ attraverso ilsistema della virgola mobile. Sono possibili tre tipi di dichiarazione:float: indica un numero a precisione singoladouble: indica un numero a precisione doppialong double: indica un numero a precisione estesa.Come al solito, il numero di bytes, il raggio dei valori, nonch le-satto significato dei termini precisione doppia e precisione este-sa, dipendono completamente dallinterpretazione che il compila-tore ne d.Costanti: Una costante pu essere dichiarata come decimale in di-versi modi. Il pi semplice quello di porre il punto decimale, anchequando si tratta di cifre di per s intere: (3.1415, 16.0, etc). Un usosconsiderato delle costanti pu portare ad effetti imprevisti: il risul-

    capitolo 2 1-02-2006 12:41 Pagina 20

  • I libri di ioPROGRAMMO/Imparare C++ 21

    Dati ed espressioniCapitolo 2 IMPARAREC++

    tato di 3.0/2.0 1.5, mentre 3/2, in quanto divisione fra interi, risulta1 (col resto di 2)!

    2.2 DICHIARAZIONE,INIZIALIZZAZIONE E ASSEGNAMENTOOra che abbiamo visto i principali tipi di dato e le relative parolechiave, dobbiamo riuscire ad usarle attivamente nel codice: perquesto dobbiamo introdurre il concetto di variabile: una zona dimemoria in cui presente un dato di un tipo ben definito, che possibile modificare durante il corso dellesecuzione. Per poterusare una variabile necessario:

    Darle un nome, ovverosia un identificativo. Per esserelegale, un ID devessere una sequenza alfanumerica inizianteper lettera - a, a1, c2d3, ad esempio.

    Stabilire il tipo cui appartiene: questa scelta va fattasubito, e non possibile cambiarla durante il corso del pro-gramma.

    Per comunicare queste scelte al calcolatore necessariodichiarare la variabile in un punto qualsiasi del programma.La dichiarazione viene fatta secondo la sintassi:

    tipo variabile;

    Proviamo a vedere questo programma:

    //Dichiarazioni

    #include

    using namespace std;

    capitolo 2 1-02-2006 12:41 Pagina 21

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++22

    IMPARAREC++ Dati ed espressioni

    int main() {

    long x; //tipo = long, variabile = x

    int y; //tipo = int, variabile = y

    char z; //tipo = long, variabile = z

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 23

    Dati ed espressioniCapitolo 2 IMPARAREC++

    valore iniziale. Il C++ offre questa possibilit attraverso un ampli-amento della sintassi della dichiarazione:

    tipo variabile = espressione;

    A titolo di esempio, puoi guardare il codice seguente:

    //Inizializzazioni a riga multipla

    #include

    using namespace std;

    int main() {

    char lettera1 = C;

    char lettera2 = I;

    char lettera3 = A;

    char lettera4 = O;

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++24

    IMPARAREC++ Dati ed espressioni

    Laddove con le parentesi quadre intendo il fatto che linizializ-zazione dei valori facoltativa. Le dichiarazioni dellesempioprecedente possono essere cos riportate in una forma pi com-patta:

    //inizializzazione a riga singola

    char lettera1 = C, lettera2 = I, lettera3 = A, lettera4 = O;

    oppure, rendendo il tutto pi leggibile:

    char lettera1 = C,

    lettera2 = I,

    lettera3 = A,

    lettera4 = O;

    le due scritture sono assolutamente identiche, dal momento che ilritorno a capo in C++ viene ignorato (ho usato lo stesso principioanche per separare i vari valori inseriti in cout nellultimo codice).Spesso non dato sapere al momento della dichiarazione il valoreche assumer la variabile, e questo pu comunque cambiaredurante il corso del programma. In qualsiasi momento, pertanto, possibile effettuare un assegnamento su una variabile gidichiarata, secondo la semplice sintassi:

    variabile = espressione

    come riferimento pratico, puoi analizzare lesempio seguente:

    //Assegnamenti

    #include

    using namespace std;

    int main() {

    int v = 1;

    capitolo 2 1-02-2006 12:41 Pagina 24

  • I libri di ioPROGRAMMO/Imparare C++ 25

    Dati ed espressioniCapitolo 2 IMPARAREC++

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++26

    IMPARAREC++ Dati ed espressioni

    pu sembrare un po magico, ma che in realt frutto di un sapi-ente sovraccaricamento degli operatori, lestrazione cerca diconvertire il dato estratto, coerentemente con il tipo di variabile incui tale dato verr inserito.La sintassi dellestrazione :

    stream >> variabile;

    Dal momento che in questo testo analizzeremo prevalentementecin come stream dingresso, troveremo sempre:

    cin >> variabile;

    Qui di seguito presento un esempio sullutilizzo tipico di cin perlestrazione di un valore:

    //Uso di cin per lestrazione

    #include

    using namespace std;

    int main() {

    cout > a;

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 27

    Dati ed espressioniCapitolo 2 IMPARAREC++

    lema che non abbiamo ancora stabilito alcuna relazione frai dati in nostro possesso. Unespressione una combi-nazione di pi valori effettuata attraverso uno o pi opera-tori.Un insieme di comprensione immediata quello degli opera-tori aritmetici, presentati in tabella 2.1, che include glioperatori comunemente utilizzati sulle calcolatrici tascabili,pi quelli di shift, che meritano un approfondimento. Abbiamogi visto gli operatori con il significato rispetti-vamente di inserimento ed estrazione: questo verosoltanto quando tali operatori si applicano agli stream (comecin e cout), perch il loro significato originale viene ridefinito(in gergo: sovraccaricato).Per i dati primitivi, invece, questi operatori prendono il nomedi bit shifting sinistro () hanno la fun-zione di spostare i bit che compongono il dato, nella rispetti-va direzione, eliminando quelli che finiscono fuori erimpiazzando quelli mancanti con 0.Lespressione 48 >> 3, ad esempio, indica di spostare i bit delnumero 48 (ovvero 00110000), di tre posizioni a destra,dando cos come risultato 6 (ovvero 00000110).Matematicamente, si pu esprimere n > m come n / 2m, a meno di possibili overflow.

    Simbolo OperazioneBitshift destro

    Bitshift sinistro

    Moltiplicazione

    Divisione

    Modulo (resto della divisione)

    Addizione

    Sottrazione

    >>

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++28

    IMPARAREC++ Dati ed espressioni

    Gli operatori, inoltre, lavorano secondo un preciso ordine di prece-denza, che ricalca lordine in cui figurano nella Tabella 2.1. Per al-terarlo necessario usare le parentesi tonde, come si pu notarenel seguente esempio.

    int x = 3 + 3 * 5 * 8 3 // x = 120

    int y = ((3 + 3) * 5) * (8 3) // y = 150

    Ovviamente, ha poco senso lavorare con delle espressioni intera-mente costanti, dal momento che ne conosciamo a priori il risulta-to: in questi casi basterebbe sostituire il codice riportato preceden-temente con i relativi commenti!

    2.4 PRATICA: UN PROGRAMMACALCOLATOREIn questo paragrafo potrai consolidare quanto acquisito finora, scri-vendo un programma che funga da calcolatrice tascabile, intesanella sua versione pi semplice: la nostra applicazione chieder duenumeri in ingresso allutente, e stamper a video la rispettiva som-ma, differenza, etcPuoi provare a realizzarlo da solo, prima diguardare il codice che riportato qui di seguito.

    //Calcolatrice minima

    #include

    using namespace std;

    int main() {

    int a, b;

    //richiesta dei due numeri

    cout > a;

    cout > b;

    capitolo 2 1-02-2006 12:41 Pagina 28

  • I libri di ioPROGRAMMO/Imparare C++ 29

    Dati ed espressioniCapitolo 2 IMPARAREC++

    //stampa dei risultati

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++30

    IMPARAREC++ Dati ed espressioni

    di 10 e 1, e nel secondo 20, che il risultato delloperazione di shif-ting. Ci avviene perch quando i dati vengono convertiti instream, come nel caso di una chiamata a cout, il C++ ridefini-sce automaticamente il significato originario degli operatori dishift con quello sovraccarico di estrazione o inserimento.

    2.5 OPERATORI RELAZIONALI E LOGICIDifficilmente immagineremmo di poter scrivere un programmaqualsiasi senza usare le espressioni aritmetiche viste nel para-grafo 2.4. Altrettanto fondamentali sono quelle che valutano ilconfronto fra due espressioni, realizzato mediante uno deglioperatori elencati in (Tabella 2.2)

    Simbolo OperazioneUguale a

    Diverso da

    Minore di

    Maggiore di

    Minore o uguale a

    Maggiore o uguale a

    ==

    !=

    =

    Tabella 2.2: Operatori relazionali

    Nota: Fa attenzione alloperatore di uguaglianza (==): unodei punti critici in cui cadono tutti i neofiti, i quali tendono aconfonderlo spesso e volentieri con loperatore di assegnamen-to (=), ottenendo cos programmi malfunzionanti.

    Queste espressioni restituiscono un valore bool (vedi paragrafo 2.1.1)che indica se laffermazione dichiarata vera o falsa. Il risultato del codice:

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 31

    Dati ed espressioniCapitolo 2 IMPARAREC++

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++32

    IMPARAREC++ Dati ed espressioni

    Loperatore or (||), restituisce vero quando almeno un valore iningresso vero, e falso altrimenti.

    Il C++ segue quella che viene chiamata logica cortocircuitata, secondola quale la valutazione delloperando di destra viene evitata quan-do il valore delloperando di sinistra sufficiente a determinare ilvalore dellespressione. Ad esempio, nellistruzione:

    a && ((b || c) && !d)

    la prima operazione compiuta dal calcolatore sar valutare se a falso:in tal caso, infatti, sar inutile analizzare il resto dellespressione, poichil risultato finale delloperazione and non potr comunque risultare vero.

    2.6 ASSEGNAMENTO ED INCREMENTIDellassegnamento abbiamo gi parlato nel paragrafo 2.2, ma aquesto punto ne sappiamo abbastanza per comprendere il fatto chegli assegnamenti sono espressioni. Il C++, infatti, eredita dal C la fi-losofia secondo la quale le istruzioni possono restituire un valore (ilche quel che fanno nella maggior parte dei casi). Lassegnamentorestituisce lespressione espressa alla destra delluguale. Detto conun esempio, scrivere:

    a = 5 + b

    ha il doppio significato di poni a uguale a 5+b e 5 + b. Puoi ve-rificarlo provando ad assegnare un assegnamento:

    int a, b;

    a = b = 5;

    capitolo 2 1-02-2006 12:41 Pagina 32

  • I libri di ioPROGRAMMO/Imparare C++ 33

    Dati ed espressioniCapitolo 2 IMPARAREC++

    Questa scrittura perfettamente consentita proprio in virt del fat-to che lassegnamento b = 5 un espressione (vale 5). In questomodo, dopo lesecuzione di queste righe, sia a che b saranno impo-stati a 5. Vedere lassegnamento come espressione quindi, rendepossibile scrivere assegnamenti multipli con una sola istruzione, ealtri trucchi che tendano a far risparmiare righe di codice e alcunitendono anche ad abusarne, producendo codice illeggibile, secondola valida massima di Orazio:sono conciso, divento oscuro. La bre-vit negli assegnamenti un chiodo piuttosto fisso per chi scrive co-dice C++ (e soprattutto C), tanto che sono stati introdotti degli spe-ciali operatori (detti dincremento), per semplificare dei casi autore-ferenziali di assegnamento. Osserva, per esempio, questo caso:

    int a = 1; // a = 1

    a = a + 1; // a = 2

    a = a + 1; // a = 3

    Loperazione a = a + 1 consiste nellincrementare a di ununit. Que-sto caso tanto frequente che stato creato loperatore ++. Scriverea++, quindi, equivalente a scrivere a = a +1; quando si vuole usa-re loperatore ++ come unespressione di assegnamento, invece,esiste una fondamentare differenza fra l'uso della notazione prefis-sa (++a) e quello della notazione postfissa (a++). Per capire qual ,puoi guardare questo codice:

    //Esempio operatore ++

    #include

    using namespace std;

    int main() {

    int a;

    a = 0;

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++34

    IMPARAREC++ Dati ed espressioni

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 35

    Dati ed espressioniCapitolo 2 IMPARAREC++

    grammatori C++, mentre sono il pane quotidiano fra la ristretta cerchi dicoloro che si occupano di ottimizzazione o della manipolazione di mes-saggi digitali.Dandogli uno sguardo,potrai vedere che gli operatori bitwi-se sono simili agli operatori logici; i neofiti, infatti, li confondono spesso,accorgendosene solo quando il codice non funziona secondo le loro aspet-tative.

    Simbolo Operazione A b r

    Complemento

    And

    Or

    Xor

    ~

    &

    |

    ^

    Tabella 2.4: Operatori logici

    01011001100111

    010101010101

    10000101110110

    La differenza fondamentale che distingue gli operatori bitwise dai lo-gici, che mentre questi ultimi agiscono sul valore logico degli ope-randi, i primi applicano dei confronti su ogni singolo bit. Loperazio-ne di complemento (~), ad esempio, restituisce un valore in cui ognibit delloperando viene negato. Pertanto ~120 indica il comple-mento del byte 120 (01111000), ovverosia 135 (10000111). Proprioquesto caso utile per indicare il fatto che le operazioni bit-a-bitsono particolarmente sensibili al tipo di dati in ingresso: quanti bitcompongono linformazione? Lespressione si deve intendere signedo unsigned? A risposte diverse corrispondono risultati completa-mente differenti. Gli altri tre operatori bit-a-bit si usano per molte ra-gioni, soprattutto nel campo della manipolazione di segnali digita-

    capitolo 2 1-02-2006 12:41 Pagina 35

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++36

    IMPARAREC++ Dati ed espressioni

    li. Un impiego tipico lapplicazione di una maschera al primo ope-rando, in modo da estrarne solo la parte di informazione che interessa(il che comune, ad esempio, per creare effetti di trasparenza neiprogrammi di grafica). Il seguente esempio scompone un byte neibit costitutivi, grazie alluso degli operatori bit-a-bit.

    // Scomposizione di un byte.

    // esempio di operatori bit-a-bit.

    #include

    using namespace std;

    int main() {

    cout > dato;

    //converte in byte

    char byte = (char)dato; // casting esplicito

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 37

    Dati ed espressioniCapitolo 2 IMPARAREC++

    Un output di esempio di questo programma :

    Scrivi il numero da convertire: 254

    Il byte, in binario, : 11111110

    Unanalisi del codice rivela innanzitutto unoperazione di casting (lavedremo nel paragrafo 2.10) per la conversione da intero a char, in mo-do da richiedere un numero intero allutente (e non un carattere ASCII),ma operare su dati di 8 bit. Per ricavare il contenuto di ogni bit abbiamocreato una maschera specifica, ottenuta semplicemente un bitshift si-nistro il byte 00000001 (come mostrato nei vari commenti). Una vol-ta ottenuta questa maschera, possiamo utilizzarla per un and bitwi-se (&), in modo da annullare tutti i bit del byte, ad esclusione di quel-lo nella posizione considerata. Otterremo cos un byte che uguale al-la maschera se il bit presente, ed 0 altrimenti. Poich qualunquenumero diverso da zero corrisponde al valore logico 1, un casting a boolpermette di stampare la cifra corretta

    2.8 OPERATORE TERNARIOLultimo operatore di cui dobbiamo discutere molto utilizzato daiprogrammatori che non temono la complicazione a favore della con-cisione. Si tratta delloperatore ternario (?), che introduce il principiodelle espressioni condizionali. Per un esempio pratico e facilmentecomprensibile, puoi riconsiderare il codice del paragrafo 2.5, ed emo-zionarti di fronte al nostro primo bug: cosa succede, infatti, se diamocome secondo operando il valore 0? Lapplicazione va in crash, e dal-la (figura 2.1) si apprende in maniera tanto evidente quanto dram-matica che a dare problemi loperazione di divisione. Il problema, co-me avrai intuito, che la divisione per zero unoperazione illegale,che genera uneccezione: poich il programma non in grado n di pre-venirla, n di gestirla, il risultato il crash dellapplicazione.

    capitolo 2 1-02-2006 12:41 Pagina 37

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++38

    IMPARAREC++ Dati ed espressioni

    Impareremo a gestire le eccezioni pi avanti, per ora ci accontenteremodi prevenirla ovverosia di fare in modo che il programma non si trovi maia dover eseguire una divisione per zero. Le vie che possono condurre aquesto risultato sono diverse: dalla meno sicura (richiedere allutente dinon porre a zero il valore b), alluso di un if (struttura che spiegheremo nelprossimo capitolo). Noi utilizzeremo unespressione condizionale, che re-stituisca il risultato di a/b se b diverso da zero, e 0 in caso contrario; an-che se questo valore non corretto, cos facendo previeniamo loperazio-ne pericolosa, evitando il crash dellapplicazione (in C++ questa co-munque una pratica da rifuggire,come vedremo pi avanti)..Unespressionecondizionale usa loperatore ternario, secondo la seguente sintassi:

    condizione ? se_vero : se_falso

    Se il valore dellespressione booleana condizione true, lespressioneassumer il valore contenuto in se_vero, altrimenti sar equivalente ase_falso. Nel nostro caso, la riga incriminata si trasforma cos:

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 39

    Dati ed espressioniCapitolo 2 IMPARAREC++

    2.9 CASTINGNel paragrafo 2.8 abbiamo avuto la necessit di convertire un va-lore int in un char. Operazioni di questo tipo prendono il nomedi cast, e possono essere implicite oppure esplicite. I casting im-pliciti avvengono spesso, e a volte senza che il programmatorese ne renda conto: un esempio di casting implicito sono le chia-mate a cout, che convertono le espressioni dal loro tipo origi-nario al tipo di stream su cui effettuare linserimento. Questi ca-sting funzionano da soli, e non causano problemi, a patto di co-noscere ci che avviene dietro le scene. In altri casi, necessa-rio rendere i casting espliciti: questo devessere fatto quando ilcasting non ovvio, o quando c rischio di perdita di precisio-ne, ad esempio in una conversione da range maggiore (int) arange minore (char). In questi e in altri casi, il casting pu esse-re imposto mettendo tra parentesi tonde il tipo di dato in cui sivuole convertire il valore.

    //Esempio di casting

    #include

    using namespace std;

    int main() {

    unsigned char a = 140;

    cout

  • Capitolo 2

    I libri di ioPROGRAMMO/Imparare C++40

    IMPARAREC++ Dati ed espressioni

    2.10 ESERCIZI Estendi lapplicazione del calcolatore per la valutazione degli

    operatori bit-a-bit e delle espressioni logiche.

    Il C++ non prevede un operatore logico di tipo xor (qualcosacome ^^). Sapresti indicare un espressione equivalente?

    Suggerimento: Perch scrivere a != b non sufficiente? In un videogioco che ha bisogno di prestazioni elevate viene ri-

    chiesto di verificare se un punto generato casualmente si trova al-le coordinate (x=15; y=20). Come creeresti lespressione di va-lutazione?

    Estensione: preferibile scrivere (x==15 && y==20) oppure(y==20 && x==15)? o indifferente?

    Suggerimento: non del tutto indifferente. Pensa alle risolu-zioni video tipiche, alle prestazioni e alla logica cortocircuitata.

    Scrivi unespressione che, date in ingresso due espressioni a e b,restituisca Maggiore se a maggiore di b, Minore se mi-nore e uguale altrimenti.

    Suggerimento: dovrai usare loperatore ternario pi di una volta.

    Un vecchio trucco dellinformatica lo xor swap, ovvero la pos-sibilit di scambiare il contenuto di due variabili intere senzausare variabili temporanee, secondo il seguente codice:

    a ^= b;

    b ^= a;

    a ^= b;

    Prova a seguire lesecuzione di questalgoritmo su alcuni esem-pi numerici, e a spiegare come funziona.

    capitolo 2 1-02-2006 12:41 Pagina 40

  • I libri di ioPROGRAMMO/Imparare C++ 41

    Controllo del flussoCapitolo 3 IMPARAREC++

    CONTROLLO DEL FLUSSO Nel precedente capitolo abbiamo visto i tipi di dato fondamentali e la lo-ro combinazione in espressioni.Abbiamo anche cominciato a notare co-me una programmazione con un flusso distruzioni lineare non sia unostrumento sufficiente (il caso della divisione per zero), e come lesecuzio-ne di compiti relativamente semplici possa tramutarsi in un numero trop-po elevato di istruzioni (convertire un numero a 64bit in binario, ad esem-pio). Per questi ed altri scopi, i linguaggi di programmazione come il C++prevedono lutilizzo di costrutti che alterino il flusso di esecuzione, facen-dolo tornare indietro, saltare in avanti (in rari casi), e ramificare.Per illustraretutto ci graficamente, faremo uso dei diagrammi di flusso. Quellopresentato nel (diagramma 3.1) rappresenta il flusso lineare che ab-biamo usato finora.

    3.1 COSTRUTTI DI SELEZIONE

    3.1.1 IFIl caso della divisione per zero pu essere risolto eseguendo listru-zione pericolosa solo se il secondo operando diverso da zero. Unastruttura che permette questa operazione prende il nome di costruttodi selezione, e nella sua forma pi semplice (diagramma 3.2) ha laseguente sintassi:

    if (condizione)

    istruzioni

    Diagramma 3.1: Costruttori di selezione.

    capitolo 3 1-02-2006 12:41 Pagina 41

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++42

    IMPARAREC++ Controllo del flusso

    Laddove il termine istruzioni pu indicare una singola istruzione oun gruppo distruzioni (in questo caso, obbligatorio racchiuderlefra parentesi graffe), che saranno eseguite soltanto se lespressionebooleana espressa in condizione vera. Il caso della divisione perzero pu quindi essere risolto facilmente cos:

    if (b) //oppure if(b != 0)

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 43

    Controllo del flussoCapitolo 3 IMPARAREC++

    if (condizione)

    istruzioni

    else

    istruzioni

    Le istruzioni previste nel blocco else saranno eseguite solo nel caso in cuicondizione sia false. Da ci possiamo ricavare il codice che ci serve:

    if (b)

    cout

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++44

    IMPARAREC++ Controllo del flusso

    Gli if, cos come ogni altro tipo di costrutto, possono essere nidifica-ti. , cio, possibile inserire una struttura if allinterno del blocco diistruzioni.Un esempio molto comune nella programmazione lestensionedellif a pi casi alternativi.

    if (condizione1)

    istruzioni

    else if (condizione2)

    istruzioni

    else

    istruzioni

    Come si vede dal (diagramma 3.4), questo significa estendereverso destra indefinitamente il flusso dellesecuzione, fino alle-ventuale else generico.

    3.1.2 SWITCHLuso degli if mostrato nel diagramma 3.4 assume dimensioni in-quietanti quando i casi da considerare sono un numero non banale,perch presume che per ognuno di essi occorra nidificare lesecu-zione: arrivare ad innestare dieci o quindici if luno dentro laltro con-tro ogni principio della buona programmazione. In simili casi, ci sipu avvalere del costrutto switch, che ha la seguente sintassi:

    switch(discriminante) {

    case n1:

    istruzioni

    case n2:

    istruzioni

    default:

    istruzioni

    };

    capitolo 3 1-02-2006 12:41 Pagina 44

  • I libri di ioPROGRAMMO/Imparare C++ 45

    Controllo del flussoCapitolo 3 IMPARAREC++

    laddove discriminante un intero (o unenumerazione vedi para-grafo 4.1.2) che pu assumere uno dei valori previsti nei vari case.Se non esiste alcun caso previsto per il valore di discriminante, il con-trollo passer alle istruzioni previste in default (se questo statoprevisto). Occorre fare attenzione alluso del costrutto switch, co-scienti del fatto che (come mostra il diagramma 3.5) il codice in realtun blocco continuo che non si interrompe alla fine di un case, maprosegue nellesecuzione di tutti quelli che seguono il che spessonon corrisponde al comportamento desiderato. Per questo, solita-mente si aggiunge una direttiva break (vedi paragrafo 3.3.1) alla fi-ne di ogni caso, per uscire dal blocco.

    switch(lati) {

    case 3:

    cout

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++46

    IMPARAREC++ Controllo del flusso

    break;

    }

    Se in questesempio ci fossimo dimenticati di inserire le tre istruzionibreak alla fine di ogni caso, il programma avrebbe stampato il messaggiorelativo alla figura giusta, pi tutti quelli dei casi successivi.

    3.2 COSTRUTTI DITERAZIONEI costrutti di iterazione (o cicli) sono fondamentali in tutti quei casi incui sia necessario ripetere pi volte la stessa sequenza distruzioni. Lecalcolatrici, ad esempio, non terminano la propria esecuzione dopoaver fornito il risultato, ma continuano ad attendere nuovi input.

    3.2.1 WHILELa struttura while permette di ripetere una sequenza distruzioni fin-tantoch una condizione vera. La condizione pu essere verifica-ta prima dellesecuzione (diagramma 3.6), secondo la sintassi:

    while(condizione)

    istruzioni

    oppure dopo (diagramma 3.7) secondo lalternativa:

    Diagramma 3.6: While

    capitolo 3 1-02-2006 12:41 Pagina 46

  • I libri di ioPROGRAMMO/Imparare C++ 47

    Controllo del flussoCapitolo 3 IMPARAREC++

    do

    istruzioni

    while(condizione);

    Anche i costrutti while possono essere nidificati a piacimento, co-me dimostra la seguente variazione (lultima!) della calcolatrice (ve-di diagramma 3.8).

    //Calcolatrice minima (versione finale)

    #include

    using namespace std;

    int main() {

    char risposta;

    Diagramma 3.7: Do..While.

    Diagramma 3.8: Calcolatrice minima.

    capitolo 3 1-02-2006 12:41 Pagina 47

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++48

    IMPARAREC++ Controllo del flusso

    do {

    int a, b;

    //richiesta dei due numeri

    cout > a;

    cout > b;

    //stampa dei risultati

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 49

    Controllo del flussoCapitolo 3 IMPARAREC++

    3.2.2 FOR Il ciclo for senzaltro il costrutto diterazione pi complesso e pi usa-to nella programmazione, e si utilizza in quei casi in cui necessa-rio ripetere lesecuzione di un blocco distruzioni tenendo traccia delnumero di repliche, mediante lincremento del valore di un contato-re o del riferimento di un iteratore. La sintassi :

    for (inizializzazioni; condizione; incrementi)

    istruzioni

    Pu essere utile seguire il diagramma 3.9, per capire esattamente lor-dine in cui vengono eseguite le varie parti del ciclo. Le inizializza-zioni consistono in una dichiarazione/assegnamento (o pi di una, se-parate da virgole), e si usano per porre il contatore ad un valore ini-ziale. Gli incrementi consistono in una istruzione (o pi, separate davirgole), che aumenti il valore del contatore. Possiamo dare un esem-pio di ciclo for, rendendo molto pi compatto il convertitore binariodellesempio 2.7:

    // Scomposizione di un byte mediante ciclo for.

    #include

    using namespace std;

    Diagramma 3.9: For.

    capitolo 3 1-02-2006 12:41 Pagina 49

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++50

    IMPARAREC++ Controllo del flusso

    int main() {

    cout > dato;

    //converte in byte

    char byte = (char)dato; // casting esplicito

    cout =0; i--)

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 51

    Controllo del flussoCapitolo 3 IMPARAREC++

    3.3.1 BREAKLa parola chiave break pu essere usata solo allinterno di un bloc-co (if, switch, while, for), per forzarne luscita. Le istruzioni successi-ve alla chiamata non verranno eseguite. Ad esempio:

    for(int i=0; i

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++52

    IMPARAREC++ Controllo del flusso

    fatti, listruzione continue interromper literazione, riprendendo il ci-clo da i=7.

    3.3.3 GOTOListruzione goto viene usata per eseguire un salto incondizionato al-linterno della funzione corrente, verso una riga che sia stata con-trassegnata da una determinata etichetta. Unetichetta viene specificataanteponendo ad unistruzione un identificativo seguito dai due pun-ti (come nei case del costrutto switch, il quale infatti usa delle etichetteed ha un funzionamento intimamente connesso al goto).Ecco un semplice esempio delluso di goto:

    #include

    using namespace std

    int main() {

    goto uscita;

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 53

    Controllo del flussoCapitolo 3 IMPARAREC++

    sanno quando infrangere il tab: ad esempio per uscire dai cicli piesterni senza dover usare delle scomode variabili temporanee.

    3.4 VISIBILITI costrutti if, while e for prevedono nella loro sintassi la dicitura istru-zioni. Questa permette, come abbiamo gi visto, due possibilit:

    Una sola istruzione: Ad esempio:

    if (numeroLati == 4)

    area = lato * lato;

    Un blocco che racchiuda pi istruzioni. Ad esempio:

    if (numeroLati == 4) {

    area = lato * lato;

    perimetro = 4 * lato;

    }

    Un blocco, quindi, si caratterizza per la presenza delle parentesi graf-fe. Qui analizzeremo il fatto che le variabili dichiarate allinterno diun blocco godono di una visibilit minore rispetto a quelle esterne.Per capire il concetto della visibilit (o scope), occorre capire come sisvolge il ciclo di vita delle variabili: nel momento in cui una variabi-le viene dichiarata, viene allocato lo spazio necessario nello stack, ela sua locazione di memoria diventa accessibile. In seguito tale va-riabile pu essere inizializzata, assegnata e ridefinita. Infine, non ap-pena questa esce dal raggio di visibilit, viene distrutta. La visibilitdi una variabile si esaurisce alluscita dal blocco in cui questa si tro-va. In questo esempio:

    int main() {

    capitolo 3 1-02-2006 12:41 Pagina 53

  • Capitolo 3

    I libri di ioPROGRAMMO/Imparare C++54

    IMPARAREC++ Controllo del flusso

    int n = 0;

    while(n < 20) {

    int i=1;

    n += i*2;

    }

    n = i; // errore! i non pi visibile

    return 0;

    }

    la variabile n diventa visibile allinizio della funzione, e scompare al-la fine, pertanto pu essere usata allinterno del ciclo while senzaproblemi. Lerrore, invece, riguarda luso della variabile i, che viene di-chiarata allinterno del ciclo while, e pertanto perde visibilit nonappena il ciclo finisce. Questo spiega perch il compilatore segnalala riga n = i con un errore di tipo: variabile non dichiarata. In unblocco si pu anche dichiarare una variabile gi esistente allester-no. In tal caso il riferimento a tale variabile sar ridefinito, e non sarpi possibile fare riferimento alla variabile esterna fino alla fine delblocco:

    #include

    using namespace std

    int main() {

    int n = 0;

    { //blocco interno

    int n = 2;

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 55

    Controllo del flussoCapitolo 3 IMPARAREC++

    cout

  • capitolo 3 1-02-2006 12:41 Pagina 56

  • I libri di ioPROGRAMMO/Imparare C++ 57

    Tipi avanzatiCapitolo 4 IMPARAREC++

    TIPI AVANZATIIn questo capitolo vedremo come manipolare i dati fondamentali, percreare dei tipi di dato pi complessi (strutture), per creare sequen-ze di pi elementi (array), e vedremo come questi dati vengano al-locati in memoria.Alla fine di questo capitolo saremo quindi in gra-do di gestire tutte le strutture che sono comunemente adoperatenella programmazione di stampo procedurale. Se sei un novizio del-la programmazione, o non hai comunque un background solido sustrutture ed algoritmi di base (liste collegate, alberi binari, ordinamento,etc) ti consiglio fortemente la lettura di [5] o equivalenti.

    4.1 COSTANTI, ENUMERAZIONI E TYPEDEFIl C++ offre degli strumenti per migliorare la leggibilit e la manu-tenibilit del codice, in quei casi in cui alcuni valori siano noti indi-pendentemente dallesecuzione: le costanti e le enumerazioni. Per sem-plificare la ridefinizione dei tipi, invece, il C++ permette di usare laparola chiave typedef.

    4.1.1 COSTANTIAlcuni valori non cambiano mai durante il corso dellesecuzione, tal-volta perch sono delle costanti per loro natura (ad esempio, pi gre-co), altre volte perch il programmatore non vuole che il loro valo-re possa cambiare in seguito allinizializzazione. A tal fine, si puanteporre ad una dichiarazione la parola chiave const, che indicauna limitazione di tipo per un valore, che rimarr costante per tuttolarco di visibilit.

    int main() {

    const int PI = 3.14;

    double angoloRetto = PI/2;

    capitolo 4 1-02-2006 14:37 Pagina 57

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++58

    IMPARAREC++ Tipi avanzati

    double angoloGiro = PI*2;

    return 0;

    }

    Cos facendo si ottiene una scrittura molto pi manutenibile e leg-gibile per il programmatore, senza introdurre alcun overhead: uncompilatore appena sopra la soglia della decenza, infatti, sar comunquein grado di risolvere la costante per sostituzione, rendendo cos que-sta scrittura identica a quella ottenuta utilizzando direttamente ilnumero.

    4.1.2 ENUMERAZIONII programmatori C++ spesso fanno pi uso delle enumerazioni, chedelle costanti, in tutti quei casi in cui un tipo di variabile pu assu-mere soltanto degli stati precisi. Un esempio di enumerazione :

    enum LuceSemaforo {

    ROSSO,

    GIALLO,

    VERDE

    }

    e la sintassi esatta di enum:

    enum nome {

    STATO1 = valore,

    STATO2 = valore,

    };

    Laddove = valore opzionale, e indica la possibilit di stabilireun valore intero per lo stato. Se tale inizializzazione non viene fatta,

    capitolo 4 1-02-2006 14:37 Pagina 58

  • si user un ordine crescente (partendo da zero, o dal primo valore in-serito). Unenumerazione si usa come un intero qualsiasi, ed quin-di particolarmente utile per rendere pi leggibili gli switch.Ad esem-pio, avendo una variabile luce di tipo LuceSemaforo, possiamo scri-vere:

    switch(luce) {

    case ROSSO:

    fermati();

    break;

    case GIALLO;

    preparati();

    break;

    case VERDE:

    vai();

    break;

    }

    4.1.3 TYPEDEFTypedef permette un meccanismo simile a quello delle costanti, maagisce sui tipi, anzich sui valori. Quando un tipo di dato si usa mol-to, possibile ridefinirlo al fine di migliorare la leggibilit del codi-ce: un operazione che, comunque, viene realizzata solo dai pi esper-ti, quando vogliono usare i tipi in maniera pi coerente col fra-mework che stanno adoperando (o costruendo):Nella programmazione windows (vedi il file header windef.h), adesempio, vengono definiti molti typedef simili a questo:

    typedef unsigned long DWORD;

    Questo permette al programmatore una dichiarazione di questo ti-po:

    I libri di ioPROGRAMMO/Imparare C++ 59

    Tipi avanzatiCapitolo 4 IMPARAREC++

    capitolo 4 1-02-2006 14:37 Pagina 59

  • DWORD numero;

    che sar del tutto equivalente a scrivere:

    unsigned long numero;

    Se sei un novizio nel C++, probabilmente non avrai nessun biso-gno di usare i typedef; ma se, invece, fai largo uso di tipi comples-si, il typedef pu essere una grande comodit.

    4.2 STRUTTURE E UNIONSEnumerazioni e typedef permettono al programmatore di inventa-re nuovi tipi di dato a partire dai primitivi. La libert concessa, per, molto poca: le enumerazioni saranno sempre interi, e i typedefnon possono in alcun modo essere considerate delle aggiunte, ben-s dei sinonimi.

    4.2.1 STRUTTURE Una maniera semplice per creare tipi di dati nuovi fornita dallaparola chiave struct, che permette di definire un tipo composto dal-linsieme di pi variabili, dette campi o membri. La sintassi :

    struct nome {

    tipo1 campo1;

    tipo2 campo2;

    };

    ed ecco un esempio di dichiarazione:

    struct Frazione {

    int numeratore;

    Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++60

    IMPARAREC++ Tipi avanzati

    capitolo 4 1-02-2006 14:37 Pagina 60

  • int denominatore;

    };

    Una volta che una struttura stata definita, possibile trattarlaesattamente come un tipo di dati primitivo. Per accedere ai campi siusa loperatore punto (.).

    int main() {

    struct Frazione {

    int numeratore;

    int denominatore;

    };

    //creiamo una nuova f

    Frazione f;

    //poniamo f a 3/5.

    f.numeratore = 3;

    f.denominatore = 5;

    return 0;

    }

    4.2.2 UNIONIStrette parenti delle strutture sono le unioni, definite mediante laparola chiave union. Lo scopo delle unioni quello di creare varia-bili capaci di assumere tipi diversi, cercando di consumare menomemoria possibile.Ci viene realizzato assegnando i diversi campi costituenti lunioneallo stesso indirizzo di memoria. Qui di seguito viene definito unesempio semplificato del comportamento delle variabili di tipo Va-riant che vengono adottate da vari linguaggi di programmazionenellautomazione OLE.

    I libri di ioPROGRAMMO/Imparare C++ 61

    Tipi avanzatiCapitolo 4 IMPARAREC++

    capitolo 4 1-02-2006 14:37 Pagina 61

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++62

    IMPARAREC++ Tipi avanzati

    union Variant {

    int valoreIntero;

    char valoreChar;

    double valoreDouble;

    };

    Le union sono una forma avanzata di ottimizzazione e non dovreb-bero mai essere usate alla leggera (il mio spassionato consiglio dievitarle), facendovi ricorso solo quando si ha una precisa necessit(ad esempio, nel costruire un ambiente run-time per un linguaggiodi scripting), e un effettivo bisogno di ridurre limpiego di memoria.

    4.3 VARIABILI E MEMORIA STATICANel momento in cui una variabile viene dichiarata localmente, co-me ad esempio in:

    int main()

    {

    int i=0;

    return 0;

    }

    il calcolatore le riserva lo spazio richiesto dal tipo (in questo caso,supponiamo 4 byte) allinterno di un blocco di memoria chiamatostack. Da quel momento in poi, i sar associato a quel particolareindirizzo Occorre quindi fare una precisa distinzione fra indirizzo e va-lore. Nel seguente codice:

    int main()

    {

    int i=13, j=13;

    capitolo 4 1-02-2006 14:37 Pagina 62

  • I libri di ioPROGRAMMO/Imparare C++ 63

    Tipi avanzatiCapitolo 4 IMPARAREC++

    return 0;

    }

    i e j hanno lo stesso valore, ma diverso indirizzo. Questo pu essereverificato per mezzo delloperatore &, che restituisce lindirizzo diuna variabile. Ad esempio:

    #include

    int main()

    {

    int i = 0xAABBCCDD; // in decimale, 2864434397

    int j = 0x11223344; // in decimale, 287454020

    std::cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++64

    IMPARAREC++ Tipi avanzati

    4.4 PUNTATORILa nostra recente scoperta che le variabili non sono altro che no-mi associati ad un indirizzo, ci porta a poter comprendere uno deglistrumenti pi utili e insidiosi a nostra disposizione, croce e deliziadei programmatori C++: i puntatori.Un puntatore una variabile che memorizza un indirizzo e permet-te di leggere il valore relativo.

    4.4.1 COME SI DICHIARANO I PUNTATORII puntatori devono essere tipizzati (vedremo presto la ragione), per-tanto la loro dichiarazione si effettua dichiarando il tipo della varia-bile da puntare, seguito da un asterisco. Ad esempio con:

    char* p;

    Dichiariamo la variabile p come puntatore a una variabile char. Inseguito possiamo associare tale puntatore ad un indirizzo specifico:

    int main()

    {

    char* p;

    char x;

    p = &x; // giusto

    p = x; // sbagliato ("x" indica il valore, non l'indirizzo)

    p = 0x22FF74; // sbagliato (non un char!)

    p = (char*)0x22F74; // giusto, ma probabilmente insensato

    return 0;

    }

    Come si evince dallesempio, la via pi corretta per usare un punta-

    capitolo 4 1-02-2006 14:37 Pagina 64

  • I libri di ioPROGRAMMO/Imparare C++ 65

    Tipi avanzatiCapitolo 4 IMPARAREC++

    tore associarlo allindirizzo di una variabile del suo stesso tipo. Congli opportuni cast (vedi esempio in 4.4.4), anche possibile asso-ciare un puntatore ad una variabile di tipo diverso o ad un qualsia-si indirizzo, ma questo ha solitamente poco senso (se non per quel-le rare applicazioni che conoscono in anticipo lubicazione di unacerta variabile in memoria). Quando un puntatore non punta anco-ra da nessuna parte, la situazione pericolosa, perch se il pro-gramma lo utilizzasse, farebbe riferimento ad una zona di memorianon conosciuta, con esiti drammatici. Per questo un puntatore dovrebbesempre essere inizializzato su un riferimento valido, oppure sul va-lore 0. In questo modo si potr verificare facilmente se il puntatore valido o meno:

    #include

    int main()

    {

    char* p = 0; //puntatore messo "a terra"

    if (p) //se il puntatore valido

    std::cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++66

    IMPARAREC++ Tipi avanzati

    #include

    int main()

    {

    int* p;

    int x;

    p = &x; // ora p punta a x

    *p = 10; // cambio il valore della variabile puntata in 10

    std::cout

  • I libri di ioPROGRAMMO/Imparare C++ 67

    Tipi avanzatiCapitolo 4 IMPARAREC++

    membro di una variabile referenziata da un puntatore: la freccia (->).Lesempio pu cos essere riscritto:

    Frazione elemento;

    Frazione* puntatore;

    puntatore->numeratore = 5

    puntatore->denominatore = 10

    Le due scritture sono assolutamente equivalenti, quindi troveretedifficilmente un programmatore C++ tanto perverso da complicar-si la vita con uno stile simile al primo esempio.

    4.4.4 ARITMETICA DEI PUNTATORILaritmetica dei puntatori la ragione fondamentale per la qualequeste variabili sono tipizzate. Operazioni comuni sono lincremen-to e il decremento di un puntatore per permettere di accedere alle cel-le contigue, o la sottrazione di due puntatori per misurarne la di-stanza relativa. Azioni che assumeranno un senso molto pi com-piuto solo quando parleremo degli array; per ora puoi comunque os-servare questo codice:

    #include

    using namespace std;

    int main()

    {

    int i = 0xAABBCCDD;

    int j = 0x11223344;

    cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++68

    IMPARAREC++ Tipi avanzati

    unsigned char *p = (unsigned char*)&j;

    //ciclo per otto elementi della memoria

    for (int a = 0; a < 8; a++, p++)

    cout

  • I libri di ioPROGRAMMO/Imparare C++ 69

    Tipi avanzatiCapitolo 4 IMPARAREC++

    4.4.5 PUNTATORI A PUNTATORIUn puntatore pu anche puntare a un altro puntatore! Il C++gestisce la cosa in maniera logica e coerente, trattando il puntatorecome un valore qualsiasi:

    int n = 0, m = 1;

    int* pn = n; //pn punta ad n

    int** ppn = pn; //ppn punta a pn che punta ad n

    int*** pppn = ppn; //etc

    Se ti stai (giustamente) chiedendo perch mai bisognerebbe punta-re ad un altro puntatore, sappi che non si tratta di un simpatico gio-co di societ. Simili strutture vengono impiegate molto spesso ve-di paragrafo 4.6.5.

    4.4.6 A COSA SERVONO I PUNTATORI?Ho dei vaghi ricordi dei miei inizi sulla via del C, ma uno molto for-te: continuavo a chiedermi a cosa diavolo servissero i puntatori. In findei conti si vive anche senza! Ovviamente, non vero: luso dei pun-tatori diventa sempre pi una necessit, via via che la conoscenza pro-gredisce, e con essa le esigenze da soddisfare.Alcuni degli usi pi co-muni dei puntatori comprendono:

    Accesso rapido ad un elemento di una variabile o di una colle-zione.

    Passaggio di valori ad una funzione per riferimento. Aritmetica dei puntatori associata alla navigazione negli array. Creazione di alberi e liste collegate. Gestione della memoria dinamica.

    Poich facile ridursi a puntare a strutture non pi esistenti (danglingpointer), o dimenticarsi di deallocare le strutture che abbiamo crea-

    capitolo 4 1-02-2006 14:37 Pagina 69

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++70

    IMPARAREC++ Tipi avanzati

    to (memory leak), causando cos il crash dellapplicazione, il C++ sid da fare per minimizzarne luso dei puntatori, mediante lintrodu-zione dei riferimenti (references) e della libreria standard, che solle-va il programmatore dal dover gestire il pericoloso dietro le quintedi molte strutture comunemente usate.

    4.5 RIFERIMENTII riferimenti (o references) sono simili ai puntatori, ad eccezione delfatto che usandoli non si possono pi eseguire le operazioni arit-metiche sugli indirizzi per fare i consueti giochetti interessanti mapericolosi che questi ultimi permettono.

    4.5.1 COME SI DICHIARANO LE REFERENCESI riferimenti si dichiarano ponendo il simbolo & (che in questo casonon ha nessuna attinenza con loperatore indirizzo) al tipo di dato,ed obbligatorio inizializzarli subito su una costante o una variabi-le:

    #include

    int main()

    {

    int valore = 10;

    int* puntatore = &valore;

    int& riferimento = valore;

    puntatore++; //il puntatore punta alla cella successiva

    riferimento++; //valore ora vale 11

    std::cout

  • I libri di ioPROGRAMMO/Imparare C++ 71

    Tipi avanzatiCapitolo 4 IMPARAREC++

    return 0;

    }

    Valore e 11

    Lesempio mostra come si dichiara un riferimento ad una variabile (stes-sa cosa il riferimento ad una costante), e la differenza fondamen-tale con i puntatori: laritmetica usata dal puntatore relativa al-lindirizzo, quella usata dal riferimento associata al valore. Si pua tutti gli effetti considerare un riferimento come un altro nome perla variabile referenziata.

    4.5.2 ACCESSO AI MEMBRI DI UNA STRUTTURAPoich non c differenza fra usare la vera variabile o un suo riferi-mento, laccesso ai membri di una struttura associata ad un riferimentosi realizza sempre per mezzo delloperatore punto.

    int main()

    {

    Frazione f;

    Frazione& ref = f;

    f.numeratore = 1;

    ref.denominatore = 3;

    std::cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++72

    IMPARAREC++ Tipi avanzati

    a quello dei puntatori: non bisogna ricordarsi se il caso di usareloperatore freccia o il punto.

    4.5.3 RIFERIMENTO AD UNA VARIABILE PUNTATACapita spesso di dover fare riferimento ad una variabile puntata: intal caso va usato loperatore di dereferenziazione:

    int x = 5;

    int* ptrx = x;

    int& refx = *ptrx;

    In questesempio, refx fa riferimento alla variabile x; se non avessi-mo fatto uso dellasterisco, avrebbe puntato al puntatore, e non al-la variabile. Loperatore * va usato per ogni puntatore, compresi thise quelli restituiti da new.

    int& refint = *new int;

    //... uso il riferimento ...

    delete &refint;

    4.5.4 PERCH SI USANO LE REFERENCESMolti programmatori C++ di formazione C ignorano completamen-te lesistenza delle references, e le adoperano solo quando sono co-stretti a farlo (per alcune operazioni di overloading degli operatori incui non se ne pu fare a meno).Quando si presentano i requisiti di costanza richiesti dalle references,invece, il loro uso preferibile rispetto a quello dei puntatori, perchlapproccio ai membri pi coerente, pi chiaro e meno ambiguo ri-spetto a quello dei puntatori.Per questo motivo, lo standard C++ invita ad usare sempre usare lereferences (e non i puntatori) per indicare un parametro di una fun-zione passato per riferimento.

    capitolo 4 1-02-2006 14:37 Pagina 72

  • I libri di ioPROGRAMMO/Imparare C++ 73

    Tipi avanzatiCapitolo 4 IMPARAREC++

    4.6 VETTORI E MATRICICapita spesso di dover considerare un insieme di pi elementi, indi-cizzati o meno. Gli esempi sono infiniti: i giocatori di una squadra dicalcio, i contatti di una rubrica, e cos via. I vettori (o _array_) e le ma-trici (vettori multidimensionali) sono la via pi rapida ed efficace cheil C++ mette a disposizione dei suoi utenti. La conoscenza di que-sti strumenti nei loro diversi risvolti un fondamento imprescindibi-le della formazione di un programmatore.

    4.6.1 LE STRINGHE COME ARRAYPer capire come funziona un vettore, possiamo vedere come le strin-ghe vengono trattate comunemente in C. Cominciamo con un codi-ce che inizializzi e memorizzi una stringa.

    #include

    int main()

    {

    char stringa[5] = "Ciao";

    std::cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++74

    IMPARAREC++ Tipi avanzati

    Dalla figura capiamo che il C++ vede le stringhe come sequenze dibytes terminate dal carattere \0, detto carattere nullo o terminato-re (byte 0). Con le nostre conoscenze in fatto di puntatori non ci sa-rebbe difficile associarne uno allindirizzo di stringa, e puntare le cel-le ad una ad una. Tuttavia il C++ ci offre una via molto pi sempli-ce: gli array.

    4.6.2 DICHIARAZIONE DI UN ARRAYCome abbiamo visto nel codice desempio, la stringa stata dichia-rata come:

    char stringa[5] = "Ciao";

    La sintassi di dichiarazione di un array, infatti, :

    tipo nome[numeroElementi];

    Il tutto in accordo con la (figura 4.2), in cui risulta evidente che lastringa ciao composta da cinque elementi. In questo caso, holasciato il numero 5 solo per semplicit ma il compilatore abba-stanza furbo da saper fare un paio di conti banali, per cui ammes-sa anche la forma:

    char stringa[] = "Ciao";

    la quale assolutamente equivalente. Linizializzazione di unarray puessere effettuata anche indicando i singoli elementi fra parentesigraffe:

    char stringa[] = {C, i, a, o};

    Nel caso dei char questo tipo di dichiarazione unevidente com-plicazione (le stringhe letterali sono fatte apposta per questi casi).Tut-

    capitolo 4 1-02-2006 14:37 Pagina 74

  • I libri di ioPROGRAMMO/Imparare C++ 75

    Tipi avanzatiCapitolo 4 IMPARAREC++

    tavia, per ogni altro tipo di dato, le parentesi graffe rimangono lasoluzione pi semplice, ad esempio:

    int numeri[] = {125, 10002, 40, -20, 52};

    4.6.3 ACCESSO AGLI ELEMENTI DI UN ARRAYDal momento della dichiarazione possibile far riferimento ad unodegli elementi dellarray usando loperatore [].

    #include

    using namespace std;

    int main()

    {

    char stringa[5] = "Ciao";

    cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++76

    IMPARAREC++ Tipi avanzati

    grammatori C++ sviluppa strani comportamenti nella vita di tutti igiorni, quando viene posta di fronte ai numeri ordinali (Svolti allastrada[3] a sinistra. Ovverosia alla quarta. Logico, no?). Se proseguiraidiligentemente nella via del C++, ti unirai presto a far parte di que-stelite sociopatologica. Fino a quel giorno, fa molta attenzione al-la numerazione degli elementi degli array cui fai riferimento.

    4.6.4 GLI ARRAY COME PUNTATORISicuramente non ti sar sfuggita la strettissima connessione che le-ga i puntatori agli array. Anzi, a dirla tutta: un array un puntatoreal primo elemento (cio, allelemento zero) in esso contenuto. Puoirendertene conto facilmente, attraverso un programma del genere:

    #include

    int main()

    {

    int array[5];

    //array punta al suo primo elemento?

    if (array == &array[0])

    std::cout

  • I libri di ioPROGRAMMO/Imparare C++ 77

    Tipi avanzatiCapitolo 4 IMPARAREC++

    Per esempio, ora sappiamo come referenziare una stringa gi allocata,pertanto possiamo ritoccare lesempio precedente in questo modo:

    #include

    int main()

    {

    int array[5];

    char* responso; //responso una stringa! Al momento inva-lida.

    //array punta al suo primo elemento?

    responso = ((array == &array[0]) ? "uguali" : "diversi");

    //ora responso punta ad "uguali" oppure a "diversi"

    std::cout

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++78

    IMPARAREC++ Tipi avanzati

    int main()

    {

    enum Pezzo {

    NESSUNO,

    PEDONE,

    CAVALLO,

    ALFIERE,

    TORRE,

    RE,

    REGINA

    };

    Pezzo scacchiera[8][8];

    scacchiera[0][0] = TORRE;

    scacchiera[0][1] = CAVALLO;

    //...

    return 0;

    }

    Poich un array un puntatore, ne consegue che un array di array un puntatore a puntatore. Pertanto un puntatore alla variabile scac-chiera devessere dichiarato come:

    Pezzo scacchiera[8][8];

    Pezzo** ptrScacchiera = scacchiera;

    4.6.6 I PROBLEMI DEGLI ARRAY.Non mi pare ci possano essere dubbi sul perch gli array siano fon-damentali: liste e tabelle sono allordine del giorno in qualunqueprogramma vada un gradino al di l della banalit pi assoluta.Allinizio del capitolo ho lodato lefficienza di queste strutture in ter-mini di memoria e prestazioni, qui invece parler delle varie e fondatissime

    capitolo 4 1-02-2006 14:37 Pagina 78

  • I libri di ioPROGRAMMO/Imparare C++ 79

    Tipi avanzatiCapitolo 4 IMPARAREC++

    ragioni per cui non si dovrebbero usare gli array, se non in casi diforza maggiore. Detto in maniera concisa: sono inflessibili, incom-pleti, scomodi e pericolosi. Gli array sono inflessibili, perch, una vol-ta dichiarata la loro dimensione (in maniera statica o dinamica), nonc alcuna maniera di allargarli o restringerli, il che significa la mag-gior parte delle volte dover tirare a indovinare la dimensione massi-ma degli elementi, offrendo cos il fianco a quellorda di cracker chenon vedono lora di provocare un bug di buffer overflow nella nostraapplicazione. La prassi di usare la dimensione degli array come di-mensione massima possibile evidenzia in maniera dolorosa il fattoche queste strutture sono incomplete perch non c alcun modo disapere la dimensione totale degli elementi che ne fanno parte. Tut-to ci porta alla scomodit di dover usare quasi sempre una costanteper memorizzare i valori massimi, e di una variabile per tenere il con-to degli elementi totali gestibili nellarray, o far ricorso a soluzionialternative poco eleganti e poco robuste, come il carattere termina-tore usato nelle stringhe. Infine, gli array sono pericolosi perch unaccesso ad un loro elemento in realt un gioco di aritmetica dei pun-tatori: lOperazione Rischiosa per antonomasia. Un semplice esem-pio: cosa succede in questo caso?

    int main() {

    int valori[5] = {1,2,3,4,5};

    int a = valori[5];

    return 0;

    }

    In questo codice un programmatore non ancora avvezzo al fatto chegli elementi iniziano dallo zero (vedi 4.6.3), volendo richiamare ilquinto elemento dellarray valori, ha in realt richiesto il sesto, scon-finando cos in una zona di memoria esterna e sconosciuta. Loperazionenon causa errori di compilazione, ma al momento dellesecuzione

    capitolo 4 1-02-2006 14:37 Pagina 79

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++80

    IMPARAREC++ Tipi avanzati

    lapplicazione andr in crash nel migliore dei casi; nel peggiore, in-vece, potrebbe continuare a funzionare con un valore di a completamentesballato, causando potenzialmente danni ancor maggiori e pi dif-ficili da rilevare.Tutti questi problemi hanno afflitto da sempre i pro-grammatori C.Per questo ti suggerisco di usare i vettori solo quando lavori in un am-bito noto e locale (cio in cui il tutto si risolve in poche righe e nonsi trascina per lintera applicazione), e di fare affidamento, per tuttigli altri casi, sui contenitori pi gestibili forniti dalla libreria standard(ad esempio Vector). Usare il C++ piacevole e comodo anche perquesto. Il discorso vale ancor di pi per le stringhe stile C, che abbiamoscoperto essere degli array in maschera: sono pericolose, scomode,inflessibili, eccetera. Usare la classe string della libreria standard, in-vece delle centinaia di funzioni criptiche previste dalla libreria C, unodei pi grandi sollievi che possano essere concessi ad un program-matore dai tempi dellinvenzione della pausa caff.

    4.7 MEMORIA DINAMICACi sono casi in cui si vuole creare una variabile, ma non si ha unideaprecisa sul quando la si vuole deallocare, o si vuole che perduri al dil del raggio di visibilit.

    4.7.1 CREAZIONE DINAMICA DI UNA VARIABILEIn questo caso possibile creare la variabile in maniera dinamica,facendo uso delloperatore new, la cui sintassi :

    new tipo;

    e che restituisce un puntatore alloggetto creato.main()

    {

    capitolo 4 1-02-2006 14:37 Pagina 80

  • I libri di ioPROGRAMMO/Imparare C++ 81

    Tipi avanzatiCapitolo 4 IMPARAREC++

    int* x = new int;

    //usiamo x come ci pare

    delete x;

    return 0;

    }

    Il codice mostra il tipico ciclo di vita di una variabile dichiarata di-namicamente:

    Mediante luso delloperatore new, la variabile viene creata in unospazio di memoria diverso dallo stack, chiamato heap

    Il puntatore restituito da new viene utilizzato finch si vuole

    Prima della fine del programma, viene richiamato loperatoredelete sul puntatore restituito da new, cosicch la memoria oc-cupata dalla variabile viene liberata.

    4.7.2 I PROBLEMI DELLA DICHIARAZIONE DINAMICARispettare il ciclo di vita della variabile creata dinamicamente fon-damentale se si vogliono evitare alcuni dei bug pi insidiosi che il C++permetta di introdurre. Se, ad esempio, la variabile viene eliminata pri-ma del tempo, e si fa riferimento ad essa quando gi stata can-cellata dalloperatore delete, si ottiene quello che viene chiamato ingergo un dangling pointer.

    #include

    int main()

    {

    capitolo 4 1-02-2006 14:37 Pagina 81

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++82

    IMPARAREC++ Tipi avanzati

    int* x = new int;

    *x = 15;

    delete x;

    std::cout

  • I libri di ioPROGRAMMO/Imparare C++ 83

    Tipi avanzatiCapitolo 4 IMPARAREC++

    ray fuori dagli indici. Ma, nonostante i preziosi strumenti offerti daivari debugger e dai tuner, gli errori di dangling pointers e memory leaksono sempre in agguato e difficili da gestire. Linsidia di questo tipodi eccezioni che una falla pu essere notata solo dopo un accumulopiuttosto ingente, e non c modo di stabilire con precisione da quan-to un puntatore sia a zonzo. Lunico modo di prevenire questi er-rori fare molta, molta attenzione quando si allocano e deallocanovariabili. Lalternativa pi semplice, ma anche pi dispendiosa in ter-mini di prestazioni, dotare il proprio C++ di un garbage collector(raccoglitore di spazzatura), che si occupi del lavoro sporco per noi[6].

    4.7.3 DICHIARAZIONE DINAMICA DI ARRAYSNegli array dichiarati staticamente, la dimensione fornita nella di-chiarazione deve essere una costante. Ovverosia, non possibile fa-re qualcosa del genere:

    #include

    using namespace std;

    int main()

    {

    int max = 0;

    cout > max;

    int valori[max]; //errore: max non costante!

    return 0;

    }

    La dichiarazione dinamica, invece, permette una simile operazione,grazie alloperatore new[], che crea un nuovo array sullo heap, e al

    capitolo 4 1-02-2006 14:37 Pagina 83

  • Capitolo 4

    I libri di ioPROGRAMMO/Imparare C++84

    IMPARAREC++ Tipi avanzati

    suo corrispettivo delete[] che lo rimuove.

    #include

    using namespace std;

    int main()

    {

    int max = 0;

    for (int i=0; i max;

    int* valori = new int[max]; //bene.

    delete[] valori;

    }

    return 0;

    }

    Come si vede dallesempio, questo non solo permette di dichiararearray della dimensione desiderata, ma anche di sfruttare il ciclo al-locazione/deallocazione per cambiarne la dimensione.

    capitolo 4 1-02-2006 14:37 Pagina 84

  • I libri di ioPROGRAMMO/Imparare C++ 85

    Paradigma proceduraleCapitolo 5 IMPARAREC++

    PARADIGMA PROCEDURALEFinora i nostri sforzi si sono concentrati sui fondamenti del lin-guaggio, per quanto riguarda i dati e la gestione del flusso. Ora nesappiamo abbastanza per guardare oltre: tutti i nostri esempi nonsono mai usciti dallinterno della funzione main, ma, dai tempi deltramonto dei vecchi sistemi monolitici, questo improponibile perogni progetto che superi la complessit dell hello world.Larchitettura di tipo procedurale ha come scopo quello di spez-zare il codice in blocchi distinti, possibilmente riutilizzabili, che as-solvano ciascuno una funzione specifica, e contribuiscano a sor-reggere il peso dellapplicazione. In questo capitolo vedremo comesi scrivono e dichiarano le funzioni, come si organizza un progettosu pi files, secondo questo stile di programmazione.

    5.1 LALTRA FACCIA DI MAINColgo loccasione di questinizio capitolo per svelarti un segreto cheh