Linguaggio C
Il C è un linguaggio di programmazione di alto livello. Progettato e realizzato
Dennis Ritchie (1941-2011) nel 1972, fu sviluppato presso gli AT&T Bell
Laboratories (New jersey, USA) per ottenere un linguaggio adatto all'implementazione
dei sistemi operativi.
Utilizzato per la prima volta in modo estensivo per la riscrittura del codice
del sistema operativo UNIX dal linguaggio assembly, può essere impiegato
con tutti i principali sistemi operativi oggi disponibili.
Stephen C. Johnson, alla metà degli anni sessanta, scrisse un compilatore
C trasportabile su sistemi diversi dal PDP-11 (Programmer Data Processor-11,la
serie di computer su cui furono sviluppate le prime versioni di UNIX): da
allora il C cominciò a essere utilizzato come linguaggio in altri sistemi
operativi,come MS-DOS (Microsoft Disk Operating System) e CPM-80 (Control
Program for Microprocessor-80). Per combinare efficienza e potenza di un
linguaggio tendenzialmente compatto, il C non possiede al suo interno alcune
funzionalità tipiche di altri linguaggi di programmazione, per esempio la
gestione del monitor e della tastiera, che sono garantite dalla disponibilità
di librerie separate da utilizzare all'occorrenza. Questa soluzione permette
tra l'altro una maggiore flessibiltà, in quanto consente al programmatore
di decidere se impiegare le funzioni esistenti o se implementarne di proprie,
sfruttando gli elementi di base messi a disposizione dal linguaggio. Tutto
ciò, unito a una grande facilità d'uso,ha contribuito alla diffusione dl
C e per evitare il proliferare altrimenti inevitabile di "dialetti", nel
1983 fu istituito un comitato ANSI (American National Standards Institute)
per uno sviluppo standard del linguaggio C,aggiungendo anche importanti
elementi e ufficializzando molte caratteristiche presenti nei diversi compilatori
realizzati. A conclusione dei lavori, il comitato produsse un documento
di specifiche, il Draft Proposed ANSI- programming language C (comunemente
indicato come ANSI C), che definisce uno standard, ossia un insieme di criteri
ai quali uniformarsi per essere conforme.
Linguaggio C++
Sebbene il linguaggio C possegga tutti gli strumenti per implementare qualsiasi tipo di dato astratto, tale implementazione è demandata interamente al programmatore. il C++ nacque per l'esigenza di una maggiore astrazione dei dati, che consentisse di definire dei nuovi tipi in modo semplice per una soluzione di numerosi problemi con la stessa efficienza e portabilità del C.
Il suo ideatore fu il danese Bjarne Stroustrup, che insieme ai fondatori di C e UNIX, nei laboratori della AT&T, iniziò nel 1979 a gettare le basi di un nuovo linguaggio che mantenesse il C come linguaggio base, per garantirne i vantaggi di efficienza e portabilità e assicurare la compatibilità con l'elevato numero di programmi C esistenti.
La prima versione prese il nome di "C con classi", poichè le "classi" sono lo strumento linguistico che consente di creare nuovi tipi di dati astratti da utilizzare come "modelli" degli oggetti reali, ralizzando così l'auspicata astrazione dei dati.
in pochi anni, il "C con le classi" assimilò i concetti fondamentali della programmazione orientata agli oggetti(OOP, Object Oriented Programming) e nel 1986 divenne C++, accolto con grande entusiasmo e sopratutto in ambiente universitario e di ricerca. nel 1990 fu pubblicato il volume "The annotated C++ reference", che rappresenta di fatto il documento proposto dal comitato ANSI e definisce lo standard del nuovo linguaggio.
Il C++ mantiene tutte le virtù del C, ovvero è uno strumento pratico e funzionale, versatile e potente, adatto a realizzare applicazioni di qualsiasi tipo dotato della possibilità di interagire direttamente con le risorse hardware della macchina, ma con una struttura della sintassi tipica dei linguaggi di programmazione di alto livello. Questo elevato grado di compatibilità con il C (di cui conserva semantica e sintassi) consente al programmatore C++ di passare progressivamente dalla programmazione strutturata/procedurale alla programmazione orientata agli oggetti senza la necessità di riscrivere da capo tutto il codice esistente, riutilizzando gli innumerevoli programmi e librerie già esistenti.
Per spiegare successo che C++ ha avuto nel corso del tempo si possono fornire le seguenti motivazioni.
● Tutti i problemi affrontabili in C sono anche in C++
● Gli ampliamenti introdotti rispetto al C rimediano le principali carenze del vecchio linguaggio.
● L'introduzione del nuovo standard ha migliorato la possibilità di scrivere applicazioni facilmente portabili su sistemi diversi.
● E' efficiente e di conseguenza viene utilizzato in tutte le situazioni dove la velocità di risposta diventa un fattore critico.
● Il C++ è uno dei più diffusi linguaggi di programmazione; questo vuol dire che sono già disponibile una quantità notevole di librerie, moltissimi segmenti di codice già pronte all'uso, un gran numero di driver e di periferiche, una bibliografia immensa, numerosi compilatori e debugger.
● E' uno tra i linguaggi più flessibili: con il C++ si può scrivere praticamente qualsiasi software, dal sistema operativo alla piccola applicazione.
● Essendo uno dei linguaggi più conosciuti e usati dai programmatori, ha dei compilatori molto affidabili e i programmi che ne risultano sono stabili(sempre che siano scritti correttamente) e veloci in esecuzione, su tutte le piattaforme.
● E' un buon compromesso tra semplicità di scrittura del codice, potenza e flessibilità.
● Per la sua grande diffusione, la sintassi e la semantica del C++ è stata scelta come base per altri linguaggi di programmazione (come il C# Java) o per linguaggi di scripting lato client (come JavaScript) e lato server (come PHP).
● Imparare il C++ significa possedere le basi per apprendere linguaggi più usati nella programmazione professionale.
Livelli di funzionalità di un computer
Nello schema seguente viene riportata la gerarchia dei livelli di funzionalità di un computer
Il livello software più basso corrisponde al linguaggio
macchina o assembler (o assembly).
A questo livello ogni "istruzione" corrisponde ad un numero binario. Il
codice binario è l'unico tipo di informazione che le macchine possono comprendere.
In realtà una macchina può solo distinguere tra ciò che è zero e tra ciò
che non lo è.
A livello immediatamente superiore troviamo il sistema
operativo. Il sistema operativo è il software di base che ci permette
di utilizzare le risorse del sistema:
1 Le periferiche di input/output
2 La RAM
3 Il microprocessore (CPU)
4 La gestione della memoria di massa (disco
fisso o hard disk)
Il sistema operativo è dunque un insieme complesso di programmi che interagendo tra loro svolgono una serie di funzioni fondamentali per consentire all'utente un utilizzo della macchina semplice ed economico.
A livello immediatamente superiore troviamo il
software di sviluppo; si tratta di un insieme di programmi che permettono
di sviluppare le applicazioni che vengono definite dal programmatore. Ne
fanno parte gli editor, i compilatori,
gli interpreti e i linker.
Si tratta di un insieme di programmi in grado di tradurre sequenze di istruzioni
formulate in un linguaggio di programmazione ad alto livello come ad esempio
il C++ in linguaggio macchina. Un linguaggio ad alto livello, contrariamente all'assembler, che
è costituito da sequenze di numeri binari, è un linguaggio di più
facile comprensibile per l'uomo .
Particolare importanza in questo caso è la differenza
tra compilatori ed interpreti.
● Un interprete,
legge un'istruzione alla volta, la traduce in linguaggio macchina e la invia
alla CPU per l'esecuzione.
● Un compilatore,
legge tutto il programma e lo traduce in linguaggio macchina; di conseguenza,
quando il programma viene eseguito, esso parte direttamente in linguaggio
macchina. Il C++ è un linguaggio compilato.
La differenza fondamentale risiede nel fatto che mentre l'interprete traduce le istruzioni una alla volta, il compilatore esegue la traduzione di tutto il programma in una sola volta, rendendo l'esecuzione molto più veloce; inoltre l'interprete, deve sempre risiedere in memoria, diminuendone la capacità disponibile, ma rendendo estremamente veloci le modifiche apportate al programma. Viceversa il compilatore viene caricato in memoria solo durante la compilazione, in quanto durante l'esecuzione non è richiesta la sua presenza; ma ad ogni modifica del programma sorgente è necessario far eseguire una nuova compilazione.
I programmi applicativi sono i programmi utilizzati dall'utente per risolvere problemi ben delimitati in ambito scientifico, grafico, commerciale, gestionale, di elaborazione del testo etc.. Il software di sviluppo serve per creare programmi applicativi.
Struttura di un programma C++
I passi da eseguire per la realizzazione di un programma scritto in linguaggio C++ sono schematizzati nel seguente disegno
La fase iniziale di editing consiste nella trascrizione al calcolatore del codice sorgente. Questa operazione può essere svolta mediante in semplice editor di testi puro, come il blocco note (notepad) di Windows. Il risultato di questa operazione sarà il salvataggio di un file comunemente chiamato file sorgente con estensione .cpp che può essere richiamato in seguito per eventuali modifiche.
Per la fase successiva occorre disporre di un compilatore C++. Esistono
molteplici compilatori e linker, alcuni come gcc su Linux consentono di
eseguire le operazioni dalla riga di comando del sistema operativo ma prevalentemente
si usano degli IDE (Integrated Developement System) che comprendono tutte
le funzionalità necessarie per produrre il file eseguibile a partire dal
codice sorgente direttamente editabile nell'IDE. I più famosi sono Microsoft
Visual C++ .NET, Dev C++ e Borland C++ Builder. Tutti i programmi realizzati
in queste pagine sono invece stati realizzati su IDE CodeBlocks
che utilizza il compilatore MingW32.
Il compilatore elabora il file sorgente e, se non rileva errori genera a
sua volta un file oggetto (.obj oppure .o). Quest'ultimo contiene il programma
scritto in linguaggio macchina ma non ancora direttamente eseguibile dall'elaboratore.
Nell'ultima fase toccherà al linker completare il programma aggiungendo
quelle parti di codice necessario al suo funzionamento, ad esempio quelle
relative alle istruzioni cin e cout
per l'input/output da reperire nel file di libreria <iostream.h>.
Il compito del linker è quello collegare il codice intermedio, prodotto
dal compilatore, con quello mancante ottenuto da apposite librerie per formare
finalmente il file eseguibile. Se il programma supera la fase di compilazione
(è, dunque, esente da errori) il linker viene richiamato automaticamente
dal compilatore stesso.
A quel punto il programma creato potrà essere eseguito tramite il sistema
operativo e la sua fase di esecuzione prenderà il nome di fase
di runtime.
La struttura generale dei nostri programmi può essere schematizzata nel
modo seguente:
Una delle cose più importanti da notare è il simbolo punto e virgola (;)
che viene usato come terminatore di ogni singola istruzione.
Non tutti i linguaggi di programmazione usano questa regola; Visual Basic
non la usa, Python non la usa.
I programmi più semplici, di tipo procedurale sono essenzialmente costituiti
da una o più funzioni. All'interno del file sorgente deve esserci almeno
una funzione main(). Le funzioni sono sempre dotate di parentesi tonde all'inizio
della loro scrittura, servono a specificare l'eventuale presenza di parametri
(argomenti) da elaborare.
Le funzioni possono restituire un valore al programma che le ha invocate,
in tal caso vanno precedute da un prefisso che specifica il tipo di dato
restituito, come nel seguente esempio dove viene restituito un valore di
tipo float cioè un numero con la virgola.
in tal caso, deve essere sempre presente l'istruzione return
che specifica il valore da restituire al programma chiamante che ovviamente
deve essere sempre dello stesso tipo indicato come prefisso al nome della
funzione. Alcuni compilatori in linguaggio C/C++ sono talmente fedeli a
queste definizioni che pretendono che per stessa funzione main() venga specificato
il tipo di dato da restituire. In questo caso basta accontentarli specificando
il tipo numerico intero.
Questo è sempre il comportamento di linguaggi fortemente tipizzati come
C/C++ o Java; molto meno rigidi possono essere altri linguaggi come JavaScript
che effettuano un controllo molto più lasco sui tipi di dati da elaborare
e per questo vengono chiamati linguaggi debolmente tipizzati.
Veniamo ai preamboli (nel codice sorgente di qualsiasi programma ci sono
sempre dei preamboli) la premessa più frequente è l'indicazione delle librerie
utilizzate come la #include<iostream>
Questa riga non è un'istruzione ma una direttiva al compilatore. Diversamente
dalle istruzioni le direttive non terminano col punto e virgola (;). Questa
direttiva ha il compito di avvisare il linker e il compilatore che saranno
necessarie istruzioni provenienti dal file di libreria standard iostream
che in questo caso corrisponde all'utilizzo delle istruzioni di cin
per permettere l'input da tastiera e cout per consentire
l'output a video.
Oggetti e funzioni della libreria standard appartengono ad un unico insieme
denominato spazio dei nomi (namespace) standard e sono distinti da tutti
gli altri identificatori per il fatto di essere preceduti dal prefisso std.
Teoricamente per usare un'istruzione cout bisognerebbe
scrivere std::cout e allo stesso modo si dovrebbe
fare per l'input da tastiera std::cin per evitare
di ripetere ogni volta questo identificatore viene utilizzata la direttiva
using secondo la seguente sintassi:
using namespace std;
In tal modo l'aspetto più consueto che assumeranno i nostri scritti sarà:
ma non è da escludere la necessità di utilizzare altre librerie come la
libreria math.h che contiene speciali funzioni come la pow(base,
esponente) per l'elevamento a potenza o la sqrt(radicando)
per l'estrazione della radice quadrata di un numero maggiore o uguale a
zero che può essere dichiarata subito dopo la iostream
con la sintassi:
#include<math.h>
Identificatori
Nel linguaggio C/C++ sono definiti identificatori i nomi tramite i quali è possibile far riferimento a variabili, funzioni e altri oggetti definiti dal programmatore. Un identificatore è costituito da uno o più caratteri, di cui il primo deve essere una lettera o un carattere di sottolineatura(underscore _), mentre i caratteri seguenti possono essere lettere, numeri o sottolineature. Il C++ consente l'utilizzo del simbolo '$' all'interno di un identificatore, ma non all'inizio.
corretto | non corretto |
conta | 1conta |
ver23 | un'id |
altro_id | altro.id |
Espressioni
Una espressione è una combinazione qualsiasi di operandi ed operatori
che dà origine ad un valore. Ogni valore (costante o variabile) presente
in un'espressione è un'operando. Le espressioni sono costruite a partire
da operatori, costanti e variabili, seguendo prevalentemente le legole generali
dell'algebra. Ciò nonostante, ci sono alcuni aspetti direttamente collegati
alla natura del linguaggio.Ad esempio
v/3.6
!0
4+6/2
Nel primo caso se v è una variabile espressa in km/h
l'espressione v/3.6 rappresenta la stessa velocità
in m/s.
Nel secondo caso è presente l'operatore di negazione (!)
permette di far commutare uno 0 in un 1.
Nel terzo caso si tratta di una semplice espressione numerica che fornisce
7 come risultato.
Input/output
In linguaggio C++ per consentire l'ingresso di dati da tastiera è possibile
usare in comando cin. Per effettuare l'output a video si usa il comando
cout, entrambi appartenenti alla libreria iostream.
Ad esempio, il tradizionale programma di test "Hallo World" può essere scritto
nel seguente modo:
#include<iostream>
using namespace std;
main(){
cout<<"ciao Mondo";
}//main
cout è la destinazione standard dell'output video
verso la quale è diretto il flusso (lo stream) delle informazioni presenti
a estra del simbolo << chiamato anche operatore
di inserzione.
L'istruzione cout, permette di presentare sullo schermo
dati e messaggi combinati assieme; ad esempio:
#include<iostream>
using namespace std;
main(){
int x=3;
cout<<"spazio percorso:"<<x<<" m";
}//main
produrrebbe come output a video la stringa
Queste istruzioni vengono spesso utilizzate in concomitanza del comando
endl che permette di andare a capo;ad esempio:
#include<iostream>
using namespace std;
main(){
int x=3;
cout<<"spazio percorso:"<<endl<<x<<"
m";
}//main
l'output prodotto a video, sarebbe:
al posto di endl può essere usato alternativamente il simbolo "\n"
di ritorno a capo secondo l'istruzione.
cout<<"spazio percorso:\n"<
L'oggetto cin è la sorgente simbolica dei dati provenienti
dall'ingresso standard che è la tastiera e deve essere sempre accompagnato
dall'operatore di estrazione >> .
Spesso i due comandi vengono scritti sulla stessa riga perchè contestuali
ad una richiesta di dati che viene fatta all'utente.
cout<<"inserisci lo spazio:";cin>>x;
printf
In alternativa agli stream per acquisire e comunicare dati è possibile
utilizzare le funzioni printf() e scanf()
della libreria standard, richiamabile tramite le direttive
#include<stdio.h> oppure #include<cstdio> . Ad esempio:
#include<cstdio>
using namespace std;
main(){
int eta;
eta=27;
printf("La mia eta' e' di %d anni\n",eta);
}//main
La prima linea di codice all'interno di main() è
int eta;
Questa riga dichiara una variabile chiamata eta e dice al compilatore che
si tratta di una variabile intera. In linguaggio C è necessario dichiarare
tutte le variabili prima di usarle. Il procedimento di dichiarazione implica
sia la specifica sia il nome della variabile che il suo tipo di dato. In
questo caso eta è di tipo int parola chiave che in C serve a dichiarare
dati di tipo intero con numeri che vanno da -32768 a 32767. La linea successiva
è
eta=27;
che è un'istruzione di assegnamento.Questa linea pone il valore 27 nella
variabile eta, anche questa istruzione termina col punto e virgola come
tutte le istruzioni scritte in C. L'istruzione:
printf("La mia età è di %d anni\n",eta);
Quest'istruzione è importante per due ragioni: la prima è che si tratta
di esempio di una chiamata ad una funzione. La seconda è che illustra l'uso
della funzione standard di uscita printf(). La linea
è costituita da due parti: il nome della funzione printf e i suoi due argomenti:
la stringa "La mia età è di %d anni\n" e la variabile eta.
Nel C non ci sono funzioni interne di input/output, queste funzioni sono
invece fornite dalla libreria standard contenute nel file di inclusione
stdio.h (libreria cstdio) . Un programma può
(e deve) contenere funzioni scritte dal programmatore stesso oltre a quelle
provenienti dalle librerie. In ogni caso chiamare una funzione risulta piuttosto
semplice: bisogna solo scrivere il suo nome e fornire gli argomenti necessari.
L'istruzione printf() lavora in questo modo: il
primo argomento è una stringa che può contenere sia caratteri che direttive
di conversione: una direttiva di conversione inizia con il segno di percentuale.
Quando il C trova %d sa che deve scrivere un intero
in formato decimale.
Il secondo argomento rappresenta il valore che deve essere mostrato, in
questo caso coincidente con la variabile eta.Infine \n è una speciale direttiva
di conversione che fa in modo che la funzione printf()
torni a capo alla linea successiva, dopo aver visualizzato la stringa.
La forma generale di printf() è:
printf("stringa di controllo",parametri);
La stringa di controllo contiene i caratteri che saranno mostrati sullo schermo, direttive di conversione che dicono alla printf() come mostrare gli argomenti rimanenti oppure entrambi. Le direttive di conversione principali sono le seguenti
codice | significato |
%d | visualizza un intero in formato decimale |
%f | visualizza un reale in formato decimale |
%c | visualizza un carattere |
%s | visualizza una stringa |
I seguenti esempi mostrano ancora l'uso della printf().
printf("%s %d","questa è una stringa",100); //__>questa
è una stringa 100
printf("questa è una stringa %d",100); //__>questa è
una stringa 100
printf("il numero %d è decimale, %f è reale",60,7.89); //__>il
numero 60 è decimale, 7.89 è reale
printf("%c%c %s -%x",'u','n'," numero in decimale ed in esadecimale:",10,10);
//__>un numero in decimale ed in esadecimale 10-A
printf(%s","SALVE\n");
//__>SALVE
E' necessario avere tanti argomenti quante sono le stringhe di conversione:
in caso contrario, sullo schermo saranno stampati caratteri senza senso.
scanf
La funzione scanf() è una delle funzioni di ingresso del C. La forma generale di scanf() è la seguente:
scanf("stringa di controllo",lista di parametri);
Anche se non è esatto, supporremo per non creare confusione, che la stringa
di controllo possa contenere solamente stringhe di conversione. Le direttive
%d e %f dicono rispettivamente
alla funzione scanf() di leggere un intero e un numero
con la virgola.
Per evitare problemi nell'esecuzione del programma, è indispensabile che
il numero delle direttive di conversionbe sia uguale a quello degli argomenti
della lista dei parametri. Nella lista dei parametri, un segno &
precede le variabili che devono ricevere i valori letti dalla tastiera.
Tipi di dati in C++
In C++ esistono cinque tipi di dati primari:
Tipo di dato | |
carattere | char |
intero | int |
reale | float |
doppia precisione | double |
Senza valore | void |
Le variabili di tipo char sono impiegate
per contenere valori in cui sono sufficienti 8 bit come le lettere del codice
ASCII 'A', 'B', 'C'.
Le variabili di tipo int possono contenere
numeri che non richiedono parte frazionaria.
Le variabili di tipo float e di tipo double
sono impiegate nel caso in cui siano necessari numeri con parte frazionaria
(cifre dopo la virgola) o numeri molto grandi.
La differenza fra questi due ultimi tipi è unicamente nella massima e nella
minima quantità che possono rappresentare.
Il tipo void è stato introdotto per aumentare
le possibilità di controllo sui tipi.
Esiste anche un altro tipo di dato: il bool
(booleano) che può assumere valore 1 o 0 (vero o falso) occupa soltanto
1 bit e viene spesso associato a variabili o condizioni logiche.
Operatori
Per operatore si intende un simbolo che indica delle manipolazioni logiche
e matematiche sui dati.
Gli operatori appartengono a tre classi
Operatori aritmetici .
Operatori relazionali .
Operatori logici .
Operatori aritmetici
Gli operatori aritmetici servono appunto ad eseguire operazioni matematiche:
operatore | azione |
- | sottrazione |
+ | addizione |
* | moltiplicazione |
/ | divisione |
% | resto della divisione tra interi |
-- | decremento unitario |
++ | incremento unitario |
-= | decremento finito |
+= | incremento finito |
Notiamo che applicando un operatore / ad un intero viene troncato il resto,
ad esempio 14/3 da come risultato 6 qualora si tratti di una divisione fra
interi. L'operatore % restituisce il resto di una divisione fra interi,
ma non può essere usato coi float o con i double. Esempio:
main(){
int x=10,y=3;
cout<<x/y; //visualizza 3 (quoto)
cout<<x%y; //visualizza 1 (resto)
}
Incremento e decremento
L'operatore incremento unitario ++ somma 1 all'operando, mentre l'operatore
decremento unitario - - sottrae 1 all'operando. Di conseguenza l' istruzione
x++; equivale all'istruzione x=x+1;
mentre l' istruzione
x--; equivale all'istruzione x=x-1;
L'operatore incremento finito funziona in modo analogo:
x+=5; equivale all'istruzione x=x+5;
mentre
x-=5; equivale all'istruzione x=x-5;
Operatori relazionali
Col termine "relazionale" ci si riferisce alle relazioni che intercorrono tra i valori
operatore | azione |
> | maggiore |
>= | maggiore o uguale |
< | minore |
<= | minore o uguale |
== | uguale |
!= | diverso |
Operatori logici
Col termine "logico" ci si riferisce al modo in cui le relazioni possono essere associate fra loro
operatore | azione |
&& | AND |
|| | OR |
! | NOT |
I costrutti fondamentali del linguaggio vengono descritti successivamente e sono riportati in questa pagina:
● istruzione
if
● istruzione
switch
● ciclo
for
● ciclo
while
● ciclo
do-while
pochissime istruzioni che consentono di realizzare qualsiasi programma applicativo.