Linguaggio C++ Un primo semplice esempio: Calcolo dello spazio di frenata di unautomobile...

Post on 02-May-2015

306 views 7 download

Transcript of Linguaggio C++ Un primo semplice esempio: Calcolo dello spazio di frenata di unautomobile...

Linguaggio C++

Un primo semplice esempio:Calcolo dello spazio di frenata di

un’automobile

Fondamenti

Si desidera determinare lo spazio (in metri)

necessario ad arrestare un’automobile viaggiante

ad una data velocità (in km/h), ipotizzando la

decelerazione costante ed uguale a 9,2 m/s2

1. Analisi del problema

Una prima rappresentazione in pseudocodice

potrebbe essere la seguente:

Richiedi la velocità iniziale in km/hCalcola lo spazio di frenataComunica il risultato in metri

{

}

2. Scrittura dell’algoritmo

Secondo il metodo d’analisi TOP-DOWN, ogni fase

può essere considerata un sottoproblema e pertanto

elaborata ulteriormente. Mentre la prima e l’ultima

fase sono in una forma sufficientemente semplice,

per scomporre la fase Calcola lo spazio di

frenata è necessario conoscere le leggi della

cinematica relativa ai moti uniformemente

decelerati

Il modello matematico (formula) che descrive il

fenomeno del moto decelerato è il seguente:

d

vs i

2

2

1

dove:s è lo spazio necessario all’arresto (in metri)vi è la velocità iniziale (in m/s)

d è la decelerazione (9,2 m/s2)

Nel nostro caso, per poter applicare la formula

precedente è necessario convertire la velocità

iniziale da km/h a m/s. Chiamando viKmh la velocità

in km/h e con vi quella in m/s si ha:

6,3iKmh

i

vv

In conclusione, la fase Calcola lo spazio di

frenata può essere vista come:

Conversione della velocità da km/h a m/sCalcolo dello spazio

{

}

Calcola lo spazio di frenata

e, in ulteriore dettaglio:

2,92

1

6,32i

iKmhi

vs

vv

Calcola lo spazio di frenata{

}

L’algoritmo complessivo rimane comunque molto

semplice, la sola struttura di controllo utilizzata è la

SEQUENZA. Si vedrà adesso come si passa

dall’algoritmo (in questo caso in pseudocodice) al

programma in linguaggio C++

Ogni programma C++ deve sempre avere una (ed

una sola) funzione main dalla quale inizia

l’esecuzione del programma:

3. Scrittura del programma

int main()

...

...

{

}

Per funzione s’intende un insieme di dati ed

istruzioni, delimitato da una parentesi graffa aperta

{ e da una parentesi graffa chiusa }, cui è associato

un nome e che ritorna un valore. Si noti la notevole

somiglianza con la struttura utilizzata nel linguaggio

di progetto per definire un algoritmo. La parola int

che precede il nome della funzione indica che il

valore ritornato è un intero.

La traduzione del primo enunciato del nostro algoritmo:

Richiedi la velocità iniziale in km/h

sottintende, implicitamente due fasi distinte:

1. deve essere presentato sullo schermo un messaggio esplicativo affinché l’utente sia a conoscenza di quale dato richiede il programma in quel momento. Ad esempio:

Scrivi la velocita' iniziale [km/h]:

2. Il dato deve essere effettivamente acquisito e memorizzato al fine di poterlo utilizzare in un momento successivo.

Il linguaggio C++ non possiede alcuna istruzione

per la comunicazione e l’acquisizione dei dati ma,

insieme al compilatore, vengono solitamente fornite

un insieme di oggetti e funzioni che consentono di

supplire a questa mancanza. Questo insieme prende

il nome di Libreria Standard

In particolare, l’oggetto cout (console output) consente di interagire con l’unità di output standard

ovvero il video, e l’oggetto cin (console input) con l’unità di input standard, la tastiera. Scrivendo

il seguente programma:

int main(){

}

#include <iostream>

cout << "Scrivi la velocita' iniziale [km/h]: ";

using namespace std;

al momento dell’esecuzione sullo schermo comparirà il messaggio scritto tra virgolette

Il carattere punto e virgola (;) ha lo scopo di indicare al compilatore la fine dell’istruzione. Attenzione a non dimenticarlo altrimenti il compilatore interpreterà i caratteri successivi come appartenenti alla stessa istruzione, con risultati spesso imprevedibili

Le prime due righe del listato sono necessarie per interpretare e manipolare correttamente la successiva cout

cout rappresenta, simbolicamente, la destinazione standard dell’output (video) verso la quale è diretto il flusso (stream) delle informazioni presenti a destra del simbolo << ovvero dell’operatore di inserzione

A questo punto è necessario che il programma possa

acquisire e memorizzare il valore della velocità

dell’automobile.

In generale, per acquisizione s’intende l’operazione

mediante la quale un dato proveniente da un’unità

d’ingresso (in questo caso la tastiera) è memorizzato

in una variabile

Per variabile s’intende un'area circoscritta della

memoria RAM del calcolatore nella quale può

essere conservato un valore. Per semplicità, e per

potervi far riferimento quando occorre, ad ogni

variabile è associato un nome (identificatore)

In questo caso, ad esempio, la variabile che contiene

la velocità iniziale dell’automobile, espressa in km/h,

potrebbe essere chiamata arbitrariamente viKmh.

Nulla vieterebbe di chiamarla con un altro nome, ad

esempio x, ma è consigliato l’uso di un nome

mnemonico poiché permette di ricordare più

facilmente il dato che rappresenta

L’identificatore di una variabile non può essere

composto da caratteri qualsiasi ma deve essere una

sequenza di una o più lettere dell’alfabeto inglese

(maiuscole o minuscole), cifre e caratteri di

sottolineatura (underscore: _). Inoltre non può

iniziare con una cifra. Ad esempio sono corretti:

somma_numeri_Area3viKmh

Mentre non sono permessi identificatori come:

viKm/hvi Kmhvi-Kmh2vi

Il C++ distingue inoltre le lettere maiuscole dalle

minuscole (è case sensitive): identificatori con lo

stesso nome ma di formato differente sono

considerati nomi di variabili diverse

(il carattere / non è consentito)(lo spazio non è consentito)

(il carattere - non è consentito)

(non può iniziare con una cifra)

Un identificatore non può avere lo stesso nome e formato di una parola chiave (keyword) del

linguaggio. Questi identificatori predefiniti, infatti, hanno un significato speciale e non devono essere

confusi col nome attribuito alle variabili

Lo Standard prevede inoltre che le parole che

iniziano con un doppio underscore __ siano

riservate alle varie implementazioni del

compilatore.

L’insieme dei valori memorizzabili in una variabile

dipende in stretta misura dal modo in cui viene

rappresentata internamente e dal numero di byte

utilizzati. A tale proposito sono previsti dei tipi

fondamentali di variabili, ciascuno dei quali

consente di memorizzare dati appartenenti a insiemi

diversi (interi, reali e caratteri)

Nella tabella che segue è indicata la gamma dei

valori e l’insieme di appartenenza di alcuni tipi

fondamentali:

Tipo Gamma di valori Insieme

char -128 ÷ 127 Caratteri

int -2 147 483 648 ÷ 2 147 483 647 Interi

double -1,7976931410308 1,7976931410308 Reali

Per comunicare al compilatore la scelta del nome e

del tipo da utilizzare per una data variabile è

necessario una dichiarazione che, nella sua forma

più semplice, ha la seguente forma:

tipo identificatore;

Ad esempio, nel nostro caso, si potrebbe chiamare

la velocità iniziale dell’automobile viKmh e,

poiché è un valore appartenente all’insieme dei

numeri reali, associarle il tipo double:

double viKmh;

Le variabili vanno dichiarate prima di utilizzarle e le

dichiarazioni possono comparire ovunque. In questo

esempio viKmh è dichiarata prima della funzione main:

#include <iostream>

using namespace std;

double viKmh;

int main(){ ...}

A questo punto si può acquisire il valore della

velocità fornito dall’utente mediante la tastiera e

memorizzarlo nella variabile viKmh. Per farlo è

necessario fare ricorso ad un oggetto della Libreria

Standard, cin. L’oggetto cin è la sorgente

simbolica dei dati che provengono dall’ingresso

standard (la tastiera). L’operatore di estrazione >>

indica la direzione del loro flusso

#include <iostream>

using namespace std;

double viKmh;

int main(){ cout << "Scrivi la velocita' iniziale [km/h]: "; cin >> viKmh;}

Il primo enunciato dell’algoritmo, Richiedi

velocità iniziale dell’automobile in km/h,

potrà quindi essere tradotto nel seguente programma:

Si può adesso passare alla fase Calcola lo spazio di

frenata di cui riportiamo per comodità l’algoritmo:

2,92

1

6,32i

iKmhi

vs

vv

Calcola lo spazio di frenata{

}

La prima espressione:

6,3iKmh

i

vv

In C++ può essere scritta nel seguente modo:

vi = viKmh / 3.6

dove vi è un’altra variabile che sarà

opportunamente dichiarata di tipo double

Il formalismo è molto simile a quello delle normali

espressioni aritmetiche. Nel linguaggio C++ anche la

modalità di scrittura, così come i criteri adottati

nella valutazione delle espressioni, hanno origine

dall’aritmetica tradizionale

L’importanza delle espressioni nel linguaggio C++ è

rappresentata dal fatto che una qualsiasi espressione

terminata dal carattere punto e virgola ;

costituisce un’istruzione. L’azione che ne deriva

consiste semplicemente nella valutazione

dell’espressione

In generale, un’espressione è una combinazione

qualsiasi di operandi e operatori che dà origine a

un valore. Un operando è uno dei valori (costante o

variabile) che viene manipolato nell’espressione.

Poiché un operando possiede un valore, ne segue che

anche gli operandi sono espressioni

Quindi:

Sono tutte espressioni in quanto ognuna di esse

rappresenta un valore

viKmh

viKmh / 3.6

3.6

Gli operatori sono simboli (sia singoli caratteri che

loro combinazioni) che specificano come devono

essere manipolati gli operandi dell’espressione. Ad

esempio, nel rapporto:

viKmh e 3.6 sono gli operandi ed il carattere /

(slash) costituisce l’operatore di divisione

viKmh / 3.6

La valutazione di un’espressione avviene

eseguendo le operazioni indicate dagli operatori sui

loro operandi secondo le regole di precedenza degli

operatori, di norma uguali a quelle dell’aritmetica

tradizionale. Ad esempio, nell’espressione:

4 + 10 / 2

come consueto, sarà prima eseguita la divisione e poi l’addizione

Il segno = usato nell’espressione:

invece, ha lo stesso significato del segno

solitamente impiegato negli algoritmi per

rappresentare l’operazione di assegnazione.

Mediante questo operatore il valore a destra

dell’operatore = (r-value) è memorizzato

(assegnato) nell’operando di sinistra (l-value);

quest’ultimo, pertanto deve riferirsi a una locazione

di memoria modificabile

vi = viKmh / 3.6

È quindi lecito scrivere:

se x è il nome di una variabile, mentre sicuramente non avrebbe nessun senso il contrario:

x = 10

10 = x

perché 10 è un valore costante e non può essere modificato

È bene osservare che l’operatore di assegnazione non

ha lo stesso significato del segno uguale usato in

aritmetica. Pertanto, espressioni del tipo:

x = y e y = x

con x diverso da y, non sono assolutamente

equivalenti: nella prima il valore di y è

memorizzato nella variabile x; nella seconda,

invece, sarà il valore di x ad essere assegnato a y

Analogamente, mentre in aritmetica non è corretto

scrivere:

x = x + 5

in C++ è perfettamente lecito e assume il significato

di: somma alla variabile x il numero 5 e

memorizzane il risultato nella variabile x

Nell’espressione:

vi = viKmh / 3.6

compaiono quindi tre operandi e due operatori, quello

di assegnazione e quello di divisione. Poiché

quest’ultimo ha priorità maggiore del primo verrà

subito effettuata la divisione tra viKmh e 3.6 e,

successivamente, il risultato sarà memorizzato nella

variabile vi

2,92

1 2iv

s

s = 1 / 2 * vi * vi / 9.2

Si vedrà, adesso, come tradurre la seconda espressione dell’algoritmo Calcola lo spazio di frenata:

In C++ l’operatore di moltiplicazione si indica con l’asterisco *. Non esiste, invece, nessun operatore per

l’elevazione al quadrato. Pertanto, la suddetta espressione si scriverà:

1 / 2

Nonostante sembri corretta, il valore di questa

espressione sarà, tuttavia, sempre zero. Il perché di

questa singolarità sta nell’espressione:

Per valutare correttamente un’espressione, il

compilatore deve conoscere il tipo di ogni operando

coinvolto:

nel caso di variabili, il tipo è quello attribuitogli al

momento della loro dichiarazione; nel caso di

operandi costanti, invece, il tipo viene

automaticamente assegnato in base al loro valore e

come è stato specificato. Ad esempio le costanti:

10234-56789

vengono automaticamente assimilate a tipi int;

vengono considerate numeri reali: la prima eccede il massimo valore per una variabile di tipo int, la seconda è chiaramente un valore reale mentre la

terza, benché possa essere correttamente rappresentata da una variabile di tipo int, essendo stata specificata con una cifra decimale (anche se

zero), viene considerata un valore reale

mentre le costanti:

4567898586-567.489.0

In base a queste considerazioni, nell’espressione 1/2

le costanti 1 e 2 sono quindi interpretate come

operandi di tipo int.

L’operatore binario / effettua la divisione del primo

operando per il secondo. Se entrambi gli operandi

sono interi mentre il risultato della divisione non lo è,

il suo valore viene approssimato secondo le seguenti

regole:

1. Se entrambi gli operandi sono positivi o di

segno discorde, il risultato sarà troncato, cioè non

verranno considerate le cifre decimali.

2. Se entrambi gli operandi sono negativi,

l’approssimazione del risultato (troncamento

o arrotondamento) dipende dal compilatore

utilizzato

viene considerato come una divisione tra interi

positivi ed il risultato, ottenuto da quello reale

(0,5) per troncamento vale, pertanto, zero

Quindi, per le considerazioni precedentemente fatte,

il rapporto:

1 / 2

Il modo più semplice per ovviare a questo inconveniente è quello di trasformare una o entrambe

le costanti coinvolte in costanti reali, ad esempio:

1 / 2.0

Se gli operandi coinvolti in un’espressione sono

differenti, molti operatori effettuano

automaticamente delle conversioni per portarli ad

un tipo comune

Solitamente, durante questa operazione, l’operando di

tipo inferiore viene convertito in quello di tipo superiore

(che può rappresentare dei valori più alti).

Ad esempio nel rapporto precedente sono coinvolti un

operando di tipo intero (1) ed uno reale (2.0). Essendo

di tipo differente, prima della divisione, il primo (di tipo

inferiore) verrà automaticamente convertito nello stesso

tipo dell’altro, (di tipo superiore). Il risultato della

divisione sarà ancora del tipo comune, in questo caso un

reale

È possibile adesso aggiornare il programma che assume una forma quasi definitiva:

#include <iostream>

using namespace std;

double viKmh;double vi;double s;

int main(){ cout << "Scrivi la velocita' iniziale [km/h]: "; cin >> viKmh;

vi = viKmh / 3.6; s = (1 / 2.0) * (vi * vi) / 9.2; ...}

Per rendere più leggibile il programma, anziché

impiegare dei valori costanti è possibile usare delle

loro rappresentazioni simboliche. Ad esempio, in

questo problema la costante 9.2 rappresenta la

decelerazione. La formula che la contiene sarebbe

più chiara se al posto di 9.2 comparisse la lettera d

o la parola decelerazione

La dichiarazione di una costante simbolica è

analoga a quella di una variabile con la differenza

che è preceduta dalla keyword const. Poiché dopo

la dichiarazione non è possibile modificare una

costante, il suo valore deve essergli attribuito in

quell’occasione:

const double decelerazione = 9.2;

Da questo momento in poi, al posto del valore 9.2 è

possibile impiegare il nome simbolico

decelerazione. Eventuali espressioni del

programma che tentano di alterare il valore di

decelerazione saranno segnalate come errori in

fase di compilazione perché non è possibile

modificare il valore di oggetti dichiarati costanti:

decelerazione = 5.6;

A questo punto, manca solo la traduzione

dell’algoritmo Comunica il risultato in metri.

Può essere fatta utilizzando ancora l’oggetto cout

che consente di presentare sullo schermo dati e

messaggi combinati insieme. Se il valore

memorizzato nella variabile s è 48.5, scrivendo:

cout << "Lo spazio per il suo arresto e' di "

<< s << " metri" << endl;

sullo schermo apparirà:

Lo spazio per il suo arresto e' di 48.5 metri

endl, presente al termine dell’istruzione, è un

manipolatore di cout e serve a terminare la linea

introducendo un ritorno a capo. In questo caso,

eventuali altre operazioni di input/output sul

videoterminale inizieranno nella riga successiva

Infine, ecco il listato completo del programma:#include <iostream>

using namespace std;

double viKmh;double vi;double s;const double decelerazione = 9.2;

int main(){ cout << "Scrivi la velocita' iniziale [km/h]: "; cin >> viKmh;

vi = viKmh / 3.6; s = (1 / 2.0) * (vi * vi) / decelerazione;

cout << "Lo spazio per il suo arresto e' di " << s << " metri" << endl;

}

Benché questo programma risulti sostanzialmente

corretto, al fine di aumentarne la leggibilità da

parte di altri programmatori (ed anche di noi stessi,

dopo un certo periodo di tempo), è bene dotarlo di

appositi commenti che lo documentino in modo

appropriato

In un programma C++ il modo più semplice per

inserire un commento è di farlo precedere dal

doppio slash //. Tutto quello che viene scritto da

quel punto fino al termine della riga è considerato

un commento:

double s; // spazio di frenata in metri

Se il commento è molto lungo ed occupa più righe (ad

esempio per illustrare una parte del programma), è

conveniente impiegare una sintassi alternativa dove il

commento inizia con la sequenza di caratteri /* e

termina con la medesima sequenza, ma invertita, */:

/*Questo programma calcola lo spazio (in metri)

necessario ad arrestare un’automobile viaggiante ad

una data velocità (indicata in km/h) supponendo che

la decelerazione sia costante.*/

All’interno di un commento è ammesso qualunque

carattere (anche //) con l’esclusione della

combinazione */ che determina la fine del

commento stesso.

Diversamente dal commento che inizia col doppio

slash, la sintassi precedente consente di introdurre un

commento anche all’interno di un’espressione, ad

esempio:

Vi = viKmh / /* fattore di conversione */ 3.6;

I commenti non influenzano né la dimensione né la

velocità del programma eseguibile. È bene quindi,

utilizzarli frequentemente al fine di rendere più

leggibile un programma. Spesso in un programma

ben scritto, i caratteri utilizzati per i commenti sono

addirittura maggiori di quelli utilizzati per le

istruzioni

Affinché il programma appena scritto possa essere

eseguito, è necessario prima tradurlo in una forma

comprensibile al calcolatore. Questo compito può

essere svolto da appositi programmi che fanno parte

del cosiddetto software di base. I passi da seguire

possono essere schematizzati dal diagramma di

flusso che segue:

Programmasorgente

Editor

Compilatore

Linker

Librerie

Programmaeseguibile

Esempio: programma per ilcalcolo dello spazio di frenata

File sorgenteEsempio:

frenata.cpp

.obj

.lib

File eseguibileEsempio: frenata.exe

I programmi descritti possono differire a seconda

delle implementazioni.

Esistono anche delle versioni che racchiudono, nello

stesso ambiente di sviluppo, le funzionalità di tutti

questi strumenti. Esempi tipici sono gli ambienti IDE

(Integrated Development Enviroment) Microsoft

Visual C++ .NET, Dev C++ e Borland C++ Builder