Introduzione a C e C++
I linguaggi di programmazione servono a scrivere programmi ovvero sequenze di istruzioni redatte in linguaggio macchina, le uniche elaborabili dalla CPU, cioè dal processore del computer, e formate da sequenze di 0 e 1 (linguaggio binario). In particolare il linguaggio di programmazione C, progettato da Dennis Ritchie su UNIX nel 1972, discende dal B che, a sua volta, discende dal BCPL. Nel 1989 l’ANSI (Istituto Nazionale Americano per gli Standard) ha standardizzato il linguaggio C, definendo l'ANSI C. Con l’evoluzione C++ (1982), C è stato orientato agli oggetti (OOP), molto utile se si ha che fare con grandi progetti dato che permette al programmatore di gestire le singoli componenti del programma, appunto, come oggetti.
Java, nato nel 1992 grazie a James Gosling e con l’iniziale nome di Oak, è quasi considerabile un'evoluzione del C++ dato che la sua sintassi deve molto al C/C++. Java però è un linguaggio di programmazione multipiattaforma orientato al web, il cui motto è: write once, run everywhere. Infatti in teoria il medesimo programma scritto in Java è eseguibile su Windows, Linux e Mac e anche su smartphone, se esiste una JVM (Java Virtual Machine) per quel sistema.
Abbiamo visto la nascita di tanti altri linguaggi, anche di script (un’esemplificazione dei linguaggi di programmazione) abbastanza semplici come Perl e Python (entrambi sviluppati da C). Microsoft ha un suo ambiente di sviluppo basato sul C++ (Visual C++) e ha anche sviluppato C# (C sharp), un linguaggio a oggetti che prende spunto, tra gli altri, anche da Java. Esiste poi l’Objective C, anch’esso orientato agli oggetti, che consente lo sviluppo di applicazioni per iOS.
I listati di codice C permettono di gestire bit, byte e indirizzi di memoria, a differenza di altri linguaggi di alto livello. È un linguaggio portabile, ossia un listato scritto in ANSI C può essere compilato su ogni compilatore di ogni sistema operativo. C è un linguaggio usato tanto per semplici programmi, tanto per programmare sistemi operativi: si presta a un'infinità di usi, grazie alle varie librerie. C++ contiene C. Quindi studiare C non è una perdita di tempo in quanto nel C++ si ritrovano gli stessi costrutti, cambiano solo alcuni comandi. Un programma scritto in C verrà dunque compilato anche da un compilatore C++. Ciò significa che un listato in C sarà facilmente riscrivibile in C++, modificando pochi termini.
Il post non tratterà la programmazione a oggetti ma si occuperà della programmazione procedurale, anche se sarebbe più corretto dire strutturata come vedremo tra poco, per la quale le differenze tra C e C++ sono poche, eccone alcune:
- Commenti: vengono ignorati dal compilatore, ovvero non vengono tradotti in codice eseguibile. Con C i commenti vanno racchiusi tra i simboli * riga multipla */, nel C++ è stato introdotto anche il simbolo/ che indica al compilatore che tutto quello che segue nella riga è un commento. Quindi in C++ sono possibili due tipi di commenti:
- commento di riga
/ questo commento termina quando si va a capo - commento lungo
* questo commento inizia qui
e termina qui */ - Dichiarazione variabili locali
- in C vanno dichiarate all’inizio del blocco che le contiene
- in C++ possono essere dichiarate in un punto qualsiasi del blocco che le contiene
- Nuovi nomi di tipi
- Sono stati introdotti lo specificatore const per le costanti e anche gli operatori new e delete per il controllo dell’allocazione dinamica della memoria
- Funzioni
- più funzioni possono usare il medesimo nome, per distinguerle basta un numero o tipo di parametri diverso (overloading di funzioni)
- è possibile richiamare le funzioni con un numero di parametri inferiore a quello dichiarato, i parametri non passati vengono sostituiti con valori standard
- parametro void
- C: – function() indica al compilatore di non controllare i parametri passati alla funzione, cioè si può passare un numero arbitrario di parametri
- C++: – function() equivale a function(void), cioè non deve essere passato alcun parametro
- Allocazione dinamica di memoria
- C: malloc e free
- C++: new e delete
- Output
- C: printf(“testo %d”, num);
- C++: cout<<“testo”<<num;
- Input
- C: scanf(“%d”,&num);
- C++: cin >> num;
Peculiarità linguaggio C
- Dimensioni del codice sorgente relativamente ridotte
- Dimensioni dell'eseguibile moderate una volta compilato il codice; un programma in C potrà però essere eseguito solo sullo stesso Sistema Operativo dove è stato compilato
- Efficienza dei programmi dovuta alle dimensioni ridotte e alla possibilità di gestire a fondo la memoria
- Implementazioni dei puntatori per memoria, array, strutture e funzioni
- Ampia compilabilità data l'alta disponibilità di compilatori per diverse piattaforme (architetture e sistemi operativi). Non portabile come Java, ma altamente portabile
- Linguaggio di alto livello per tutti gli usi (general purpose) - Un linguaggio è di livello tanto più basso quanto è più vicino al linguaggio macchina, formato da 0 e 1 e non a quello umano come C, C++, Java e molti altri
Pregi linguaggio di basso livello (es. Assembly): il codice è molto veloce (minore richiesta di risorse, minor peso nel programma finale); nei sistemi dove ogni istante è veramente importante (come per esempio nelle catene di montaggio) spesso viene scelto un linguaggio a basso livello proprio per la sua velocità.
Difetti linguaggio di basso livello: scriverlo è complicato e un codice scritto per un computer potrebbe non funzionare su un altro.
C è un linguaggio di basso livello tra quelli di alto livello perché ha poche istruzioni, gestisce efficientemente l’accesso alla memoria e ai registri interni del processore e può ospitare codice Assembly.
Per far scrivere Ciao in C:
#include <stdio.h>
int main() {
printf ("Ciao\n");
}
Per far scrivere Ciao in Assembly per Intel:
IDEAL
MODEL SMALL
STACK 100h
DATASEG
HW DB "Ciao", 13, 10, '$'
CODESEG
Begin:
MOV AX, @data
MOV DS, AX
MOV DX, OFFSET HW
MOV AH, 09H
INT 21H
MOV AX, 4C00H
INT 21H
END Begin
Tecniche di programmazione
Esistono i metodi di:
- programmazione procedurale (Procedural Programming)
Nei programmi procedurali (es. Cobol, Fortran, Basic) le istruzioni, che vengono eseguite sequenzialmente una dopo l'altra, sono organizzate in blocchi (subroutine o function) studiati per un preciso scopo, accessibili a cascata o attraverso il comando goto da più punti del codice. Il codice è eseguito dall'inizio alla fine (top-down) ed è semplice da seguire - programmazione strutturata (Structured Programming)
Concorre alla nascita della programmazione strutturata la critica al goto che rappresentava, negli anni Sessanta, lo strumento principe usato in programmazione, ma deleterio per leggibilità e modificabilità del software (il cosiddetto problema dello spaghetti code) e che quindi non dovrebbe essere usato. Mantiene un approccio top-down ma l'esecuzione delle istruzioni non è obbligatoriamente in ordine sequenziale bensì influenzata da cicli e condizioni basate sul valore dei dati (es. C, Pascal, Algol) - programmazione orientata agli oggetti (Objected Oriented Programming - OOP)
Il modello OOP (es. Java, C#, C++) necessita di maggiore codice perché bisogna prima instanziare un oggetto (una sorta di contenitore detto classe), ma ha un’implementazione centralizzata su quell’oggetto e tutti gli oggetti possono comunicare tra loro, ciò consente una potenzialità maggiore. Diversamente dai linguaggi già descritti, gli OOP hanno un approccio di progettazione bottom-up, ovvero la prima cosa da fare è la progettazione degli oggetti
Programma compilato e interpretato
Un programma si sviluppa scrivendo codice sorgente in un certo linguaggio.
Linguaggi come FORTRAN, COBOL , C-based (C, C++, C#) .NET, Objective-C, Visual Basic, ecc. sono linguaggi compilati e seguono questi passi:
- si scrive il codice sorgente in un editor o in un ambiente di sviluppo IDE
- il codice viene controllato per verificare che non ci siano errori
- il codice viene compilato da un programma detto compilatore, ovvero ogni istruzione viene trasformata nel corrispondente codice in linguaggio macchina che può essere, così, eseguito dal processore
I linguaggi compilati hanno ottime prestazioni.
Nei linguaggi interpretati, ad esempio PHP o Perl, il codice sorgente viene invece interpretato al volo da un programma chiamato interprete che traduce istantaneamente il codice sorgente in linguaggio macchina; ad esempio quando il codice PHP viene elaborato, restituisce una pagina HTML pura.
I linguaggi interpretati hanno alta portabilità, ma problemi relativi al carico di lavoro maggiore per il processore (che ogni volta deve elaborare la pagina) e nella ricerca di errori nel codice sorgente.
Java è una via di mezzo, è sia compilato che interpretato: il codice sorgente viene compilato in un formato intermedio (chiamato bytecode) che fornisce un file con estensione .class, il quale viene interpretato dalla Java Virtual Machine (JVM), che ha il compito di interpretare al volo le istruzioni bytecode in istruzioni per il processore ed eseguire il programma. È uno dei punti di forza del Java, che lo ha reso portabile verso ogni piattaforma. Questa metodologia implica inoltre la possibilità di controllare eventuali errori del codice sorgente (grazie alla compilazione), di creare programmi relativamente leggeri (il bytecode è un formato che crea file di dimensioni ragionevoli), ma ha prestazioni non proprio ottime.
Java Esempio.java Compilazione Esempio.class Interpretazione Java Virtual Machine Esecuzione Macchina reale |
C++ Esempio.cpp Compilazione Esempio.exe Esecuzione - - Macchina reale |
Primo programma in C++
Anche se per programmare in C/C++ è possibile usare qualsiasi editor di testi, anche il Blocco Note dei Sistemi Operativi Windows o il tool a pagamento Visual C++ o altri programmi ancora, in questo manuale si utilizzerà il software gratuito Dev-C++.
Per scrivere il primo programma in C++ si proceda nel modo seguente ricordando che C++, come C, è case-sensitive cioè distingue tra maiuscole e minuscole quindi, ad esempio, MAIN() o Main() non è main():
- Avviare l’ambiente di sviluppo
- Eseguire le operazioni di editing, cioè di scrittura del programma da File Nuovo File Sorgente e scrivere quanto segue (che poi verrà spiegato accuratamente):
#include <iostream>
using namespace std;
int main (){
cout<<”Hello world!\n”;
system(“pause”);
return 0;
}
- Per compilare il programma cliccare Esegui Compila e dare hello.cpp come nome del file (se il codice è stato scritto correttamente saranno segnalati 0 errori e avvertimenti, quest’ultimi sono detti warning e non sono errori)
- Per eseguire il programma cliccare Esegui Esegui oppure cliccare il file hello.exe che è stato generato dalla compilazione nella stessa posizione del file hello.cpp
Spiegazione del programma
- #include <iostream> Direttiva al preprocessore, un comando, che permette di richiamare le librerie standard, in questo caso iostream, che fornisce le operazioni di input e output. Senza librerie un programma non avrebbe a disposizione i comandi per eseguire operazioni
- using namespace std; direttiva propria del C++, include nomi di funzione e identificatori delle librerie standard; dichiara l'uso dello spazio dei nomi della libreria standard ed evita di far precedere il nome std ad alcuni elementi del programma. Il punto e virgola chiude un'istruzione, per far capire che dopo quel simbolo inizia una nuova istruzione
- int main (){ La funzione principale unica e onnipresente in un qualsiasi programma è main con o senza parametri tra le parentesi tonde; è da main che il programma inizierà l’esecuzione. Le istruzioni (statement) della funzione sono racchiuse tra parentesi graffe { } e sono eseguite sequenzialmente ossia in ordine, dalla prima all'ultima. int è il tipo di dati restituito dalla funzione, in mancanza di indicazione si sottintende int ovvero intero, esistono molti altri tipi
- cout<<”Hello world!\n”; quest’istruzione stampa verso un output (ad esempio su video) la frase tra virgolette. C++ definisce alcuni stream predefiniti, tra i quali cin e cout per la lettura da tastiera (input) e la scrittura su monitor (output). Gli operatori principali per manipolare gli stream è quello di inserimento << (operatore di output) per inserire caratteri in uno stream, e quello estrazione >> (operatore di input) per estrarre caratteri da uno stream. Per usare cout necessita #include <iostream>
- system(“pause”); La funzione system blocca il programma fin quando non si preme un tasto. Questa funzione può mandare in esecuzione qualsiasi comando del sistema operativo o programma. Per esempio system(“CLS”) pulisce la finestra di output (CLear Screen), mentre system(Color xy) colora (x è il colore di sfondo e y il colore del testo). I colori disponibili sono: 0 = nero, 1 = blu scuro, 2 = verde, 3 = verde acqua,
4 = bordeaux, 5 = viola, 6 = verde oliva, 7 = grigio chiaro,
8 = grigio, 9 = blu
A = verde limone, B = azzurro, C = rosso, D = fucsia,
E = giallo, F = bianco
Ad esempio:
#include<windows.h>
#include<conio.h>
using namespace std;
int main ()
{
system("COLOR 2c");
return 0;
}
- return 0; return <valore> fa terminare la funzione restituendo il valore indicato, qui non c'è nessun valore da restituire, metto 0; in ogni caso se non viene inserito, il compilatore mette automaticamente return 0
- } Chiude la funzione principale main(). Se una funzione non restituisce nessun valore si può indicare, come tipo, void:
void main(){}
Sequenze di escape C e C++
La sequenza formata dalla barra rovesciata seguita da uno o più caratteri è detta sequenza di escape e corrisponde a un carattere.
\n (new line) porta il cursore all’inizio della riga successiva
\t (tab) porta il cursore al prossimo fermo di tabulazione (ogni tabulazione è di 8 caratteri)
\a (alert) emette un beep
\f (form feed) nuova pagina
\r (carriage return) ritorno a capo
\’ stampa un apice
\” stampa le virgolette
\\ barra rovesciata
\? punto di domanda
\b (Backspace) indietro di un carattere
\<cifreOttali> il carattere corrispondente nel codice ASCII al numero ottale
\x<cifreEsadec> il carattere corrispondente nel codice ASCII al numero esadecimale
La sequenza di escape consente di inserire il carattere corrispondente a un qualsiasi codice ASCII in due modi:
- usando la barra rovesciata \ seguita dal codice ASCII in ottale (es. \11 denota un byte con il valore numerico 9)
- usando la barra rovesciata \ seguita dal codice ASCII in esadecimale (es. \x41 corrisponde al carattere ‘A’).
È possibile attaccare più sequenze di escape una dopo l’altra.
Eseguire il debug con Dev-C++
Il debugging (o debug) è l'attività d'individuazione e correzione da parte del programmatore degli errori (bug) rilevati nel programma. È una delle operazioni più importanti, a volte difficile se il programma è complesso e anche delicata per il pericolo di inserire nuovi errori nel tentativo di correggere quelli per i quali si svolge l'attività di debug.
Per attivare il debug su Dev-C++ seguire la seguente procedura:
- Strumenti
- Opzioni di compilazione
- Generazione di Codice Ottimizzazioni
- Linker
- mettere Yes su Genera le informazioni per il debug
Per eseguire il debug su Dev-C++ seguire la seguente procedura:
- Compilare il programma
- cliccare Esegui Debug
Se viene riscontrato un errore comparirà una riga evidenziata per segnalare che lì c’è un errore. Correggerlo ed eseguire nuovamente il programma.
Esercizi in C e C++
Dopo l’esempio iniziale in C++ vediamo ora dei semplici esercizi in C e C++ con relativi commenti.C Scrivi Ciao sul monitor ed emetti un beep
#include <stdio.h> /Direttiva al preprocessore per usare printf
int main() {*int dice al Sistema Operativo che main(), cioè il programma, gli restituirà un intero*/
printf ("Ciao\a");/Stampa (mostra) un messaggio sul monitor
return 0;
}
C Scrivi sul monitor Ciao! e vai a capo, poi scrivi Questo è un mio programma e vai due volte a capo, infine scrivi Premi un carattere da tastiera per uscire
#include <stdio.h>
#include<conio.h>/per usare getch()
int main()
{
printf ("Ciao!\nQuesto è un mio programma\n\n");
printf("Premi un carattere da tastiera per uscire.\n");
getch();/Mette in pausa la console di output finché non viene premuto un tasto
return 0;
}
C++ Stampa a video partendo dalla seconda riga con cout<< o cout << (console output). Esiste un’altra funzione di I/O chiamata cin>> (console input) per l’input dei dati.
abc
def
ghi
lmn
opqrs
tuvz
Premere un tasto per continuare…
#include <iostream>/Per usare cout
#include <stdlib.h>/Per usare system
using namespace std;
main(){
cout << "\nabc\n";
cout << "def\n";
cout << "ghi\n";
cout << "lmn\n";
cout << "opqrs\n";
cout << "tuvz\n";
system("pause");
}
Se desideri approfondire C e C++ (o Java) e allenarti con gli esercizi, ti consiglio...
Fondamenti di C e C++ Teoria e pratica
Versioni cartacea e digitale aggiornate a Febbraio 2023 - 303 pagine
LINK TESTUALE versione digitale
LINK TESTUALE versione cartacea
Questo manuale fornisce le basi per portare chiunque a diventare un programmatore nei linguaggi C e C++. Lo fa senza appesantire troppo la teoria, che comunque tratterà di tutti i costrutti fondamentali di questi linguaggi, ma attraverso molti codici commentati in italiano. Un testo ricco di contenuti ed esempi, accessibile anche ai neofiti della programmazione. Il manuale contiene il link per scaricare tutti i codici C e C++, e relativi commenti, degli esempi proposti.