In questo capitolo verranno fornite alcune conoscenze di base per C, C++ e Java.
3.1 Commenti
Per corredare il codice di commenti, è sufficiente delimitare questi ultimi tra i due simboli: /* e */. In C++ e Java è possibile inserire commenti di un'unica riga precedendoli con: //.
3.2 Dichiarazione di variabili
In un linguaggio fortemente tipato (come C, C++ e Java) ogni variabile o espressione ha un tipo che è già noto al momento della compilazione, e quindi prima dell'esecuzione. Questa caratteristica aiuta il programmatore ad evitare errori comuni e ad effettuare controlli sulla compatibilità dei tipi nelle espressioni e negli assegnamenti.
Tipi e variabili hanno identificatori, costituiti da una qualsiasi sequenza di lettere alfabetiche, simbolo “_” e cifre. Gli identificatori non possono però avere una cifra come primo carattere, né possono coincidere con una parola chiave o riservata del linguaggio. Identificatori validi possono essere ad esempio: parola, numero, c4s4, _return, a3_sei.
Ogni variabile deve essere dichiarata prima di essere usata, con questa forma:
<id_tipo> <id_variabile>;
dove id_tipo è il tipo predefinito o definito dal programmatore, mentre id_variabile è l'identificatore. Esempi di tipi predefiniti in C, C++ e Java sono int, char e double.
3.3 Espressioni e assegnamenti
L'assegnamento consiste nell'attribuire un valore ad una variabile. Ha questa forma:
<id_variabile> = <espressione>;
Un'espressione è costruita combinando costanti, identificatori di variabili, invocazioni di funzioni o metodi, operatori e parentesi tonde, queste ultime necessarie per specificare la priorità degli operatori. È importante che ogni operatore sia applicato a sottoespressioni del tipo appropriato, e che gli argomenti delle funzioni siano del tipo giusto o compatibile.
Un assegnamento restituisce il valore assegnato alla sua variabile al membro sinistro, e in virtù di questo fatto può essere utilizzato a sua volta all'interno di un'espressione. Sono dunque possibili assegnamenti come i seguenti:\\
x = y = z = 0.0;
d = (e = (top – bottom)/2) + 1
Esistono poi una serie di operatori combinati con l'assegnamento della forma “(operatore)=” (come “+=”, “*=” o “-=”), che permettono di abbreviare certi tipi di assegnamenti che modificano il valore di una variabile in funzione del suo valore precedente. Ad esempio “x += 3” significa “x = x + 3”.
Un'ulteriore abbreviazione sono gli operatori di incremento “+ +” e di decremento “- -”, che possono essere prefissi o suffissi a un identificatore di variabile intera a seconda che si voglia effettuare l'incremento (o il decremento) prima o dopo la fine dell'istruzione.
3.4 Vettori (array)
I vettori (o array) sono delle entità analoghe alle tabelle, definibili e gestibili da tutti i linguaggi di alto livello. Sono caratterizzati da una dimensione, corrispondente al numero di componenti di cui il vettore è formato, specificata tra parentesi quadre.
I linguaggi C, C++ e Java permettono di costruire vettori costituiti da tipi di base o definiti dal programmatore. Per esempio un vettore di dieci interi si definisce come: int x[10];
Da notare che gli indici dei componenti del vettore partono da 0, il che significa che se un array ha dimensione n, la sua ultima componente ha indice n-1.
È possibile definire vettori multidimensionali semplicemente aggiungendo tra quadre la grandezza della dimensione successiva. Per esempio una tabella di interi di quattro righe e sei colonne avrà come dichiarazione: int x[4][6];
3.5 Funzioni
Tutti i linguaggi di alto livello permettono la gestione dei sottoprogrammi, distinguendoli in:
- procedure, ovvero sottoprogrammi che prendono in ingresso zero o più argomenti (o parametri), svolgono una particolare elaborazione in base ad essi e non restituiscono (almeno esplicitamente) alcun risultato al programma chiamante;
- funzioni, ovvero sottoprogrammi che prendono in ingresso zero o più argomenti (o parametri), svolgono una particolare elaborazione in base ad essi e restituiscono un valore al programma chiamante per mezzo dell'istruzione “return espressione ”.
Quindi: le funzioni restituiscono un valore, le procedure no.
Le funzioni si dichiarano con la seguente sintassi:
<tipo> <nome_funzione> (<argomenti>)
{
<corpo>
}
dove <tipo> è il tipo del risultato della funzione (o la parola chiave void, per le procedure) e <argomenti> è una lista di zero o più dichiarazioni di argomenti separate da virgole.
Torna su
4. Costrutti di controllo
I costrutti di controllo permettono di combinare tra loro istruzioni elementari creando così istruzioni complesse o blocchi di istruzioni. In questo capitolo verranno analizzati quelli di C, C++ e Java.
4.1 Esecuzione in sequenza
L'esecuzione in sequenza consiste nell'esecuzione di un'istruzione dopo l'altra. È possibile creare una sequenza o blocco di istruzioni scrivendole in successione e racchiudendole tra parentesi graffe: { } .
4.2 Costrutti iterativi
I costrutti iterativi permettono l'esecuzione di un ciclo, ovvero la ripetizione in modo controllato di un determinato blocco di istruzioni.
Il tipo di iterazione più semplice è la while:
while (<condizione>)
<istruzione>
La condizione viene controllata all'inizio di ogni iterazione e l'istruzione viene eseguita solo se la condizione è verificata. Nel momento in cui la condizione non è più verificata l'esecuzione dell'iterazione viene interrotta.
Il do...while permette l'esecuzione dell'istruzione prima del controllo della condizione, quindi almeno una volta:
do
<istruzione>
while (<condizione>)
Se il numero di iterazioni è noto a priori, è possibile utilizzare il costrutto for:
for (<inizializzazione>; <condizione>; <incremento>)
<istruzione>
Il for è semanticamente equivalente ad un ciclo while così strutturato:
<inizializzazione>
while (<condizione>)
{
<istruzione>
<incremento>
}
Il for viene considerato zucchero sintattico, ovvero un costrutto che non aggiunge di per sé nulla alla potenza espressiva di un linguaggio, ma che abbrevia la scrittura di determinati costrutti molto comuni e ricorrenti, rendendo più chiari i programmi.
Esistono due istruzioni di salto controllate:
- break, che interrompe l'esecuzione del ciclo e passa all'istruzione successiva;
- continue, che non esce dal ciclo ma passa all'iterazione successiva.
4.3 Costrutti di selezione
I costrutti di selezione possono essere di due tipi fondamentali:
- gli if , che permettono una selezione tra due alternative in base ad una
condizione binaria;
- gli switch , che permettono una selezione tra molte alternative in base al valore
di un'espressione scalare. Sono il corrispondente della tecnica delle tabelle di salto.
La sintassi della selezione binaria è la seguente:
if (<condizione>)
<istruzione1>
else
<istruzione2>
Se la condizione è verificata viene eseguita l'istruzione1 (o il blocco di istruzioni1), altrimenti viene eseguita l'istruzione2 (o il blocco di istruzioni2). Se non è prevista un'alternativa, il ramo else può anche essere omesso.
La sintassi della selezione tra n alternative ha invece la seguente sintassi:
switch (<espressione>)
{
case <costante1>:
<istruzione1>
break;
...
case <costante n>:
<istruzionen>
break;
}
Torna su
5. Eliminazione dei salti
5.1 Il Teorema di Böhm-Jacopini
Il Teorema di Böhm-Jacopini (o “di struttura”) costituisce una giustificazione matematica all'abbandono di uno stile di programmazione che faceva uso dei salti a favore della programmazione strutturata.
Tale teorema dimostra che è possibile realizzare qualsiasi algoritmo senza utilizzare alcuna istruzione di salto, disponendo dei tre soli costrutti di controllo: sequenza, iterazione e selezione.
Unico “contro” della programmazione strutturata è che in alcuni casi un programma senza salti può essere meno efficiente di un equivalente programma con salti.
5.2 La trasformazione di Ashcroft e Manna
Una dimostrazione alternativa del Teorema di Böhm-Jacopini è la trasformazione di Ashcroft e Manna.
L'idea che sta alla base di tale dimostrazione è che è sempre possibile trasformare un qualsiasi programma con salti in un unico ciclo while contenente tanti if.
5.3 Costrutti minimali
Il Teorema di Böhm-Jacopini dimostra che è possibile realizzare qualsiasi algoritmo utilizzando i tre soli costrutti di esecuzione in sequenza, iterazione e selezione, ma in realtà sono sufficienti i primi due. Una selezione può essere infatti trasformata in un costrutto di iterazione in cui l'iterazione viene effettuata al massimo una volta, sempre che la condizione di entrata sia verificata.
Torna su
6. Strutture dati