IL LINGUAGGIO C++

Il C++ fu ideato dal danese Bjarne Stroustrup presso i laboratori della  AT&T nel 1979.
In quell’epoca era molto sentita l'esigenza di un linguaggio con possibilità di astrazione dai dati primitivi, che consentisse di definire nuovi  tipi di dati in modo semplice e che fosse anche adatto ad affrontare con facilità problemi di simulazione.
Stroustrup anziché sviluppare un linguaggio del tutto nuovo decise di utilizzare il C come  linguaggio base, per non perdere i vantaggi offerti sia in termini di efficienza e portabilità, sia di compatibilità con i programmi C esistenti, e di estenderne le funzioni con arricchimenti sintattici.
 

La prima versione di questa estensione del C, che prese il nome di "C con classi" includeva il concetto di classe, come strumento linguistico  necessario a creare nuovi tipi di dati astratti, che meglio potessero modellare oggetti reali, mentre la versione finale del  1986, che fu chiamata C++, oltre ad includere tutti i costrutti sintattici del C  includeva anche i concetti fondamentali della programmazione orientata agli oggetti ( OOP ).
Il programmatore C++ ha molta libertà di espressione, pertanto può scrivere programmi che riflettono ampiamente il suo stile, il suo modo di risolvere i problemi, il suo tipo di approccio all'algoritmo.
Questa caratteristica del linguaggio, che ha senz'altro riflessi positivi sull'efficienza del programma, può essere causa di  difficoltà talora insuperabili, ad esempio nei successivi interventi sul sorgente a scopo di manutenzione o di studio, quando  lo stile del programmatore sia eccessivamente criptico.

Caratteristiche:
 

Linguaggio ibrido ovvero Orientato agli Oggetti ma anche alla programmazione tradizionale
Linguaggio Portabile
Consente la gestione di bit, byte, indirizzi di memoria e di interagire con le risorse hardware della macchina
Sintassi semplice, utilizza poche parole riservate
Linguaggio strutturato

STRUTTURA DI UN PROGRAMMA C++
 

Secondo un approccio tecnico, seguito dalla maggior parte dei testi sui linguaggi di programmazione potremmo definire la struttura di un programma C++ utilizzando la notazione BNF come segue:

<programma> ::=<dichiarazioni globali>

                             <funzione> {<funzione>}

Una funzione consiste di 4 parti:

<funzione> ::=<tipo_ritorno> <nome_funz> (<elenco_argomenti>)

                        { <sequenza_istruzioni>}
 
 Andando avanti nelle definizioni, seguendo un approccio più discorsivo potremmo dire che in C++  esistono solo funzioni, una di queste deve essere sempre presente e chiamarsi main.
Il main è una funzione indispensabile il cui nome non può essere cambiato.
Ma vediamo subito un programma che scrive "Ciao Ciao!" sullo schermo:


 

#include <stdio.h>    
                                           /* Include la libreria stdio.h che contiene funzioni necessarie per le operazioni di I/O */
main() 
                                        /* Definiamo la funzione main, che non riceve nessun valore come argomento.
                                              Il programma inizierà l'esecuzione a partire da questa  funzione,
                                              che deve essere presente in ogni    programma */

{                                                                              /* Le istruzioni della funzione sono racchiuse tra le {,} */
   printf("Ciao Ciao!\n");                 /* Chiamiamo la funzione di libreria
                                                                               * printf che si occupa di stampare.
                                                                             *  \n indica il NewLine,  ossia serve per andare a capo.
                                                                            * si deve mettere un punto e virgola alla fine di ogni istruzione. */

}                                                      /* chiudiamo la funzione principale main() */
 

In questo primo programma si notano già delle cose fondamentali:
 

Ad essere pignoli anziché scrivere:

#include <stdio.h>
main()
{
  printf("Ciao Ciao!\n");
}
 

dovremmo mettere (void) come parametro del main al posto di (), infatti void significa "vuoto". Inoltre, poiché main è una funzione dovrebbe restituire un valore, pertanto dovrebbe esserci un return , ma se il return è assente, il compilatore restituisce 0 quindi dovrei specificare int prima di main.

#include <stdio.h>
int main(void)
{
    printf("Ciao Ciao!\n");
   return(0);
}
 
 
 
 
Caratteri speciali

I seguenti caratteri speciali (detti anche letterali o costanti senza identificatore ) sono utilizzati per le operazioni di stampa:

\b backspace (muove il cursore indietro di una posizione)
\f form feed (muove il cursore in avanti di una pagina logica)
\n nuovalinea (va a capo)
\r ritorno carrello  (ordina al cursore di posizionarsi all'inizio della riga)
\t tabulazioni orrizontali
\" virgolette(individuazione del carattere ")
\' apice (individuazione del carattere ')



  Un programma C++ è costituito da una sequenza di elementi (token) separati da spazi (blank).
Ogni elemento deve appartenere ad uno dei seguenti insiemi:

Dichiarazione delle variabili

<dichiarazione_variabile> ::=<tipo> <elenco_di_variabili>;

<elenco_di_variabili> ::=<ident> {,<ident>}

Gli elementi di base di un programma sono le variabili e le costanti.
In C++ le variabili devono essere dichiarate prima di essere usate.
Le variabili fondamentali che si possono definire sono di tipo: char, int, float, double e void.
 Ad esempio con char si definiscono variabili di tipo carattere, con int variabili che possono contenere solo numeri interi, con float e double variabili che possono contenere numeri reali.
Il tipo void (vuoto) come si vedrà meglio in seguito si utilizza come argomento di funzioni senza argomenti (main) o per indicare il tipo di risultato (vuoto ovvero inesistente) restituito da funzioni che non producono risultati.

Esistono altri tipi di variabile che vedremo dopo, assieme ad altre classificazioni che dividono le variabili in globali, locali ecc.

Per dichiarare una variabile basta scrivere il tipo seguito dal nome che decidiamo di associare alla variabile stessa, ad esempio:

  int c1;
 
In questo caso abbiamo dichiarato una variabile di tipo intero e di nome c1.
Si noti che abbiamo terminato la dichiarazione con un ";".
Il nome da dare alla variabile si chiama identificatore può essere scelto a piacere, rispettando alcune limitazioni.

Da notare che il C++ è case-sensitive ovvero  distingue tra maiuscole e minuscole, per cui c1 è diverso da C1.
Le parole chiave come printf() DEVONO essere scritte in minuscolo, e solitamente anche le variabili sono scritte con caratteri minuscoli, nonostante si possano usare anche caratteri maiuscoli.
La regola quindi sarebbe di scrivere le variabili a lettere minuscole, e se si vuole si può usare il carattere "_" come ad  esempio:

  int c1_prova;
 
 
  Detto questo, vediamo come assegnare un valore ad una variabile:
 
  c1 = 1000;
 
Abbiamo introdotto l'operatore di assegnamento "=", che in questo caso assegna a c1 il valore 1000.
Si noti che:

  c1 = 500+500;
  c1 = (250+250)+500;
 
sono istruzioni equivalenti, infatti si può assegnare anche un'espressione, (questa sarà calcolata in fase di precompilazione).
 

Per stampare il valore di una variabile sul video con la funzione printf() occorre aggiungere %d nel punto che ci interessa, e mettere il nome della variabile dopo la chiusura delle " ", in questo modo:

  printf("Il valore di c1 e’ %d.",c1);
 
Al momento della stampa il %d sarà  sostituito dal valore della variabile c1. Da notare la virgola che separa i doppi apici "" dalla variabile.

Esempio
 

#include <stdio.h>
                                                 /* Includiamo la libreria standard */
int c1;  
                                            /* Dichiaro c1, una variabile di tipo intero */
main()                             
                                                /* Funzione principale, eseguita per prima */
{                                                   /* Inizio della funzione main() */
 c1 = 1000;                                         /* Assegno a c1 il valore 1000 */
 printf("Il valore di C1 e’ %d.\n",c1);
 c1 = 1500;                                         /* Assegno a c1 il valore 1500 */
 printf("Il valore di C1 e’ %d.\n",c1);
}                                                       /* Fine della funzione main() */
 

Come risultato si ottiene:

Il valore di C1 e' 1000.
Il valore di C1 e' 1500.

Si noti che abbiamo usato %d per indicare che  la variabile in questione è di tipo intero. Per stampare il valore di una variabile di tipo float, dovremmo mettere %f. Vedremo in seguito altre opzioni di stampa.
Si noti che il C++ è un linguaggio case-sensitive, pertanto  se scrivessimo Printf il compilatore darebbe un errore in quanto non troverebbe la funzione nella libreria stdio.h.
 

Per dichiarare  due variabili dello stesso tipo conviene scrivere:
 
  int c1, c2;
 
anziché dichiarare singolarmente.

Per stampare più di una variabile numerica occorre mettere un %d o %f nel punto del testo che ci interessa, e mettere poi il nome della variabile dopo la stringa.

Esempio:

  printf("C1 = %d , C2 = %d\n", c1, c2);
 
Ossia si mettono i %d o %f  dove interessa, e poi la lista delle variabili che li sostituiranno nello stesso ordine, separate da virgole.
 


Tipi di variabili

Le variabili elementari possono appartenere seguenti tipi base:

 

tipi base char int float double void
a cui possono essere applicati i modificatori:
 
modificatori signed unsigned long short
 
 

il cui effetto serve a ridurre o aumentare la memoria a disposizione con  conseguente diversa possibilità di rappresentazione. I modificatori hanno effetto solo sui char e int.
 
 

char da -128 a 127
unsigned char da 0 a 255
signed char da -128 a 127
int  16  -32.768   a   32.767
unsigned int  16  da 0 a 65.535
signed int  16  -32.768  a 32.767
short int  16  -32.767 a 32.767
unsigned short int 16 da 0 a 65.535 
signed short int 16  -32.768 a 32.767
long int  32  -2.147.483.648 a 2.147.483.647
unsigned long int  32  da 0 a 4.294.967.295
signed long int  32  -2.147.483.648 a 2.147.483.647
float  32  1,2*10-38..3,4*1038 pr.7 cifre
double  64  1.3*10-308.. 0.7*10308 pr.15 cifre
long double  80  3.4*10-4932 a 1.1*104932 pr.19 cifre
 

Come si vede i tipi interi (char, int, short, long) possono essere signed e unsigned, in questo caso sono solo positivi, pertanto contengono il doppio dei valori, in quanto si evita di rappresentare quelli negativi.

I tipi in virgola mobile, ossia float, double e long double, sono comunque signed.

Se si va fuori del range di definizione di una variabile, ad esempio se si aggiunge 100 ad una variabile int che vale già 32700, il risultato è -32736!

Questo a causa dell'aritmetica in complemento a 2, che per semplificare fa "ricominciare" dal numero più basso quando si supera quello più alto... dato che non è segnalato alcun errore e il risultato erroneo può essere usato, ATTENTI A NON FAR ECCEDERE MAI I TIPI, piuttosto definite dei long, se sospettate che un int non basti.
 
 Poiché l'occupazione in byte del tipo di dati dipende dal particolare sistema il C++ dispone dell'operatore sizeof.
 

 


Operatore sizeof

sizeof(<identificatore_tipo>)

Restituisce in output la dimensione in byte del tipo di dato passato come parametro.
Es: sizeof(int) produce in output 2. 



                                     Costanti

Costanti intere

Una costante intera è un numero decimale (base 10), ottale (base 8) o esadecimale (base 16) che rappresenta un valore intero positivo o negativo.
 

Un numero con prefisso 0 è interpretato in base ottale e di tipo int.

Es. a = 0100;   (in a è memorizzato il numero 64)
 

Es. a =0x1B;   (in a è memorizzato il numero 27)
  Es.  long a = 0x1BL;  (il numero 27 è memorizzato in una locazione di  4 byte)
 
 

Costanti floating-point

Una costante floating-point è un numero decimale (base 10), che rappresenta un valore reale positivo o negativo. Può essere specificato in 2 modi:

Esempi:  15.75      -1.5e2      25E-4     10.

In qualunque notazione, se il numero è scritto senza suffissi, è assunto di tipo double. Per forzare il tipo float bisogna apporre il suffisso f (o F)
Es.  10.3 è di tipo double             1.4e-5f  è di tipo float.
 

Costanti carattere

Una costante carattere è rappresentata inserendo fra singoli apici un carattere stampabile oppure una sequenza di escape.

Esempi:
 ‘A’          carattere A
 ‘\n’         carattere newline
 ‘\003’     carattere cuoricino

In memoria un carattere occupa un byte ed è rappresentato da un numero intero di 1 byte (il suo codice ASCII).

Le conversioni fra tipo char e tipo int sono automatiche (purché il valore intero da convertire sia compreso nel range del tipo char) e quindi i due tipi possono essere mescolati insieme nelle espressioni aritmetiche. Per esempio l’operazione:
  MiaVar = ‘A’ + 1;
è ammessa, se la variabile MiaVar è stata dichiarata int oppure char.

Il carattere NULL ha codice ASCII 0 e si rappresenta con ‘\0’ (da non confondere con il carattere 0 decimale che ha codice ASCII 48).
 

Costanti stringa

Una costante stringa è rappresentata inserendo un insieme di caratteri (fra cui anche sequenze di escape) fra doppi apici (virgolette).

Es.  “Ciao \n”

In  C++ non esistono le stringhe come tipo intrinseco. Infatti, come si vedrà, esse sono definite come sequenze (array) di caratteri, con una differenza rispetto ai normali array: il compilatore, nel creare una costante stringa, aggiunge automaticamente un NULL dopo l’ultimo carattere, di conseguenza     ‘A’  e  “A”  sono due costanti diverse infatti:
‘A’  è un carattere e occupa 1 byte  (con il numero 65)
“A” è una stringa e occupa 2 byte ( con i numeri 65 e 0)
 


IDENTIFICATORI
 
 

Gli identificatori sono nomi tramite i quali e' possibile fare riferimento a varibili, funzioni, etichette ed altri oggetti definiti dall'utente.

Un identificatore è costituito da uno o più caratteri:
 

Gli identificatori possono avere qualsiasi lunghezza
Vengono riconosciute diverse le maiuscole dalle minuscole
Non possono essere usate le parole chiave che sono riservate
 
 
Parole riservate
 
asm double  new  switch
auto else operator template
break enum  private this
case extern  protected throw
catch float public try
char for register typdef
class friend return union
const goto  short unsigned
continue if  void  virtual
default inline sizeof void
delete int static volatile
do long struct while
 Si noti che quando si usa una parola riservata seguita da un identificatore va inserito almeno un blank in quanto diversamente non sarebbe possibile distinguere i due elmenti.

Esempio:  int n (esatto)  intn (errato)


Dichiarazione e Inizializzazione delle variabili

<tipo> <ident> = <costante>

Esempio

char car = 'a';

int i = 7;

float q=4.2;

float x=14e-3F ; Un numero reale può essere rappresentato anche in notazione esponenziale ,  e indica 10 elevato a esponente, quindi in questo caso equivale a 14*10-3

double r= 123.742;

long double int z = 123.742L

int k=0x80;     Il suffisso 0x indica che  il valore in input è in esadecimale
 


Assegnamento di valori alle variabili

<ident> = <costante>|<espressione>

Ricordando che %d si usa per stampare una variabile int, mentre %f si usa per stampare una variabile float, facciamo un esempio pratico:

#include <stdio.h>             /* Includiamo la libreria standard */

int c1, c2;                             /* Dichiaro 2 variabili di tipo intero */
float a, b;                             /* Dichiaro 2 variabili di tipo float */

main()                               /* Funzione principale, eseguita per prima */
{                                   /* Inizio della funzione main() */

 c1 = 1000;                     /* Assegno a c1 il valore 1000 */
 c2 = c1 + 500;             /* Assegno a c2 il valore di c1 + 500 */
 a = 21.06;                     /* Assegno ad a il valore 21,06 */
 b = 3.1415;             /* Assegno a b il valore 3.1415 */

                                /* Stampo il valore di 2 variabili di tipo int con un solo printf() */

 printf("C1 = %d , C2 = %d\n", c1, c2);

                            /* Stampo il valore di 2 variabili di tipo float con un solo printf() */

 printf("A = %f , B = %f\n", a, b);

                            /* Stampo il valore di tutte le 4 variabili con un solo printf() */

 printf("C1=%d,C2=%d,A=%f,B=%f\n",c1,c2,a,b);

}                               /* Fine della funzione main() */
 
 

Si che in C++ le assegnazioni producono come risultato (oltre alla assegnamento vero e proprio)  il valore che si vuole asegnare, pertanto  è possibile effettuare assegnazioni multiple purche' le variabili siano dello stesso tipo.
  Ad esempio:
 
        int somma;
        main()
          {
            int a,b,c=3;     /* equivalente a c=3; b=c; a=c*/
            a=b=c;
          }
 



 

 OPERATORI ARITMETICI

Gli operatori aritmetici fondamentali sono:

- sottrazione e meno unario

+ addizione

* moltiplicazione

/ divisione

% (modulo) resto della divisione intera (non puo' essere usato sui tipi float e double )
 

L'operatore "%" (modulo) può essere utilizzato solamente con le variabili di tipo integer;

L'operatore "/" e' utilizzato sia per la divisione tra interi che per i reali.

A proposito della divisione si osservi che   z=3/2 assegnerà a z il valore 1, anche se e' stato dichiarato come float
in quanto di regola, se entrambi gli argomenti della divisione sono interi,  allora verrà effettuata una divisione intera;
per avere il risultato corretto sarà necessario scrivere:
        z = 3.0/2          oppure
        z = 3/2.0     o, ancora meglio,    z = 3.0/2.0

 
 
 
 
 



Notazione abbreviata

Esiste una notazione abbreviata per assegnare valori ad una variabile in  espressioni del tipo
 
 

<variabile1> = <variabile1> <operatore> <espressione>

equivalenti a:


<variabile1> <operatore>=<espressione>

 

quindi ad esempio:

   c2 *= c1;       ->      c2 = c2 * c1;
   c2 /=  c1;       ->      c2 = c2 / c1;
   c2 %= c1;     ->      c2 = c2 % c1;
   c2 += c1;      ->      c2 = c2 + c1;
   c2 -= c1;      ->       c2 = c2 - c1;
 
 
 

Quando cioè ad una variabile si assegna il valore della stessa variabile +,-,*,/,% qualche altra variabile, o costante,o espressione, anziché riscrivere il suo nome si può abbreviare in questo modo.

Nota: l'espressione x*=y+3 corrisponde a x=x*(y+3) e non a x=x*y+3.
 

 
  OPERATORI di INCREMENTO/DECREMENTO

 

++ incremento
-- decremento
 

Come già accennato, le assegnazioni in C++ vengono effettuate utilizzando   "=".
Ma oltre agli operatori arimetici standard +,-,*,/  e all'operatore %  (modulo) per gli interi, in C++ si hanno anche gli operatori incremento ++  e decremento -- (il cui effetto produce un incremeto o decremento di 1 sulla variabile a cui si riferiscono) che possono essere preposti o posposti alla variabile.
Se  sono preposti (es: ++z)  l'incremento viene effettuato prima che sia valutata l'intera espessione ,   mentre se sono posposti il prima viene calcolato l'intera espressione e dopo viene effettuato l'incremento.

Ad esempio:
                  int x,z=2;

         1)       x=(++z)-1;
                         A questo punto x=2 e z=3
 
                  int x,z=2;
         2)       x=(z++)-1;
                         A questo punto x=1 e z=3
 
 

E' importante sottolineare che un'istruzione del tipo   x++ e' piu' veloce della corrispondente  x=x+1
 


 

- (unario)

* / %

+ -
 

 
Le parentesi tonde si usano per costituire espressioni, come ad esempio:

  c2 = (a-30+c1*(c++)/-(c));

   c2 = (a-30+c1*(c2+b))/(c1*5);
 
ove si intende imporre un diverso ordine di priorità agli operatori.
 

Le parentesi quadre sono usate per  l'indicizzazione degli array e le parentesi  graffe la definizione di inizio e fine di una funzione.
 

Le espressioni vengono valutate in base alla priorità degli operatori.
A parità di priorità vengono svolte da sinistra verso destra.

E’ importante che le variabili e costanti che compaiono nell’espressione e la variabile cui viene assegnato il risultato siano tutte dello stesso tipo.

In caso negativo il compilatore provvede ad effettuare una conversione di tipo automatica.
Per maggior chiarezza si consiglia di esplicitare la conversione di tipo tramite l’operatore apposito cast.


Il  C++ esercita un forte controllo sui tipi e da messaggio di errore quando si tenta di eseguire operazioni fra operandi di tipo non ammesso.

Es. l’operatore % richiede che entrambi gli operandi siano interi.

I quattro operatori matematici si applicano a qualsiasi variabile di tipo base, ma i tipi dei due operandi devono essere uguali. Tuttavia, nel caso di due tipi diversi, il compilatore esegue una conversione di tipo implicita su uno dei due operandi, adeguando il tipo più semplice a quello più complesso, secondo la seguente gerarchia (in ordine crescente di complessità):

char - short - unsigned short - long - unsigned long - float - double

Es.  nell’operazione   3.4/2  il secondo operando è trasformato in 2.0 e il risultato è correttamente 1.7

Nelle assegnazioni, il tipo dell’operando di destra viene sempre trasformato implicitamente nel tipo dell’operando di sinistra (con un messaggio warning se la conversione potrebbe implicare perdita di dati (loss of data ), trasformando un tipo più complesso in un tipo più semplice).

Es.  date le variabili  char c  e  double d,  l’istruzione  c = d  è ammessa, ma genera un messaggio warning in fase di compilazione.

Nelle operazioni fra tipi interi, se il valore ottenuto esce dal range (overflow), l’errore non viene segnalato.
Accade la stessa cosa se l’overflow si verifica a seguito di una conversione di tipo.
Es.  short n = 32767;
  n++;
(l’errore non viene segnalato, ma in n si ritrova il numero -32768)
 



                             Conversioni di tipo esplicite - CASTING
 
 

Quando si vuole ottenere una conversione di tipo che non verrebbe eseguita implicitamente, bisogna usare l’operatore unario di casting (o conversione esplicita), che consiste nell’indicazione del nuovo tipo fra parentesi davanti al nome della variabile da trasformare.
Es. se la variabile n é di tipo int, l’espressione (float)n  trasforma il contenuto di  n  da int in float.

In C++ si può usare anche il formato funzione:
 float(n) é equivalente a  (float)n

Tutti i tipi base del C++ consentono il casting (con warning di possibile loss of data se l’operazione trasforma un tipo più complesso in uno più semplice), fermo restando il fatto che, se la variabile da trasformare è operando di una certa operazione, il tipo risultante deve essere  fra quelli ammissibili (altrimenti viene generato un errore in compilazione).
Per esempio:  float(n) % 3  é errato in quanto l’operatore % ammette solo operandi interi.

Vediamo ora un esempio in cui si evidenzia la necessità del casting:
 int m=10, n=4;
 float r, a=2.7F;
 r = m/n+a;
nell’ultima istruzione la divisione è fra due numeri interi e quindi, essendo i due operandi dello stesso tipo, la conversione implicita non viene eseguita e il risultato della divisione è il numero intero 2; solo successivamente questo numero viene convertito in modo implicito in 2.0 per essere sommato ad a.

Se vogliamo che la conversione a float avvenga prima della divisione, e che questa fornisca il risultato esatto (cioè 2.5), dobbiamo convertire esplicitamente almeno uno dei due operandi e quindi riscrivere così la terza istruzione:
 r = (float)m/n+a;  (non servono altre parentesi perchè il casting ha la precedenza sulla divisione)
 
 



   
Abbiamo visto fin ora come definire e modificare e stampare delle variabili ma non ancora come immettere deivalori da tastiera durante l'esecuzione del programma.

Per leggere i dati dalla tastiera e memorizzarli nelle variabili  si può utilizzare la  funzione scanf() ,il suo formato è simile al printf():

 scanf("stringa di controllo", &variabile);

Se vogliamo leggere un numero nella stringa di controllo si deve mettere un %d se si tratta di intero e %f se si tratta di vuole  un numero reale mentre %c se vogliamo leggere un carattere.

Oltre allo specificatore di formato %d,%f ecc., non si deve però mettere altro, attenzione!

Altri caratteri sarebbero interpretati come "caratteri da scartare" in lettura.

Prima di ogni operazione di lettura da input conviene sempre stampare  un messaggio:

 printf("Immetti un numero\n");                 (stampiamo il messaggio)
 scanf("%d",&c1);                              (leggiamo il numero, e lo salviamo)
 

Si noti che, a differenza della printf, il nome della variabile deve essere preceduto dal carattere "&".
& permette di riferirci  all'indirizzo della variabile,  il motivo verrà spiegato nel paragrafo dei "puntatori".
 



Esempio:
programma che richiede una coppia di numeri, li somma e visualizza il risultato:
 
#include <stdio.h>

float a,b,c; 
                    /* Definisco 3 variabili di tipo float */

main()
{
 printf("\nDammi il primo numero: ");
 scanf("%f", &a);

 printf("\nDammi il secondo numero: ");
 scanf("%f", &b);

 c = a+b;

 printf("\%f + %f = %f\n",a,b,c);


 

Si noti che è possibile  limitare il numero delle cifre da stampare  modificando un po' %f  ad esempio:
 %6.4f  stampa un numero con 6 cifre totali di cui 4 dopo il punto.
%.4f lunghezza totale non fissata ma 4 cifre dopo il punto.
%2.0f lunghezza totale di 2 cifre e 0 cifre dopo la virgola.

 

#include <stdio.h>
int a,b,c;

main()
{
 printf("\nDammi 2 numeri: ");
 scanf("%d%d",&a,&b);
 c = a+b;
 printf("\n%d + %d = %d\n",a,b,c);
}
 

 


                            USO DELLE DIRETTIVE
 

Un programma C++  prima della compilazione viene elaborato da un preprocessore che si occupa di eseguire le direttive.
 

Il preprocessore crea una copia del file sorgente (da far leggere al compilatore) e, ogni volta che incontra una direttiva, la esegue sostituendola con il risultato dell’operazione.
Pertanto il preprocessore, eseguendo le direttive,  non produce codice binario, ma codice sorgente per il compilatore.

Le direttive più importanti sono: #include (per includere file) e #define (per definire costanti o macro)

Abbiamo già visto che  #include <stdio.h> serve per includere nel programma dei file (in questo caso  utili per eseguire le operazioni di I/O).
 

  La direttiva #include viene usata quasi esclusivamente per inserire gli header-files (.h).

La direttiva #define invece serve a definire costanti (o macroistruzioni che vedremo successivamente).



Direttiva  #define di una costante

Quando il preprocessore incontra la seguente direttiva:

  #define  <identificatore>  <valore>

dove, <identificatore> é una parola costruita secondo le stesse convenzioni dei nomi delle variabili, e <valore> é un’espressione qualsiasi, delimitata a sinistra e destra da blank (o caratteri di tabulazione o controllo) sostituisce <identificatore> con <valore>  in tutto il file (da quel punto in poi).

Es.  #define   GIORNI   365

da questo punto in poi, ogni volta che il programma deve usare il numero 365, si può utilizzare in sua vece l’identificatore GIORNI

In generale la direttiva #define serve per assegnare un nome o identificatore a una costante predefinita.

In pratica la direttiva #define produce gli stessi risultati dello specificatore di tipo const; al posto della direttiva dell’esempio precedente si sarebbe potuto scrivere la dichiarazione:

const  int  GIORNI= 365 ;
 
 

 

Operazioni di sostituzione di questo tipo sono utili sia per rendere più leggibili i programmi, (sostituendo i numeri con parole che si riferiscono al significato di tale numero) sia per renderli parametrici.
Infatti, se dovessimo modificare un valore costante, dovremmo ricercarlo e cambiarlo in tutti i punti del programma in cui compare, mentre se lo definiamo come costante o con  #define, basterà cambiare il suo valore una sola volta nella definizione per cambiarne automaticamente il valore in tutto il programma.

Si noti che, come suggerisce lo standard,  conviene che per gli identificatori delle define siano  usati caratteri MAIUSCOLI, (ciò contribuisce a distinguere variabili da costanti).

Vediamo altri esempi:
 
  #define NUM_PAGINE 400
  #define NOME_AUTORE Dante
  #define TITOLO Divina_Commedia
 
  Quando il preprocessore trova nel programma:
 
  NUM_PAGINE
  NOME_AUTORE
  TITOLO
 
sostituirà prima di iniziare la compilazione gli identificatori con le costanti:
 
  400
  Dante
  Divina_Commedia
 



Esempio

Programma che calcola l'area e la circonferenza di un cerchio dato il raggio

Come è noto le operazioni da fare sono:
 Area =PG*raggio*raggio
 Perimetro = 2*PG*raggio

Dato che  PG è un numero reale (3,141592... ecc.) costante possiamo usare il #define per associare 3.14159265 all'identificatore più comodo "PG".

Si ricordi,  che PG non è una variabile, ma un identificatore di costante che sarà sostituito fisicamente, dopo la precompilazione, con 3.14159265.
 

#include <stdio.h>                 /* Includiamo la libreria standard */
 
#define PG 3.14159265             /* Scrivendo PI intendiamo 3.14159265 */
 
float raggio;                    /* Definisco una variabile di tipo float */
 
main()                       /* Funzione principale, eseguita per prima */
                            
 printf("\nRaggio del cerchio? ");
 scanf("%f", &raggio);
 printf("\nArea: %f", PG*raggio*raggio);
 printf("\nPerimetro: %f\n", 2*PG*raggio);
}
 
 
Come si può osservare in questo programma abbiamo definito solo una variabile: raggio, in quanto per risolvere il problema non era necessario memorizzare area e perimetro.
In questo esempio abbiamo potuto verificare come tramite la printf si può stampare  anche  il risultato di espressioni, e non solo semplici variabili .
 
 


 
Iterazione for while do...while    
Selezione if if   else if   else     if ? switch
Salto return break continue goto exit
 


 
 

Istruzioni iterative

iterazione 

for 

while 

while do 

 

                                               Istruzione iterativa for

Ora vedremo come eseguire un ciclo, ossia come ripetere l'esecuzione di una istruzione per un numero  determinato di volte.
Per prima utilizzaremo l'istruzione for, che ha questa forma:

for (inizializzazione;condizione;incremento) <istruzione del ciclo>;
 

In generale, possiamo dire  test della condizione viene eseguito prima di entrare nel ciclo ed ciclo viene eseguito se la condizione è vera, qualsiasi essa sia, e si ferma quando diviene falsa.
 
#include "stdio.h"
void main(void)
{int x;
for(x=1; x<=100; x++)
    printf("%d",x);
}

Inizialmente diamo a x il valore 1, poi prima di ogni ciclo effettuiamo il test ovvero controlliamo se x<= 100, in caso positivo eseguiamo il printf, incrementiamo x di 1 e torniamo al test, altrimenti se non è soddisfatto non entriamo nel ciclo e passiamo all'istruzione successiva alla for.
 
 

 
 

Si noti che: Nel nostro caso  è indifferente, vedremo in seguito casi in cui questa differenza è significativa.
 

#include "stdio.h"
void main(void)
{int x;
for(x=100; x>=0; x--)
    printf("%d",x);
}
 
x-- equivale a x=x-1
 

#include "stdio.h"
void main(void)
{int x;
for(x=1; x<=10; x+=5)
     {
        printf("%d",x);
        printf("\nIl valore di x e’ %d.\n",x);
     }
}

x+=5 equivale a x=x+5
Output:
1
Il valore di x e' 1.
6
Il valore di x e' 6.
 
 

#include "stdio.h"
void main(void)
{int x;
  int y;
for(x=1, y=1; x+y<=100; x++, y+=6)
    printf("%d\n",x+y);
}
Output:
2
9
16
23
30
37
44
51
58
65
72
79
86
93
100
 



loop senza fine

for (;;) printf("questo loop non finisce mai")
 

 /* programma C++ che illustra il calcolo delle potenze negative di 2 */

#include <stdio.h>

main()
{
  const int MIN_VAL = 2;
  const int MAX_VAL = 100;
  const int INCR_VAL = 2;
  double x;
//uso la variabile i internamente al blocco della istruzione for
//con la particolare gestione dell’indice posso calcolare le potenze negative di 2
  for (int i = MIN_VAL; i <= MAX_VAL; i *= INCR_VAL)
{
    x = 1/(double)i;
     printf("1/ %d = %f \n”, i , x);
   }
}

output:
1/ 2 = 0.500000
1/ 4 = 0.250000
1/ 8 = 0.125000
1/ 16 = 0.062500
1/ 32 = 0.031250
1/ 64 = 0.015625
 

#include <stdio.h>

main()
{
  const int MIN_VAL = 2;
  const int MAX_VAL = 100;
  const int INCR_VAL = 2;
  double x;

  for (int i = MIN_VAL; i <= MAX_VAL; i *= INCR_VAL)
{
    int y=3;
    x = 1/(double)i;
    printf("1/ %d = %f, %d \n”, i , x, y);
   }
}
 

Osservazioni
 



 

                                                  OPERATORI RELAZIONALI


> maggiore

>= maggiore uguale

< minore

<= minore uguale

== uguale
!= diverso

 



                                    Istruzione iterativa while

Ora vedremo come eseguire un ciclo, ossia come ripetere l'esecuzione di una istruzione o più istruzioni  per un numero indeterminato di volte.
Per questo scopo utilizzaremo l'istruzione while (quando), che ha questa forma:

while (condizione) <istruzione del ciclo>;

  #include <stdio.h>
main()

{
int c1;
    printf("Inserire il valore di C1>10 \n");
     scanf (“%d”,&c1);
     while (c1<=10)
       {
        printf("Valore di C1 non valido\n");
        printf("Inserire il valore di C1>10 \n");
       scanf (“%d”,&c1);
       }
     printf("Il valore di C1 e' %d.\n",c1);
}
 

Analogamente alla for una while con condizione vuota (sempre vera) corrisponde a un loop.
 
 
 
 
 

 



 

                                      Istruzione iterativa do ... while

Ora vedremo come eseguire un ciclo, ossia come ripetere l'esecuzione di una o più istruzioni  per un numero indeterminato di volte eseguendo prima le istruzioni del ciclo e poi il test di uscita.
Per questo scopo utilizzaremo l'istruzione  do ...  while (quando), che ha questa forma:

do <istruzione del ciclo>; while (condizione);
 

  esempio

#include <stdio.h>
main()
{
int c1;
     do
     {
       printf("Inserire il valore di C1>10 \n");
       scanf (“%d”,&c1);
      }
      while (c1<=10);
}
 
 

Programma che calcola il fattoriale di n>0

#include <stdio.h>
main()
{
int n,i,f;
     do
     {
       printf("Inserire il valore di n>0 \n");
       scanf (“%d”,&n);
      }
      while (n<=0);
 i=n;
 f=1;
 do
     {
        f*=i;        /*  f=f*i   */
        i-=1;         /*  i=i-1   */
      }
      while (i>1);
 printf("fattoriale di %d = %d  \n",n,f);
}

Avendo definito le variabili di tipi int potrà verificarsi che per valori grandi di n si ottengono valori negativi per il fattoriale, a causa dell'overflow che non viene segnalato!

Analogamente alla for una do .. while con condizione vuota (sempre vera) corrisponde a un loop.
 
 
 
 


Istruzioni condizionali

Selezione 

if 

if  else 

if  else if

  ?  switch


 L'istruzione "if" ha le stesse funzioni degli altri linguaggi. Può avere   tre forme di base:

Selezione if 
if ( <condizione>) <istruzione>;

 "if" in inglese significa "se", quindi  una possibile traduzione è "se la condizione è vera, esegui  l'istruzione (o il blocco di istruzioni), altrimenti no.".
 
 

 
 

Si noti che valutare una condizione significa calcolare un valore  che può essere vero o falso, in C++ alla condizione falso viene associato il valore 0 mentre a vero viene associato diverso da 0 (=1).

Quindi un if(0) non farebbe mai eseguire l'istruzione, mentre un if(1) la farebbe eseguire sempre.
 
 

Esempio:

#include <stdio.h>
main()
{
   int a;
        /* Definiamo una variabile intera a che servirà
        per il costrutto if */

     printf("\nDammi un intero>10: ");
     scanf("%d",&a);
     if (a>10) printf("\na è maggiore di 10\n");

}

 



Selezione if   else
 
if ( <condizione>) <istruzione1>; else <istruzione2>;

Il costrutto "if " in questo caso ha un ramo opzionale, chiamato "else"(altrimenti), la traduzione potrebbe essere: "se la condizione è vera, esegui istruzione1, altrimenti esegui istruzione2". Quindi l'esecuzione di un'istruzione esclude l'altra.
 

#include <stdio.h>
main()
{
  int a;
                    /* Definiamo una variabile intera a
                    che servirà  per il costrutto if */

     printf("\nDammi un intero : ");
     scanf("%d",&a);
     if (a>10) printf("\n a  è maggiore di 10\n");
     else printf("\na è minore o uguale di 10\n");

}
 

Naturalmente è possibile eseguire anziché una istruzione un blocco di istruzioni.
 
 



Selezione if   else if

if ( <condizione1>) <istruzione1>;
                    else if ( <condizione2>) <istruzione2>; else <istruzione3>;
 
 

 (if annidati)
Per completare l'istruzione if, vedremo come  mettere una istruzione if all'interno di altre istruzioni if (if annidati). Ossia fissare più di una condizione per l'esecuzione delle istruzioni. Ad esempio:
 
     if (a>10)
     {
       if (a<20)
           printf("\na è compreso tra 11 e 19\n");
     }
     else printf ("\na non mi interessa\n");
 
 

if (a>10)
     {
       if (a= =20)
           printf("\na è vale 20\n");
     }
     else printf ("\na non mi interessa\n");
 

Osservazione: Attenzione a non scrivere   if (a = 20), in quanto tenendo conto che:
 

in tal caso anche a fronte di un input diverso da 20 (ma >10) verrebbe assegnato ad a il valore 20, e poiché in C++ ogni assegnazione produce come output il valore da assegnare alla variabile (in questo caso 20) tale valore sarebbe utilizzato per valutare l' espressione logica che risulterebbe vera in quanto  produce un valore diverso da 0, pertanto di conseguenza sarebbe eseguita la printf!
 
Se l’istruzione controllata da un if consiste a sua volta in un altro if  (sono possibili più istruzioni if  “annidate”), ogni eventuale else si riferisce sempre all’if immediatamente superiore (in assenza di parentesi graffe).

Per essere sicuri di ottenere quello che si vuole, mettere sempre le parentesi graffe, anche se sono ridondanti.
 


  Operatore "?"

 
L' operatore condizionale ? (ternary condition) e' la forma piu' efficiente per esprimere  semplici if, ha la seguente forma:
 
      <variabile> <condizione> ? <risultato1> : <risultato2>;
 
 che equivale a:

   if ( <condizione>) <variabile>  =<istruzione1>
                                else <variabile>  =<istruzione2> ;
 
  Ad esempio:
 
      z=(a>b) ? a : b

 equivale a:
 
         if (a>b)
           z=a;
         else
           z=b;
 
     ovvero  assegna a z il massimo tra a e b.

 



Selezione switch

 La selezione switch serve ad effettuare scelte multiple.   La sua forma generale e':
 

 switch (variabile){

                    case <costante1>: <sequenza di istruzioni1>;
                                                                            break;
                    case <costante2>: <sequenza di istruzioni2>;
                                                                            break;
                                ....
                    case <costanten>: <sequenza di istruzionin>;
                                                                            break;
                    default   : <sequenza di istruzioni>;
                                                                            break;

                            }

 Il break serve per terminare lo switch dopo l'esecuzione di una scelta,  altrimenti verrà valutato anche il caso successivo (questo, a differenza  di molti altri linguaggi).
 
E' possibile anche avere un'istruzione nulla, includendo solamente un ";"  oppure lasciando fallire l'istruzione di switch omettendo qualsiasi frase.

Il caso "default" e' facoltativo e raggruppa tutti i casi diversi da quelli testati con case.

In genere  lo switch si usa per creare una  menù di selezione e per controllare le varie scelte.

Esempio:

#include <stdio.h>
main()
{
int scelta=1;
                    /* Definiamo una variabile intera
                    scelta che servirà  per il ciclo while */

while(scelta!=0)
                    /* Se scelta è diverso (!=) da zero, esegui */
    {
 printf("\n Scegli il canto dell'inferno che ti interessa:\n\n");
 printf(" 1)  Canto primo. \n");
 printf(" 2)  Canto secondo. \n");
 printf(" 3)  Canto terzo. \n");
 printf(" 4)  Canto quarto. \n");
 printf(" 5)  Canto quinto. \n");
 printf(" 0)  Esci\n\n");
 scanf("%d",&scelta);            /*Leggiamo da input scelta*/
 switch(scelta)              /* e usiamo scelta per il confronto */
 {
    case 1:
       printf("\nNel mezzo del cammin di nostra vita\n");
       printf("Mi ritrovai per una selva oscura,\n");
       printf("Che la diritta via era smarrita.\n");
       break;
    case 2:
       printf("\nLo giorno se n'andava, e l'aer bruno\n");
       printf("Toglieva gli animai, che sono in terra.\n");
       break;
    case 3:
       printf("\nPer me si va nella città dolente,\n");
       printf("Per me si va nell'eterno dolore,\n");
       printf("Per me si va tra la perduta gente.\n");
       break;
    case 4:
       printf("\nRuppemi l'alto sonno nella testa\n");
       printf("Un greve tuono, si ch'io mi riscossi,\n");
       printf("Come persona che per forza e' desta\n");
       break;
    case 5:
       printf("\nCosi' discesi del cerchio primaio\n");
       printf("Giu' nel secondo, che men loco cinghia,\n");
       break;
    case 0:
       printf("\nUscita.\n");
    default:
      printf("\nSelezione sbagliata!\n");
 }                   /* Fine del costrutto switch */
    }                     /* Fine del loop while */
}    /* Fine della funzione main() */
 
 

Si noti che che lo switch può verificare solo l'uguaglianza, a differenza degli if che possono verificare condizioni qualsiasi.
 



 
 

Istruzioni di salto

Salto 

return 

continue 

goto 

break 

exit 



 return

L'istruzione return, come vedremo meglio durante lo studio delle funzioni, indica con il proprio argomento quale valore deve essere restituito dalla funzione al termine della sua esecuzione.



 goto

L'istruzione goto è detta "di salto incondizionato", perché quando viene eseguita il controllo passa immediatamente alla  istruzione relativa all'etichetta.
L'etichetta è l'identificatore di una istruzuione, pertanto precede l'istruzione a cui è riferita e è da questa separata tramite due punti.

E' però possibile saltare ad una etichetta solo se si trova all'interno della stessa funzione in cui si trova la goto; non sono consentiti salti interfunzione.

L'uso della goto è assolutamente sconsigliato in quanto il programma facilmente diventa non strutturato!
La giustificazione più usuale all'uso di goto  è relativa alla possibilità di uscire immediatamente da cicli annidati al verificarsi di una data condizione, ma anche in questi casi è preferibile utilizzare metodi alternativi.
                      ......
                  goto <etichetta >;
                   ...
<etichetta>: <istruzione>;

Esempio
#include <stdio.h>
main()
{
      int a;
      printf("\nDammi un intero : ");
 ciclo:   scanf("%d",&a);
       if (a>10) goto stampa;
        else
        {
        printf("\na e’ minore o uguale di 10\n");
        goto ciclo;
        }
stampa:  printf("\n a  e’ maggiore di 10\n");

}
 



 break , continue

Le istruzioni break e continue vengono usate per la terminazione di un ciclo o di un costrutto switch.
 
break    - permette di uscire da un ciclo  o da uno switch
continue - fa terminare l'esecuzione di una iterazione del ciclo e saltare nuovamente alla valutazione della condizione del ciclo.

Nel caso di cicli o switch nidificati il comando si riferisce al ciclo o switch più interno in cui sono contenuti i comandi.

esempio:
 

 
Consideriamo come esempio il seguente frammento di programma ove viene letto un valore intero ed elaborato purche'  sia maggiore o uguale di 0.
 
#include <stdio.h>
int main(void)
{
int value;
while (scanf("%d",&value) >=0)
    {
       if (value==3)
          {
           printf("Valore 3 = %d\n",value);
           continue;
               /* Torna nuovamente all'inizio del ciclo senza
                   eseguire la prossima istruzione */
           };
       if (value==1)
         {
           printf("Valore 1 = %d\n",value);
           continue;
                  /* Torna nuovamente all'inizio del ciclo senza
                   eseguire la prossima istruzione */
        };
        if (value==0)
        {
           printf("Valore = %d\n",value);
         break;  /* Abbandona il ciclo * /
        };
    }
}
 

 
Si noti che l'effetto di break e continue è simile a quello dell'istruzione goto pertanto se ne sconsiglia quando è possibile l'uso.



salto exit
 

L'istruzione exit produce la terminazione dell'intero programma e il ritorno al  sistema operativo.