Sei sulla pagina 1di 55

Le Basi della Programmazione

Linguaggio C++ Le basi

Le Basi della Programmazione

1. Linguaggio C++, la storia

Nel 1972 Dennis Ritchie progettava e realizzava, presso i Bell Laboratories, la prima versione del linguaggio C. Ritchie aveva ripreso e sviluppato molti dei costrutti sintattici utilizzati nella costruzione del sistema operativo UNIX da Ken Thompson. Successivamente gli stessi Thompson e Ritchie riscrissero in C il codice di UNIX. Da allora il C ha subito pochissime trasformazioni, mantenendosi un linguaggio di alto livello che possiede un ristretto insieme di costrutti e di parole chiave, ma con una straordinaria forza espressiva. Il C consente di programmare in maniera modulare, utilizzando macro e funzioni, di interagire direttamente con funzioni tipiche del basso livello come ad esempio lindirizzamento assoluto di memoria. Per leleganza della sua sintassi e la compattezza dei costrutti, il C una sfida permanente alle capacit intellettuali del programmatore. Quando nella prima met degli anni Ottanta, nella teoria della programmazione si sviluppano le basi per la OOP (Object Oriented Programming, programmazione orientata agli oggetti) si capisce presto che quella sar la chiave di volta per lo sviluppo di applicazioni general-purpose. Ecco allora che il danese Bjarne Stroustrup propone nel 1983 un nuovo linguaggio denominato C con classi e successivamente C++. Mantenendo una compatibilit quasi assoluta con il C, il C++ un linguaggio Object Oriented che diventer lo standard de facto per la programmazione di applicativi nel ventennio a seguire. Il C++ rappresenta un linguaggio completamente autonomo rispetto al C, pur utilizzandone sostanzialmente la sintassi. In particolare, lintroduzione di costrutti quali i template e le classi rende il C++ un linguaggio multi paradigma, principalmente quello a oggetti. Ci che ha sempre differenziato il C e il C++ da altri linguaggi artificiali il fatto di essere stati creati da dei programmatori che dovevano fronteggiare particolari esigenze quali la codifica dei programmi e lorganizzazione del codice e non da un gruppo di ricerca appositamente creato allo scopo. Questo ha portato un sacco di programmatori a inviare commenti, suggerimenti e migliorie a Ritchie e Stroustrup nei primi anni dello sviluppo dei due linguaggi ottenendo un prodotto sicuramente pi malleabile e adatto alle necessit.

Le Basi della Programmazione

I paradigmi di programmazione
Quando si utilizzavano sistemi operativi a linea di comando, come UNIX e il DOS, lunica programmazione possibile era basata sul paradigma imperativo. Il paradigma imperativo (da imperio, comando) fondato sullesplicita richiesta di eseguire un comando. In tale contesto il programmatore dovr privilegiare lesame del processo di calcolo in quanto precisa sequenza di azioni da svolgere. Le strutture di controllo assumono la forma di istruzioni di flusso (GOTO, FOR, IF/THEN/ELSE, ecc.) e il calcolo procede per iterazione piuttosto che per ricorsione. I valori delle variabili sono spesso assegnati a partire da costanti o da altre variabili (assegnamento) e raramente per passaggio di parametri (istanziazione). La programmazione strutturata una tecnica il cui scopo di limitare la complessit della struttura del controllo dei programmi. Il programmatore vincolato ad usare solo le strutture di controllo canoniche definite dal Teorema di Bohm-Jacopini, ovvero la sequenza, la selezione e il ciclo, evitando le istruzioni di salto incondizionato. La programmazione orientata agli oggetti (OOP) si dimostrata la pi adeguata nello sviluppo di sistemi software complessi. In questo paradigma il concetto principale quello di oggetto in quanto metafora di un concetto del mondo reale. In tale contesto il programmatore interpreta lo svolgersi delle azioni come una sequenza di interazioni fra oggetti, quindi come un flusso (pi) semplice da interpretare e sviluppare. La programmazione guidata dagli eventi (EDP, event driven programming) perde la sequenza logica precisa descritta dai precedenti paradigmi, ma il flusso delle azioni viene guidato dagli eventi che il programma intercetta. Evento linterazione del mondo esterno con il programma, quindi la pressione di un tasto sullinterfaccia grafica da parte dellutente, come la stampante che segnala di aver finito la carta o linchiostro.

Linguaggi compilati e linguaggi interpretati


In informatica, un linguaggio di programmazione un linguaggio formale, dotato di una sintassi e di una semantica ben definite, utilizzabile per il controllo del comportamento di una macchina formale. Lutilit dei linguaggi di programmazione sta nel fatto che le macchine formali sono descrizioni teoriche di strumenti implementati tipicamente come microcontrollori o microprocessori. Sostanzialmente quindi, un linguaggio di programmazione lo strumento che ci permette di creare il software adatto a controllare strumenti governati da processori. Se si considera infine che si stima che ogni strumento elettronico attualmente in commercio dal valore superiore alle 50 euro contenga un

Le Basi della Programmazione

processore, si capisce subito limportanza dei linguaggi di programmazione nella definizione della tecnologia moderna. Programmare in un dato linguaggio di programmazione significa generalmente scrivere uno o pi semplici file di testo chiamato codice sorgente, seguendo sintassi e semantica del linguaggio scelto. I linguaggi di programmazione si dividono dunque in due grandi classi di linguaggi: 1. linguaggi compilati 2. linguaggi interpretati. I linguaggi compilati sono quelli che sottopongono il codice sorgente a compilazione. Questa loperazione di traduzione del codice sorgente dal linguaggio comprensibile alluomo, quello stabilito dalla sintassi del linguaggio scelto, al linguaggio comprensibile dal processore, definito linguaggio macchina. Il risultato della compilazione viene solitamente definito codice oggetto. Per fare questo ogni linguaggio compilato ha bisogno di un suo proprio compilatore: questo genericamente un programma che si occupa della traduzione descritta. La compilazione crea dunque un legame fra il codice oggetto prodotto e il processore che dovr eseguirlo. Questo crea importanti vantaggi, come la velocit di esecuzione del codice e lefficienza nella gestione della memoria, ma anche svantaggi come una difficile portabilit (su altre architetture) del codice oggetto e a volte anche del codice sorgente. Esempi di linguaggi compilati sono il C, C++, Pascal e Fortran. I linguaggi interpretati si propongono di eliminare il problema della portabilit. Il codice sorgente in questi casi non viene pi compilato per la creazione del codice oggetto, ma coincide esattamente con esso! Viene infatti interpretato in fase di esecuzione riga per riga e tradotto ogni volta nella corrispondente operazione da eseguire. In questo caso, i linguaggi interpretati necessitano dunque di un interprete che interpreti e faccia eseguire il codice. Linterpretazione in teoria libera dalla dipendenza dalla piattaforma: sar sufficiente avere un interprete adatto ad ogni piattaforma per poter eseguire il codice ovunque. Daltro canto per si assiste ad un grosso decadimento delle prestazioni in fase di esecuzione, per il doppio lavoro di dover interpretare ed eseguire in real-time. Alcuni linguaggi (java, C#) cercano di mitigare questo aspetto negativo dellinterpretazione mantenendo i suoi aspetti positivi, fornendo una sorta di semi-compilazione preventiva che fornisce un linguaggio intermedio (bytecode) facilmente interpretabile. Esempi di linguaggi interpretati sono il Basic, il Perl, il Python e in generale tutti i linguaggi di scripting.

Le Basi della Programmazione

Valutare un linguaggio di programmazione


Non ha senso, in generale, parlare di linguaggi migliori o peggiori, o di linguaggi migliori in assoluto: ogni linguaggio nasce per affrontare una classe di problemi pi o meno ampia, in un certo modo e in un certo ambito. Per, dovendo dire se un dato linguaggio sia adatto o no per un certo uso, necessario valutare le caratteristiche dei vari linguaggi:

Caratteristiche intrinseche
Sono le qualit del linguaggio in s, determinate dalla sua sintassi e dalla sua architettura interna. Influenzano direttamente il lavoro del programmatore, condizionandolo. Non dipendono n dagli strumenti usati (compilatore/interprete, IDE, linker) n dal sistema operativo o dal tipo di macchina. Espressivit: la facilit e la semplicit con cui si pu scrivere un dato algoritmo in un dato linguaggio. Didattica: la semplicit del linguaggio e la rapidit con cui lo si pu imparare. Leggibilit: la facilit con cui, leggendo un codice sorgente, si pu capire cosa fa e come funziona. La leggibilit dipende non solo dal linguaggio ma anche dallo stile di programmazione di chi ha creato il programma. Robustezza: la capacit del linguaggio di prevenire, nei limiti del possibile, gli errori di programmazione. Modularit: quando un linguaggio facilita la scrittura di parti di programma indipendenti (moduli) viene definito modulare. In genere la modularit si ottiene con luso di sottoprogrammi (subroutine, procedure, funzioni) e con la programmazione ad oggetti. Flessibilit: la possibilit di adattare il linguaggio, estendendolo con la definizione di nuovi comandi e nuovi operatori. Generalit: la facilit con cui il linguaggio si presta a codificare algoritmi e soluzioni di problemi in campi diversi. Efficienza: la velocit di esecuzione e luso oculato delle risorse del sistema su cui il programma finito gira. Coerenza: lapplicazione dei principi base di un linguaggio in modo uniforme in tutte le sue parti.

Le Basi della Programmazione

Caratteristiche esterne
Oltre alle accennate qualit dei linguaggi, possono essere esaminate quelle degli ambienti in cui operano. Un programmatore lavora con strumenti software, la cui qualit e produttivit dipende da un insieme di fattori che vanno pesati anchessi in funzione del tipo di programmi che si intende scrivere. Diffusione: il numero di programmatori nel mondo che usa il tale linguaggio. Standardizzazione: Lesistenza di alcuni dialetti del linguaggio frammenta la comunit di programmatori, limitandone lutilit e la diffusione generale. Integrabilit: dovendo scrivere programmi di una certa dimensione, molto facile trovarsi a dover integrare parti di codice precedente scritte in altri linguaggi: se un dato linguaggio di programmazione consente di farlo facilmente, magari attraverso delle procedure standard, questo decisamente un punto a suo favore. Portabilit: la possibilit che portando il codice scritto su una certa piattaforma su unaltra, questo funzioni subito, senza doverlo modificare.

Le Basi della Programmazione

2. Tecniche di programmazione

Un programmatore, inteso come creatore di software, deve essere in grado di occuparsi di un po' tutte le fasi del ciclo di vita del software. Questa ultima espressione si riferisce al modo in cui si scompone l'attivit di progettazione, creazione, test e manutenzione di un software in sotto attivit coordinate fra loro. Questo concetto fondamentale in programmazione e segna la sua comparsa del mondo informatico segna la definitiva trasformazione della programmazione da attivit artigianale, affidata alla creativit dei singoli individui, ad attivit scientifica, con un forte accento sul processo di creazione come strumento di controllo del software. Esistono numerosi modelli su cui basare il ciclo di vita di un software, che prevedono comunque la scomposizione del processo di sviluppo in attivit analoghe. Le distinzioni fra questi si basano soprattutto su: l'enfasi relativa che si attribuisce a ciascuna attivit; l'individuazione degli attori specifici incaricati di ciascuna attivit; l'ordine in cui le attivit si svolgono. Le principali attivit costituenti il processo di sviluppo sono: Analisi, ovvero l'indagine preliminare sul contesto in cui il prodotto software deve inserirsi, sulle caratteristiche che deve esibire, ed eventualmente su costi e aspetti logistici della sua realizzazione. Al termine della fase verr creato un documento che descrive le caratteristiche del sistema, tale documento viene definito "documento di Specifica". Progettazione, in cui si definiscono le linee essenziali della struttura del sistema da realizzare, in funzione dei requisiti evidenziati dall'analisi e dal documento finale da essa creato. In questa fase sar sviluppato un documento che permetter di avere una definizione della struttura di massima (architettura di alto livello) e una definizione delle caratteristiche dei singoli componenti (moduli). Implementazione, ovvero la sua realizzazione concreta; questa tipicamente consiste nella realizzazione di uno o pi programmi in un determinato linguaggio di programmazione.

Le Basi della Programmazione

Collaudo, volta a misurare in che modo il sistema realizzato soddisfa i requisiti stabiliti nella fase di analisi, ovvero a valutarne la correttezza rispetto alle specifiche. Le tipologie specifiche di test (prove) si possono inoltre distinguere in funzione dei particolari aspetti dei moduli o del sistema che vengono valutati; si parla per esempio di test funzionali, test di performance, test di accettazione, test d'installazione. Manutenzione, che comprende tutte le attivit di modifica del software successive al suo rilascio presso il cliente o la sua immissione sul mercato. Queste attivit possono essere volte a correggere errori del software, adattarlo a nuovi ambienti operativi, o estenderne le funzionalit. La manutenzione incide sui costi tanto che si stima che il 60% di questo dipenda dalla manutenzione. Ogni modifica al software comporta necessariamente la necessit di nuovi test, sia relativi alle nuove funzionalit eventualmente introdotte, sia mirati a verificare che le modifiche apportate non abbiano compromesso funzionalit preesistenti (test di regressione).

Vediamo con un migliore dettaglio come sviluppare queste fasi in modo da ottenere un'organizzazione il pi possibile precisa del lavoro da svolgere, lasciando comunque ad ognuno la possibilit di personalizzare il lavoro, mettendo l'accento su uno degli aspetti, a seconda delle inclinazioni proprie e delle difficolt del progetto.

Le Basi della Programmazione

La fase di Analisi
Prevede l'analisi dei requisiti, uno studio del problema il cui obiettivo stabilire se vale la pena (da un punto di vista tecnico ed economico) realizzare il sistema del quale si vanno definendo i requisiti. Ovviamente in un progetto scolastico o in un progetto personale questa fase autoreferenziale. I requisiti del problema vanno individuati mettendo in evidenza l'obiettivo finale del processo stesso: in questa fase vanno individuate le variabili di input e di output, il linguaggio di programmazione pi adatto a risolvere il problema e l'obiettivo finale del progetto software.

La fase di Progettazione
In questa fase si scompone il problema in tutti i suoi sotto-problemi (moduli) secondo uno sviluppo top-down, si definisce la gerarchia e la interdipendenza fra questi e se ne discute la soluzione, supportandola con esempi, modelli matematici, diagrammi di flusso. Obiettivo della fase definire l'algoritmo risolutivo del problema.

Strategie Risolutive
Elaborare un algoritmo non una operazione semplice ed sempre diversa per ognuno dei problemi che ci si trova ad affrontare. Spesso per le tecniche di risoluzione, come i modi di ragionare delle persone, tendono a seguire determinati schemi, consolidati con il tempo. Eccone alcuni:

Sfruttare L'esperienza Gioco dello Scarabeo numerico Lo scarabeo numerico si gioca con 9 carte numerate da 1 a 9, poste sul tavolo scoperte. I due giocatori devono prendere, alternativamente, una carta dal tavolo. Vince chi per primo riesce a totalizzare 15 con tre carte. Cercate di definire una strategia di gioco che vi consenta di vincere, o quanto meno di non perdere. (saper giocare a tris potrebbe essere utile!)

Le Basi della Programmazione

10

Procedere a ritroso Gioco dei 24 dadi Si hanno a disposizione 24 dadi di cui uno, ma non si sa quale, leggermente pi pesante di tutti gli altri. Utilizzando una bilancia a due piatti identificare il dado pi pesante in non pi di 3 pesate Gioco del 100 Si gioca in due, uno per volta, partendo da zero e aggiungendo almeno 1 al totale e al massimo 10. Vince chi dice 100. Algebra Zio e nipote Augusto ha il triplo degli anni che aveva sua nipote Clarabella 10 anni fa, e Clarabella, ora, ha la met degli anni che suo zio Augusto avr fra 5 anni. Che differenza di et c' tra zio e nipote?

La fase di Implementazione
In questa fase si passa alla realizzazione pratica dell'algoritmo progettato, implementandolo nel linguaggio di programmazione scelto nella fase di analisi. Per ogni difficolt implementativa incontrata in questo livello necessario interrompere la fase e tornare alla precedente fase di progettazione, risolvere l'ostacolo, rielaborare l'algoritmo risolutivo e procedere di nuovo alla sua implementazione. La fase termina con la realizzazione di un software funzionante che risolve il problema individuato nella fase di analisi.

Le Basi della Programmazione

11

3. C++, primi elementi

La struttura generale del codice e lorganizzazione dei programmi nel linguaggio C++, perfettamente compatibile con quella del C ed descrivibile tramite un semplice schema: DIRETTIVE AL PREPROCESSORE DICHIARAZIONE VARIABILI GLOBALI PROGRAMMA PRINCIPALE (main) FUNZIONE 1 FUNZIONE 2 ... FUNZIONE n

Di tutte le sezioni componenti la struttura di un programma, comunque doveroso notare che solo una obbligatoria e cio la dichiarazione di un programma principale. Tutte le altre possono essere omesse. Le direttive al preprocessore C sono istruzioni di pre-elaborazione del codice sorgente, che permettono unorganizzazione dinamica del codice. Sono semplicemente distinguibili da ogni altra istruzione perch ogni direttiva inizia sempre con un #. Le direttive pi comuni sono la #include che serve a dichiarare lutilizzo di una determinata libreria e la direttiva #define che serve a dichiarare una sostituzione di testo. Ogni programma scritto in C++ strutturato come una serie di chiamate a funzione. Non c codice esecutivo fuori da una delle funzioni, che sono lambiente predefinito per le operazioni. La funzione main semplicemente la funzione che viene chiamata per prima. A questa viene dunque affidato il compito di contenere tutte le chiamate alle altre funzioni disponibili. La sintassi generale di una funzione della forma:

Le Basi della Programmazione <tipo restituito> <nome funzione> ( <elenco parametri> ) { <istruzione 1> ; <istruzione 2> ; ... ; } dove: tipo restituito indica il tipo del valore di uscita della funzione, elenco parametri rappresenta linsieme dei parametri che la funzione accetta in ingresso.

12

Il linguaggio C++ prevede un insieme di categorie lessicali chiaramente distinguibili fra loro: commenti identificatori parole chiave costanti letterali punteggiatura e operatori

I commenti sono una parte fondamentali di ogni buon programma in qualsiasi linguaggio di programmazione!! Infatti i commenti sono parti inserite direttamente nel codice, ma ignorate dal compilatore. Questo permette dunque di commentare il codice nella nostra lingua, descrivendo le operazioni effettuate. Sono importanti anche nellottica di permettere ad altri di leggere il nostro codice o di ricordarne a noi stessi la funzionalit. In C++ sono possibili due tipi di commenti: il commento di riga // questo commento termina quando si va a capo il commento lungo /* questo commento inizia qui e termina quando qui */

Le Basi della Programmazione

13

Identificatore un nome generico utilizzato per descrivere assieme variabili, costanti, tipi, funzioni e macro. Ognuna di queste categorie rappresenta una caratteristica del linguaggio. Ogni identificatore deve iniziare con una lettera o con un underscore (_) e non pu contenere spazi. Tutti gli identificatori presenti in un programma devono essere diversi, indipendentemente dalla categoria cui appartengono.

Tipi di dato fondamentali


Ogni identificatore che un programma intende utilizzare deve essere prima dichiarato. Per le variabili abbiamo la seguente sintassi: <qualificatore> <tipo> <identificatore>; dove: qualificatore un parametro opzionale che descrive il tipo e pu assumere i valori: signed, unsigned, short, long. tipo rappresenta (appunto) il tipo di dato memorizzato.

In C++ i tipi fondamentali si dividono in due categorie: semplici e strutturati. I tipi semplici, gli unici che ci interessano per adesso, sono:

Nome int

Descrizione Comprende i numeri interi relativi compresi in un certo intervallo, che dipende dalle caratteristiche fisiche della macchina ospite. (a 32 bit, circa 8 miliardi fra positivi e negativi) Comprende i numeri interi da 0 a 255. Tipicamente questo numero viene rappresentato per` come un carattere in base allutilizzo del codice ASCII. Comprende i numeri reali, rappresentati in virgola mobile con precisione singola (a 32 bit, 7 cifre).

char

float

Le Basi della Programmazione Nome double Descrizione Comprende i numeri reali, rappresentati in virgola mobile con precisione doppia (a 32 bit, 16 cifre). Rappresenta i valori logici true (1) e false (0). Rappresenta una sequenza di interi come una sequenza di identificatori costanti.

14

bool enum

I tipi strutturati si dicono cos perch non sono tipi definiti autonomamente come i tipi semplici, ma sono piuttosto strutture di dati creati a partire da altre gi esistenti. I tipi strutturati saranno comunque approfonditi pi avanti nel corso degli studi. Le categorie di tipi strutturati comprendono:

Nome array

Descrizione Sono variabili in grado di ospitare un numero predefinito di valori omogenei. Ad esempio la variabile array nomeMesi potrebbe essere un array contenente 12 stringhe ognuna corrispondente al nome di un mese. Sono variabili in grado di ospitare un numero predefinito di valori NON omogenei fra loro, ma solitamente unite da caratteristiche comuni. Ad esempio la variabile struct persona potrebbe contenere nome, cognome, data di nascita, peso, altezza. Sono variabili in grado di memorizzare nella stessa variabile tipi differenti in istanti differenti. Ad esempio la variabile temperatura potrebbe essere una union contenente un valore oat che indica la temperatura oppure il valore NULL ad indicare una temperatura non misurata.

struct

union

Stringhe e Caratteri
La gestione di caratteri e stringhe (sequenze di caratteri, cio parole) uno degli argomenti peculiari del linguaggio C++ e una delle cose in cui si discosta maggiormente dal genitore C.

Le Basi della Programmazione

15

Sostanzialmente per, rimane anche la possibilit di gestire le stringhe come in C, eliminando di fatto ogni eventuale problema di compatibilit fra i due approcci. Essendo per le stringhe solo un insieme di caratteri, sar utile introdurle successivamente a questi. I caratteri in C++ sono gestiti tramite una variabile di tipo char che abbiamo detto contenere un numero intero fra 0 e 255. La corrispondenza numero - carattere viene affidata alla cosiddetta tabella dei codici ASCII (American Standard Code for Information Interchange), che vediamo qui rappresentata:

Come visto allinterno di questa troviamo una serie di caratteri speciali, visualizzabili, ma non descrivibili direttamente. Per esempio, sappiamo che per andare a capo in un testo digitato, basta premere il tasto INVIO, ma come possibile indicare questo carattere? Per tutti i caratteri speciali come questo esiste un insieme di shorcut (scorciatoie, combinazioni di tasti) speciali, definite sequenze di escape:

Le Basi della Programmazione Sequenza di escape \n \t \a \f \r \v \b \\ \' \'' \? \0 New line tab beep Form feed Carriage return Vertical tab backspace backslash apex Double apex Question mark End string Descrizione

16

Una qualsiasi sequenza di caratteri, lettere, numeri, spazi e sequenze di escape, pu essere inclusa fra doppi apici ( ) a formare una stringa costante. Ad esempio, la stringa "ciao, Mondo!\n" indica la scritta ciao, Mondo! con alla fine un carattere newline, lo stesso effetto che si otterrebbe premendo INVIO su un testo digitato.

Le Basi della Programmazione

17

Operatori
Un operatore un simbolo che opera su una o pi espressioni, producendo un valore che pu essere assegnato ad una variabile. In C++ sono disponibili nativamente i seguenti insiemi di operatori:

Operatori Aritmetici Operatore + * / % Addizione Sottrazione Moltiplicazione Divisione Modulo (resto divisione intera) Incremento Decremento Descrizione x=2 x+2 x=2 5-x x=4 x*5 15/5 5/2 5%2 10%8 10%2 x=5 x++ x=5 x-Esempio 4 3 20 3 2.5 1 2 0 x=6 x=4 Risultato

++ --

Bisogna notare che la divisione funziona diversamente a seconda del tipo. Cio se a=5 e b=2 sono variabili intere, il risultato di a/b sar lintero 2 (troncamento della parte decimale), mentre se sono variabili reali (float), il risultato sar il reale 2.5. Nel caso di una operazione fra un reale e un intero il risultato sempre un reale.

Le Basi della Programmazione Operatori di Assegnazione Operatore = += -= *= /= .= %= x=y x+=y x-=y x*=y x/=y x.=y x%=y Esempio x=y x=x+y x=x-y x=x*y x=x/y x=x.y x=x%y come fare

18

Operatori di confronto Operatore == != > < >= <= Descrizione uguale a diverso da maggiore di minore di maggiore o uguale a minore o uguale a Esempio 5==8 restituisce false 5!=8 restituisce true 5>8 restituisce false 5<8 restituisce true 5>=8 restituisce false 5<=8 restituisce true

Operatori Logici Operatore && Descrizione and Esempio x=6 y=3 (x < 10 && y > 1) restituisce true x=6 y=3 (x==5 || y==5) restituisce false x=6 y=3 !(x==y) restituisce true

||

or

not

Esistono inoltre alcuni operatori meno comuni che saranno approfonditi in caso di necessit: Operatori sui bit

Le Basi della Programmazione Operatore && || ^ >> << Descrizione AND logico bit a bit XOR bit a bit Complemento a uno Shift destro Shift sinistro

19

Operatori speciali Operatore , Descrizione Esempio Comma un operatore binario e associativo da sinistra a c = (a = 5, b = 2, a + b) destra che fornisce come risultato il valore dellultima assegna a c il valore 7. espressione riportata a destra dellultima virgola ottenuto con i risultati parziali delle espressione di sinistra. Question & colon un operatore ternario con sintassi < EsprLogica >? < Espr1 >:<Espr2 > che valuta Espr1 se EsprLogica vera, Espr2 altrimenti. a = (b > 0?1 : 2) assegner ad a il valore 1 se b maggiore di zero, 2 altrimenti.

?:

Esercizi

Le Basi della Programmazione 1. Ciao, Mondo! 2. Programma per calcolare larea e il perimetro di un rettangolo di base = 4 e altezza = 6.

20

3. Programma che permette l'inserimento, da parte dell'utente, di un numero intero e che lo visualizza. 4. Programma che permette l'inserimento, da parte dell'utente, di un numero reale e che lo visualizza. 5. Programma che permette l'inserimento, da parte dell'utente, di un carattere e che lo visualizza. 6. Programma che permette l'inserimento, da parte dell'utente, di una stringa e che la visualizza. 7. Programma che visualizza il numero intero int n = 5 e va a capo, il carattere char c = 'b' e va a capo, il numero reale float r = 3.14 e va a capo. 8. Programma per calcolare larea e il perimetro di un rettangolo con i valori di base e altezza inseriti dall'utente. 9. Lavorare coi parametri della funzione main(). 10. Calcolo dello spazio occupato da ognuno dei tipi fondamentali semplici, tramite loperatore sizeof. 11. La libreria climits e i limiti superiore e inferiore dei tipi interi. 12. Dati tre numeri interi, considerarli come hh:mm:ss di un orario determinato. Calcolare i secondi trascorsi dalla mezzanotte del giorno prima. 13. Sapendo che in un parcheggio la prima ora costa 1.5 mentre tutte lo successive costano 1 e, scrivere un programma che richieda il numero complessivo delle ore e visualizzi il totale da pagare.

Le Basi della Programmazione

21

4. Le strutture di controllo

Il lavoro di creazione e organizzazione dellalgoritmo risolutivo di un certo problema si pu descrivere, in buona approssimazione, con la stesura di una serie di operazioni da compiere per ottenere il risultato prefissato. Durante questo lavoro, vanno tenute in considerazioni anche variabili come il linguaggio implementativo scelto, il sistema che ospiter il programma finale e ogni variabile contingente. ovvio dunque che la stesura dellalgoritmo unoperazione preliminare alla stesura del codice e andrebbero sempre ben distinte. La schematizzazione sistematica delle operazioni da svolgere per le risoluzione del problema il procedimento che viene definito programmazione strutturata. Obiettivo di questa tecnica di programmazione quello di organizzare in strutture pi semplici ogni algoritmo risolutivo. In un algoritmo le istruzioni possono essere organizzate in: 1. sequenza, in cui le istruzioni sono eseguite una dopo laltra 2. alternanza, in cui alcune istruzioni sono eseguite alternativamente (o un gruppo o un altro) 3. ripetizione, in cui alcune istruzioni sono ripetute un numero finito di volte. Accanto a queste semplici strutture possono esserne inserite altre pi generali o pi particolari, per schematizzare ogni algoritmo secondo i criteri che pi si adattano ai propri gusti. Queste tre sono state scelte perch vale il seguente teorema (Jacopini-Bohm, 1966) Qualsiasi algoritmo pu essere riscritto in maniera equivalente utilizzando solo le strutture di sequenza, alternanza, ripetizione. Questo algoritmo cio sostanzialmente afferma che le tre strutture indicate sono i mattoni fondamentali per riprodurre qualsiasi algoritmo formulabile dalluomo. In altre parole, tutti i problemi che sono risolvibile, hanno anche una soluzione costruita semplicemente con istruzioni in sequenza, alternanza e ripetizione.

Le Basi della Programmazione

22

Struttura alternativa
La struttura alternativa viene rappresentata in C++ tramite lo schema: if CONDIZIONE ISTRUZIONI cond VERA [else ISTRUZIONI cond FALSA] ove ovviamente il primo gruppo di istruzioni viene eseguito solo se la condizione vera, mentre il secondo solo se la condizione falsa. Le istruzioni rappresentano sempre un blocco e vanno quindi racchiuse fra parentesi graffe { e }. La condizione una espressione booleana di cui viene valutata la veridicit. Le parentesi quadre nello schema stanno invece ad indicare che la seconda parte dellistruzione ` opzionale, cio si potrebbe creare semplicemente una struttura del tipo if CONDIZIONE ISTRUZIONI in modo da eseguire un blocco di istruzioni solo nel caso che una condizione si verifichi.

Le Basi della Programmazione

23

Proviamo con un esempio a prendere pratica con le strutture illustrate. Immaginiamo di dover risolvere il seguente problema: dati due numeri dallutente, disporli in ordine crescente; Il seguente codice lo risolve. int primo, secondo, min, max; cout << "inserire primo numero: "; cin >> primo; cout << "inserire secondo numero: "; cin >> secondo; if (primo < secondo) { min = primo; max = secondo; } else { min = secondo; max = primo; } cout << "minore: " << min << endl; cout << "maggiore: " << max << endl;

Le Basi della Programmazione Va notato anche che listruzione IF.. ELSE pu essere ripetuta dentro ad unaltra, producendo una nidificazione e in questo caso si potrebbe perdere il legame fra IF e lELSE correlato. In questo caso vale la regola che ogni ELSE si riferisce sempre al pi vicino IF sovrastante. Nellesempio seguente lindentazione chiarifica i legami fra i vari IF.. ELSE. if CONDIZIONE if CONDIZIONE ISTRUZIONI else ISTRUZIONI else ISTRUZIONI

24

Le Basi della Programmazione

25

Struttura alternativa multipla


Capita a volte di dover tener testa non a due semplici possibilit alternative, ma ad una serie di comportamenti in risposta agli eventi. E questo il caso in cui possibile utilizzare una struttura alternativa multipla, la cui sintassi viene qui mostrata: switch (VARIABILE) { case VALORE-1: ISTRUZIONI-1; break; case VALORE-2: ISTRUZIONI-2; break; ... default: ISTRUZIONI; } Importante notare che la struttura switch funziona solo con variabili intere ed assolutamente analoga ad una serie di IF nidificati che hanno nella condizione il controllo sulla stessa variabile. Anche qui vediamo di illustrare un esempio: dato un numero intero da 1 a 12, visualizzare il mese corrispondente. Vediamo il codice: int mese; cout << "inserisci numero (da 1 a 12): "; cin >> mese; switch(mese) { case 1: cout << Gennaio; break; case 2: cout << Febbraio;

Le Basi della Programmazione break; ... case 12: cout << Dicembre; break; default: cout << E questo? Che mese ?; }

26

Listruzione break interrompe lo switch e in generale provoca luscita da un blocco, portando la linea da eseguire ad essere la prossima dopo una parentesi graffa chiusa. In caso di assenza lo switch esegue dal punto di ingresso tutte le istruzioni rimanenti nel blocco.

Esercizi sulla struttura alternativa


1. Dato un numero, inserito dallutente, verificare se pari o dispari 2. Scambiare due valori fra loro (ordinare due valori) 3. Scrivere un programma che richieda in ingresso tre valori interi distinti e ne determini il maggiore. 4. Scrivere un programma che richieda in ingresso QUATTRO valori interi distinti e ne determini maggiore e minore 5. Ordinare tre valori 6. Soluzione equazione di secondo grado 7. Programma che visualizza un men di scelta 8. Controllo se una data valida (giorno rispetto al mese, se si vuole strafare, anno bisestile). PS: un anno bisestile se divisibile per 4, ma non per 100. Oppure se divisibile per 100 e per 400. 9. Programma che individua la pi recente fra due date e calcola la differenza di tempo in secondi fra le due.

Le Basi della Programmazione

27

Struttura iterativa
In questa parte ci occuperemo di tutte le istruzioni del linguaggio C++ che permettono di ripetere un numero finito di volte un blocco di istruzioni. Il numero delle ripetizioni solitamente controllato tramite una condizione booleana. Una caratteristica comune delle istruzioni seguenti che tutte ripetono le istruzioni finch la condizione risulta vera, mentre escono dal ciclo esecutivo quando questa diventa falsa.

Iterazioni PREcondizionali
Le iterazioni PREcondizionali sono semplicemente quelle che prima di iniziare a eseguire le istruzioni da ripetere controllano la verit della condizione imposta. In questo caso se la condizione falsa, il ciclo non viene eseguito neanche una volta while CONDIZIONE ISTRUZIONI Vediamo con un esempio di chiarire il concetto: calcolare il prodotto di due numeri interi A, B sommando A per B volte.

int A = 5, B = 7, prodotto = 0; while ( B!=0) { prodotto += A; B--; }; // prodotto = prodotto + A; // B = B - 1

Le Basi della Programmazione

28

Iterazioni POSTcondizionali
Le iterazioni POSTcondizionali sono quelle iterazioni che prima eseguono una volta il blocco di istruzioni da ripetere e poi controllano una condizione per capire se bisogna iterare il procedimento. do ISTRUZIONI while CONDIZIONE Questo tipo di iterazione non molto comune e si utilizza solo in determinati casi, come ad esempio per controllare che un valore di input corrisponda ad un certo range:

int A; do { cout << "inserire un valore intero maggiore di ZERO: "; cin >> A; } while( A < 0);

Le Basi della Programmazione

29

Iterazioni enumerative
Le iterazioni enumerative, o con contatore, sono una struttura derivata dalle iterazioni PREcondizionali. Sono molto comuni perch permettono di mettere in evidenza un contatore intero nella condizione che indicher il numero di ripetizioni da eseguire. E questa quindi la struttura da utilizzare nel caso in cui sia noto a priori il numero di iterazioni. for ( INIZIALIZZAZIONE, CONDIZIONE, INCREMENTO ) ISTRUZIONI Vediamo un esempio: scrivere i primi 10 numeri interi for( int i = 1; i <= 10; ++i) { cout << i << endl; } E' importante notare due cose di questa nuova struttura introdotta: 1. il ciclo for, pur utilizzando una sintassi propria, si comporta in maniera assolutamente identica alla ripetizione pre-condizionale (il ciclo while). Questi due costrutti sono dunque assolutamente interscambiabili 2. a differenza dei precedenti costrutti iterativi, il ciclo for utilizza molto comodamente come contatore una variabile intera (nell'esempio precedente, la variabile intera i) definibile direttamente al suo interno. La variabile cos definita una pura variabile di lavoro: esiste solo durante il ciclo for di cui fa da contatore e scompare alla fine del ciclo

Le Basi della Programmazione

30

Esercizi sulla struttura iterativa


1. Visualizzare i primi 100 numeri. 2. Fare la somma dei primi 100 numeri e poi dei primi n numeri, con n inserito dallutente. 3. Fare la somma di 10 numeri inseriti a scelta dallutente 4. Calcolo dello zero di macchina 5. Programma per il calcolo del fattoriale di un numero dato e poi di tutti i fattoriali dei numeri minori di quello. 6. Visualizzazione Tavola Pitagorica 7. Visualizzazione di un rettangolo con cornice + e interno = di misure A e B, decise dallutente 8. Predisporre un programma, che determini il maggiore, il minore e la media degli n valori immessi dallutente. 9. Divisori di un numero intero A, inserito dallutente 10. Dato B, verificare se primo 11. Programma che calcola tutti i numeri primi minori di X 12. Dato C, verificare se perfetto e poi calcolare tutti i numeri perfetti minori di C. 13. Programma che disegna un rettangolo di caratteri di BASE e ALTEZZA scelti dallutente. Il rettangolo formato da un carattere a scelta come cornice e da un carattere a scelta allinterno. 14. Scrivere un programma che, richiesti i coefficienti (a, b, c) di unequazione di secondo grado, determini il numero delle soluzioni possedute e i valori, se esistono, delle soluzioni. 15. Scrivere un programma che calcoli il podio di una gara con 10 corridori. I corridori sono identificati da un numero di gara e hanno un tempo sul giro espresso in primi, secondi, millesimi decisi da una funzione random.

Le Basi della Programmazione

31

5. Array

Gli array sono una particolare struttura derivata molto utilizzata in programmazione. Rappresentano un tipo di dato derivato da altri preesistenti e raggruppano un insieme di dati omogenei (= dello stesso tipo) fra loro. Con una sola variabile di tipo array dunque, possiamo indicare tanti dati (e quindi tanti valori) dello stesso tipo. Gli array sono insiemi collettivi omogenei di grandezza predefinita, quindi il numero di elementi facenti parte della collezione viene decretato in fase di dichiarazione dello stesso. Ognuno degli elementi dellarray viene numerato per poterlo distinguere dagli altri; la variabile o costante intera utilizzata per riferirsi a questo numero viene definita indice dellarray. La dichiarazione di un array analoga a quella delle altre variabili, ricordandosi per che questo un tipo derivato: non esiste un array in s, ma esistono array di interi, array di reali, di caratteri o di qualsiasi tipo di dato definito in precedenza. La dichiarazione assume dunque la forma: tipo nome [ dimensione ] ; dove: tipo il tipo degli elementi della collezione; nome il nome (identificatore) della variabile array; dimensione un intero o una costante intera che rappresenta il numero di elementi della collezione.

Ad esempio, per dichiarare un array di 12 interi di nome giorniMese dovremo scrivere: int giorniMese[12]; Larray giorniMese conterr dunque 12 variabili numerate da 0 a 11. La numerazione degli elementi di un array infatti parte sempre da ZERO! Questo significa che per assegnare ad esempio il valore 31 al primo elemento (il numero di giorni del primo mese) dovremo scrivere:

Le Basi della Programmazione giorniMese[0] = 31; Per assegnare il valore 30 allundicesimo mese scriveremo giorniMese[10] = 30;

32

La posizione 10 rappresenta infatti lundicesimo elemento dellarray. Molto utile, quando si tratta di array, lavorare in collaborazione con il costrutto for. Ad esempio per azzerare tutti i valori dellarray giorniMese, sar sufficiente scrivere: for(int i=0; i< 12; ++i) { giorniMese[i] = 0; } Allo stesso modo per permettere ad un utente di inserire 10 valori baster scrivere: int valoriUtente[10]; for(int i=0; i<10; ++i) { cout << "Inserisci valore " << i + 1 << ": "; cin >> valoriUtente[i]; } Nei casi analoghi allesempio giorniMese, in cui dobbiamo inizializzare un array a dei valori prefissati e scrivere 12 o pi assegnazioni pu risultare noioso, sar sufficiente scrivere: int giorniMese[12] = {31,28,31,20,31,30,31,31,30,31,30,31}; inizializzando dunque larray in fase di dichiarazione. Per prendere confidenza con gli array ecco una serie di esercizi da eseguire PRIMA di procedere con la lettura. Buon lavoro!

Le Basi della Programmazione

33

Esercizi svolti

Dichiarare un array di 100 interi e riempirlo con i primi 100 numeri dispari.

int a[100]; for(int i = 0; i<100; i++) { a [ i ] = 2*i + 1; }

Dichiarare un array di 15 interi e inserirvi numeri casuali compresi tra 1 a 90.

Per eseguire questo programma dobbiamo imparare due nuove funzioni: la funzione rand() restituisce un valore intero positivo. int cart[ 15 ]; srand( time(0) ); for(int i = 0; i<15; i++) { cart [ 15 ] = rand() % 90 + 1; }

Dichiarare un array di stringhe e inizializzarlo con i giorni della settimana.

string giorni[7] = {lun, mar, mer, gio, ven, sab, dom};

Le Basi della Programmazione

34

Esercizi semplici sugli array


1. Dichiarare e inizializzare larray di stringhe mesiAnno e visualizzarne il contenuto. 2. Permettere allutente di inserire 5 valori interi memorizzati tramite array, poi visualizzarli e farne la somma. 3. Eseguire la media aritmetica di 8 valori numerici inseriti dallutente e memorizzati in un array di double. 4. Inserimento random di valori oat in un array di 5 posti e visualizzazione. 5. Visualizzare uno qualsiasi degli array precedenti in ordine inverso (dallultimo elemento al primo). 6. Dato un array di 100 interi, inizializzato con valori random, trovare: il massimo e la sua posizione, il minimo e la sua posizione.

Le Basi della Programmazione

35

Ricerca di un elemento
La ricerca di un elemento in un array una operazione tra le pi comuni in assoluto in informatica: immaginate ogni volta che inserite un nome utente, un codice, una password oppure semplicemente cercate un numero di telefono nella rubrica del telefonino! Tutte queste sono operazioni in cui informaticamente avviene una ricerca su un array di elementi omogenei fra loro. Vi sono vari tipi di ricerche effettuabili su un array; per ora vedremo la cosiddetta ricerca sequenziale. Questa ricerca esamina le componenti dellarray fino a trovare lelemento desiderato oppure arrestandosi alla fine dellarray se lelemento non presente. Per semplicit descriveremo la procedura per un array di interi.

// array inizializzato int ar[10] = {1,12,23,34,45,56,67,78,89,90}; int pos = -1; int daCercare; cout << "Inserisci valore: "; cin >> daCercare; int i = 0; while( pos == -1 && i < 10 ) { if( ar[i] == daCercare ) { pos = i; } i++; } // se ar[i] non mai stato uguale a daCercare significa che // lelemento non presente nell'array // (e che pos rimasto -1!!) if (pos == -1) // posizione dellelemento da cercare; // -1 significa NON trovato

Le Basi della Programmazione { cout << "L'elemento NON e presente nell'array." << endl; } else {

36

cout << "Lelemento e presente alla posizione " << pos << endl; } La ricerca si arresta quando viene trovato un elemento uguale a quello da cercare o quando lindice arrivato allultimo elemento (pos == 1 && i < 10 ). Ulteriori esercizi per la comprensione e lapprofondimento sono elencati di seguito.

Esercizi sulla ricerca sequenziale


1. testare il codice proposto per la ricerca modificandolo in modo da permettere allutente di inserire anche i valori dellarray. 2. Modificare il codice proposto per la ricerca di un numero reale su un array di reali. 3. Modificare il codice proposto per la ricerca di un carattere su un array di caratteri. 4. Modificare la ricerca su array trovando tutte le occorrenze nellarray di un valore da cercare. (suggerimento: i risultati saranno stanziati NON su un intero ma su un array di interi)

Le Basi della Programmazione

37

Ordinamento degli elementi


Ordinare gli elementi di un array (array sorting) unaltra fra le operazioni pi comuni esistenti in informatica: mai riusciremmo a leggere un elenco del telefono o un vocabolario disordinato!! I metodi di ordinamento sono numerosi e spesso molto ingegnosi: qui ne vengono presentati due fra i pi semplici e comuni. Il primo il classico Bubble Sort, un metodo che permette di far salire in alto gli elementi pi grandi di un array, cos come le bolle di sapone pi grandi salgono pi velocemente. Il risultato finale sar ovviamente un array ordinato in senso crescente. Il secondo il cosiddetto Insert Sort, ovvero Inserimento Ordinato. In questo modo ogni array sempre ordinato perfettamente perch ogni nuovo elemento viene inserito al punto giusto, spostando i successivi di una posizione in avanti. Una semplice ricerca in Rete permetter di scovare almeno unaltra decina di algoritmi di ordinamento ognuno con la sua peculiarit e i suoi punti di forza. Come detto, i due che verranno citati sono stati scelti in base alla semplicit di comprensione (del primo) e utilit nella programmazione (di entrambi, soprattutto il secondo). // BUBBLE SORT int ar[ 10 ]; srand( time(0) ); for(int i = 0; i < 10; i++) { ar[i] = rand(); } // ordinamento for(int j = 1; j < 10; j++) { for( int i = 0 ; i < 10 -j; i++) { // se il valore del precedente maggiore del successivo, scambiali if(ar[i] > ar[i+1]) { int temp; temp = ar[i];

Le Basi della Programmazione ar[i] = ar[i+1]; ar[i+1] = temp; } } }

38

Il secondo algoritmo, piuttosto che un ordinamento di un insieme di valori disordinati, una procedura per inserire sempre nel punto giusto i valori in un array. E molto utile in quanto molti elenchi hanno un naturale bisogno di essere ordinati e comunque aggiornabili e una procedura completa di ordinamento richiederebbe un lavoro inutile e da ripetere ogni volta per ordinare un solo elemento. // INSERT SORT int ar[ 10 ]; int n = 0; ... // numero elementi inseriti nel array // va eseguito ogni volta che si vuole inserire un numero nellarray if(n<10) { int temp; cout << "Inserisci un valore: "; cin >> temp; if(n==0) { array[n] = temp; n++; } else { int i = n - 1; while(temp < array[i] && i >= 0) { array[i+1] = array[i]; i--;

Le Basi della Programmazione } array[i+1] = temp; n++; } }

39

Ognuno di questi algoritmi merita sicuramente di essere testato personalmente. Fatelo. E poi anche gli esercizi di comprensione e approfondimento che seguono.

Esercizi su ordinamenti degli array


1. Ordinamento di un array di caratteri. 2. Ordinamento di un array di caratteri case-insensitive (dove a conta come A). 3. Ordinamento decrescente di un array di interi random. 4. Inserimento ordinato decrescente di un array di oat. 5. Men di scelta che permette inserimento ordinato e visualizzazione dei cognomi dei componenti della classe.

Le Basi della Programmazione

40

Ricerca Binaria (ricerca su array ordinato)


La ricerca binaria (o dicotomica) un altro degli algoritmi fondamentali che studieremo nel corso. un algoritmo di ricerca molto veloce che si pu utilizzare solo nei vettori ordinati. Per dare unidea di come funziona immaginate di cercare una parola nel vocabolario: apriamo a caso il tomo e guardiamo a che punto siamo; se la parola si trova prima apriamo unaltra pagina a caso tra quelle precedenti, viceversa in quelle successive. Ripetiamo restringendo il campo di ricerca finch non troviamo la pagina in cui deve trovarsi la nostra parola e solo a quel punto consultiamo la pagina! Il seguente algoritmo funziona esattamente cos: cerca allesatta met del array il valore da cercare. Se lo trova bene, altrimenti se il valore da trovare minore di quello trovato si cerca nella prima met (a met della prima met), se il valore da trovare maggiore di quello trovato si cerca nella seconda met (la met della seconda met). Il procedimento si itera finch non si trova il valore o finch ci sono caselle in cui cercare. Nell'esempio seguente il campo di ricerca delimitato ad ogni iterazione dai valori low e high, che ne rappresentano estremo inferiore e superiore. // RICERCA DICOTOMICA int daCercare; cout << "Inserisci elemento da cercare: "; cin >> daCercare; int high = 10 1; // 10 la dimensione dell'array int low = 0; int pos = -1; do { int i = (high + low)/2; if ( ar[i] == daCercare ) { pos = i; } else { if( ar[i] < daCercare ) { low = i + 1;

Le Basi della Programmazione } else { high = i - 1; } } } while(high >= low && pos == -1); if (pos == -1) { cout << "Elemento NON presente!" << endl; } else { cout << "Lelemento si trova alla posizione " << pos << endl; }

41

Esercizi sulla ricerca dicotomica


1. Men che permetta inserimento ordinato e ricerca binaria su un insieme di interi. 2. Ricerca binaria su un elenco decrescente di oat. 3. Test di velocit. Preparare un array di interi di 1000 caselle con valori random. Eseguire una ricerca su un elemento. Poi ordinarlo ed eseguire una ricerca binaria. Valutazione delle differenti velocit di ricerca.

Le Basi della Programmazione

42

6. Stringhe in C++

Nel linguaggio C++, la classe std::string la rappresentazione standard per una stringa di testo. Questa classe rimuove molti dei problemi introdotti nel linguaggio C++ nella gestione delle stringhe e permette pure una conversione implicita dal tipico array di caratteri del linguaggio C alla nuova classe C++. Inoltre introduce nuovi importanti benefici nella trattazione delle stringhe, come la possibilit di confronto tramite gli appositi operatori ==, !=, <, >, <=, >= invece dellutilizzo delle funzioni della libreria C string.h che diviene sostanzialmente deprecata (in C++). Vediamo un semplice esempio di utilizzo per capire quanto sia facile gestire stringhe in C++: string uno = "ciao"; string due = "hello"; if( uno == due ) cout << uno << " uguale a " << due << endl; else cout << uno << " diverso da " << due << endl; return 0; Semplificando, si pu dire che il tipo string cos introdotto, funziona esattamente come tutti gli altri tipi primitivi introdotti.

Le Basi della Programmazione

43

Stringhe e Puntatori
Poich il tipo char* pu essere implicitamente trasformato in string &, questo modo di dichiarare i parametri permette un uso pi semplice e universale della funzione. const char* a = "ciccio"; string b = "pippo"; // tutti e tre i metodi funzionano!! scrivi( a ); scrivi( "poldo" ); scrivi( b );

Operatori su stringhe
Le stringhe in C++ possono utilizzare una serie interessante di operatori di convenienza, in modo da rendere semplici e intuitive certe operazioni altrimenti piuttosto laboriose nel linguaggio C. Il pi semplice e ovvio loperatore di assegnazione: string str; str = "andrea"; // possibile assegnare parole str = a; // ERRORE: una stringa non un carattere str = "a"; // cos funziona char pippo[30] = "diamantini"; str = pippo; // CORRETTO char *pluto = str; // ERRORE Inoltre, le stringhe usano loperatore aritmetico + per le concatenazioni string nome = "andrea"; string cognome = "diamantini"; string spazio = " ";

Le Basi della Programmazione string nomeCompleto = nome + spazio + cognome; cout << nomeCompleto << endl; // scrive "andrea diamantini"!! e gli operatori di confronto (come gi visto nel primo esempio) string uno = "prova"; string due = "tentativo"; if( uno < due ) // VERO. Alfabeticamente parlando { // nellalfabeto e soprattutto nel codice ASCII! cout << "OK!" << endl; } Chiaramente funzionano anche gli operatori ! =, <=, >=.

44

Ultima cosa, la possibilit di accedere casualmente ad ogni elemento della stringa, esattamente come in un array di caratteri string uno = "prova"; uno[0] = P; // mette la maiuscola char c = uno[3]; // c vale v

Le Basi della Programmazione

45

7. Tipi Derivati

I tipi derivati (o composti) sono cos detti perch nelle basi del linguaggio di programmazione non esistono, ma devono essere dichiarati dal programmatore a partire dai tipi fondamentali (o semplici). I linguaggi di programmazione fanno un grande uso dei tipi derivati, perch tendono ad avvicinarsi molto di pi alle strutture dati necessarie per risolvere un problema, favorendo la leggibilit e la chiarezza del programma. In C++ abbiamo gli strumenti per definire infiniti tipi derivati, classificabili per in poche categorie: array, funzioni, puntatori, strutture, unioni, campi, file.

Quindi qualsiasi tipo derivato sar comunque in una di queste categorie e quindi soggetto alle regole proprie della stessa. Alcuni di questi tipi sono gi stati affrontati nel corso della nostra trattazione, vediamo gli altri ancora sconosciuti.

Le Basi della Programmazione

46

Strutture
la struttura serve a definire in un unico tipo un insieme di dati che non sono slegati fra loro, ma che insieme rappresentano un unico concetto. Quando, ad esempio, si parla di un compleanno, si ha in mente un unico dato, quello relativo alla propria data di nascita. Secondo le nostre attuali conoscenze, avremmo per dovuto dichiarare almeno tre variabili intere diverse per memorizzare ci che nella nostra testa un unico concetto. La struttura si pone di superare questo limite, insito nella semplicit dei tipi fondamentali e permette di dichiarare un nuovo tipo, data, che contenga informazioni sul giorno, mese, anno di ogni data. struct data { int gg; int mm; int aa; }; Come si vede, tramite la parola chiave struct, abbiamo definito il nuovo tipo data, che contiene al suo interno tre interi, uno per il giorno, uno per il mese, uno per lanno. I dati contenuti allinterno di una struct sono solitamente definiti campi. Attenzione per! La struct cos definita NON una variabile, ma un tipo, che dovr essere istanziato per poter essere utilizzato. data compleannoAndrea; compleannoAndrea.gg = 3; compleannoAndrea.mm = 7; compleannoAndrea.aa = 1976; Come visto nellesempio, i campi sono accessibili dallistanza tramite loperatore punto (.). A quel punto, possibile lavorare coi dati come si sempre fatto. I campi di una struct possono essere di qualsiasi tipo (semplice o derivato), ma che deve gi essere stato definito. Cos se la struct data gi definita, possiamo ora creare:

Le Basi della Programmazione struct indirizzo { string via; int numero; string citta; char provincia[2]; int cap[5]; }; struct datiPersonali { string nome; string cognome; data dataNascita; indirizzo indResidenza; };

47

Da notare che la dichiarazione di una struct richiede il punto e virgola ; finale dopo la chiusura delle parentesi graffe. In C++ esistono solo due casi in cui assolutamente necessario (e questo il primo che incontriamo)!

Esercizi sulle struct


1. Programma che utilizza la struttura data sopra descritta per memorizzare la propria data di nascita e visualizzarla nella forma gg/mm/aaaa 2. Programma che utilizza la struttura datiPersonali sopra descritta per memorizzare i propri dati personali e che al termine dellinserimento li visualizza. 3. Programma che gestisce la classifica finale di una gara di nuoto con nome dellatleta, vasca in cui ha gareggiato, posizione finale raggiunta e tempo di gara. 4. Programma che simula la rubrica del proprio cellulare. 5. Programma che gestisce i dati per la compravendita di automobili.

Le Basi della Programmazione

48

Unioni
Le unioni sono un costrutto analogo alle strutture, ma con tuttaltra funzionalit: se infatti le strutture introducono una organizzazione logica dei dati, le union raggruppano nello stesso spazio di memoria tutti i campi presenti. Questo permette di memorizzare un solo campo per volta tra tutti quelli presenti nella union. Vediamo la sintassi generale: union Identificativo { tipo_campo1 nome_campo1; tipo_campo2 nome_campo2; . . . }; Ovviamente, visto come sono definite, sar facile intuire che le union occupano in memoria lo stesso spazio del loro campo pi grande. E possibile utilizzare le union per memorizzare nella stessa variabile (alternativamente) dati di tipo diverso. Immaginiamo di avere un elenco di oggetti di cui per lidentificazione si hanno una sigla di TOT lettere, tipo codice a barre oppure un numero di serie. In quel caso sar possibile definire una union del tipo: union oggetto { char codiceBarre[20]; int numeroSerie; }; Il tipo union oggetto potr dunque contenere o una sequenza di 20 caratteri oppure un intero. le union, vista la loro estrema particolarit e la difficolt nel trattamento dei dati memorizzati, sono poco usate se non in casi particolari, che qui non tratteremo.

Le Basi della Programmazione

49

Campi
I campi (fields) sono un costrutto tipico del C, che permette di accedere direttamente ai singoli bit che compongono una variabile. La sintassi generale analoga alle precedenti incontrate: struct Identificativo { unsigned nome_campo1 : numero_bit1; unsigned nome_campo2 : numero_bit2; . . . }; Vediamo con un esempio pratico di illustrare al meglio il concetto: struct Date { unsigned nWeekDay : 3; unsigned nMonth : 5; unsigned nYear : 8; }; Date today; La variabile today di tipo Date occupa uno spazio di memoria pari alla somma dei bit utilizzati in ogni campo, arrotondato (sempre) per eccesso ad un multiplo della word di macchina. Nel nostro caso, abbiamo 3 + 6 + 5 + 8 = 22 bit utilizzati, mentre le macchine moderne hanno attualmente word a 32 o 64 bit. Questo significa, nel caso di word = 32, che la variabile today occupa 4 byte, pari a 32 bit di memoria (22 utilizzati, 10 sprecati). I bit della variabile si accedono con la solita sintassi (dot notation), utilizzata anche per le strutture e le unioni e i bit possono essere trattati come semplici numeri interi non negativi. Ad esempio, sar possibile scrivere, nella variabile today, qualcosa come: today.nMonth = 3; Come le union, anche i campi sono costrutti poco usati, se non nellinterazione diretta bit a bit con determinati dati. Il loro utilizzo tipico non sar oggetto del corso. // 0..7 (3 bits) unsigned nMonthDay : 6; // 0..31 (6 bits) // 0..12 (5 bits) // 0...100 (8bits)

Le Basi della Programmazione

50

Puntatori
Un Puntatore in C++ una variabile che contiene lindirizzo di memoria in cui si trova un oggetto, invece che memorizzare direttamente quello. Il tipo di dato puntatore dunque tipicamente un tipo di dato derivato dal tipo delloggetto di cui contiene lindirizzo di memoria. Vediamo un esempio di codice che possa chiarirci le idee: int main() { int a, b; int *p; a = 2; p = &a; *p = 3; b = a; } Un puntatore nullo un puntatore a cui non stato assegnato nessun indirizzo (p = 0, oppure p = NULL). Quando si tenta di utilizzare un puntatore nullo, si ottiene un crash dellapplicazione con un poco rassicurante Segmentation Fault. I problemi tipici con i puntatori sono suddivisibili in due categorie, entrambe da evitare accuratamente: // p contiene lindirizzo di a // allindirizzo individuato da p // viene assegnato il valore 3 // allintero b viene assegnato il valore di a. // Quanto vale b?? // p un puntatore a intero

Problema 1: Dangling Reference


La problematica definita dangling reference (riferimento sospeso) si verifica quando un valore viene memorizzato tramite puntatore e il puntatore perde il riferimento all'oggetto. Questo rimane perso in memoria in un punto indefinito, occupando il blocco fino allo spegnimento della macchina! int n = 4;

Le Basi della Programmazione double *p = & sqrt(n); p = 0; // Assegnato a p lindirizzo del calcolo // della radice quadrata di n. // il numero precedentemente calcolato // NON stato cancellato, // ma non pi raggiungibile

51

Problema 2: Dangling Pointer


La problematica definita dangling pointer (puntatore sospeso) si verifica quando un puntatore contiene un indirizzo inutile, non contenente nessun dato del problema. Quando si cerca di accedere al dato contenuto nel puntatore si viola un blocco di memoria non accessibile e l'applicazione va in crash! int *p; int i = 1; while(i<3) { int n = 2*i; p = &n; i++; } cout << *p; // CRASH! p contiene un indirizzo non valido // infatti n non esiste pi!! // p punta allindirizzo di n

Se non intendiamo modificare in alcun modo loggetto attraverso il puntatore, possiamo dichiararlo costante: int n = 7; const int *p = n; *p = 5; // ERRORE. CODICE NON COMPILABILE

Le Basi della Programmazione

52

Riferimenti
Oltre ai puntatori, il C++ supporta anche il concetto di riferimenti. Come un puntatore, un riferimento contiene lindirizzo di un oggetto. Le differenze sostanziali sono che: i riferimenti sono dichiarati usando & anzich * i riferimenti devono essere inizializzati (cio bisogna subito assegnargli un indirizzo) e non possono essere poi riassegnati. loggetto associato ad un riferimento direttamente accessibile con la solita sintassi (no >) un riferimento non pu essere nullo

I riferimenti sono usati soprattutto per la dichiarazione dei parametri. Per default, il C++ utilizza il passaggio di parametri per valore: int moltiplicazione(int a, int b) { return a*b; } E in questo caso dovremmo invocare la funzione nel seguente modo: int primo = 2; int secondo = 3; int d = moltiplicazione(primo,secondo); Questo crea nel calcolo della funzione distanza un inutile spreco di memoria dato dalla duplicazione dei valori passati come parametri. Il programmatore attento scriver dunque: int moltiplicazione(int* a, int* b) { return (*a) * (*b); } E utilizzer cos la funzione:

Le Basi della Programmazione

53

int primo = 2; int secondo = 3; int d = moltiplicazione(&primo,&secondo); Questa accortezza per rende le cose pi dificili e introduce ulteriori problemi, ad esempio nel caso del passaggio di puntatori nulli, oppure rischiando che il codice della funzione moltiplicazione modifichi i valori dei nostri dati. Utilizzando riferimenti al posto dei puntatori, faremo cos: int moltiplicazione(int& a, int& b) { return a*b; } E potremo utilizzare il semplice codice: int primo = 2; int secondo = 3; int d = moltiplicazione(primo,secondo); Se infine NON vogliamo rischiare modifiche indesiderate ai nostri valori, possiamo utilizzare riferimenti costanti: int moltiplicazione(const int& a, const int& b) Puntatori e riferimenti rappresentano le stesse informazioni in memoria. Per convertire uno nellaltro sufficiente utilizzare gli operatori unari & e * int p; int *ptr = p; int &ref = *ptr; int *p2 = &ref;

Le Basi della Programmazione

54

I riferimenti hanno una sintassi molto semplice e conveniente, ma i puntatori possono essere sempre riassegnati, possono contenere valore nullo e la loro sintassi esplicita li individua senza ombra di dubbio. Per queste motivazioni in programmazione si usano di solito i puntatori lasciando ai riferimenti, per lo pi costanti, il ruolo di parametri nelle funzioni.

Strutture e Puntatori
Data una struttura, possibile accedere ai suoi campi anche tramite un opportuno puntatore. I puntatori per, utilizzano loperatore arrow ( >) per accedere ai campi della struttura, al posto delloperatore dot (.). Data la struttura data, definita precedentemente, in una variabile normale possibile riferirsi ai suoi campi tramite il solito operatore dot (.). struct data n; n.gg = 30; n.mm = 3; n.aa = 2009; Se utilizziamo un puntatore invece, dobbiamo riferirci ai campi tramite loperatore arrow ( >): struct data m; struct data *p; p = &m; p->gg = 31; p->mm = 3; p->aa = 2009; Inoltre quando utilizziamo gli operatori unari & e *, dobbiamo ricordarci che loperatore . e > hanno sempre precedenza su di essi.

Le Basi della Programmazione struct data m; struct data *p; p = &m; p->gg = 6; (*p).mm = 4; cout << Anno: << (&m).aa << endl;

55