edutecnica

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"<<<" m";
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.