Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Indice
Elementi base di programmazione .............................................................................................................. 1
Indice............................................................................................................................................................. 1
Vista d’insieme .......................................................................................................................................... 2
I diversi linguaggi................................................................................................................................... 2
Algoritmo .............................................................................................................................................. 3
Böhm-Jacopini ....................................................................................................................................... 3
Linguaggio di programmazione, programma, elaboratore, informatica. ............................................. 4
Dato Informazione Astrazione. ............................................................................................................ 5
La memoria del computer..................................................................................................................... 5
Cos’è un bit ........................................................................................................................................... 6
Cos’è un Byte ........................................................................................................................................ 7
Qual è la differenza tra bit e byte? ....................................................................................................... 7
La CPU ................................................................................................................................................... 7
Architettura Von Neumann .................................................................................................................. 8
Architettura Harvard ............................................................................................................................. 8
Architetture a confronto ....................................................................................................................... 8
Linguaggi compilati,, interpretati, misti .................................................................................................... 9
Cosa serve per programmare in C ............................................................................................................ 9
Debugger ............................................................................................................................................. 10
Sintassi di base ........................................................................................................................................ 11
Statement ........................................................................................................................................... 11
Tipi di dati semplici o primitivi ............................................................................................................ 11
Tipi di dati strutturati .......................................................................................................................... 11
Definizione di variabili......................................................................................................................... 12
Le stringhe........................................................................................................................................... 13
Scope di una variabile ......................................................................................................................... 13
Operatori principali ............................................................................................................................. 14
Controllo del flusso ................................................................................................................................. 14
Controlli condizionali .......................................................................................................................... 14
Controlli Iterativi: i cicli while, for ....................................................................................................... 16
Le funzioni ............................................................................................................................................... 19
La funzione “speciale” main() ............................................................................................................. 20
Perché si usano le funzioni? ................................................................................................................ 21
Esercizi .................................................................................................................................................... 22
Controlli condizionali .......................................................................................................................... 22
Iterazioni ............................................................................................................................................. 22
Variabili strutturate: gli array.............................................................................................................. 24
2
Vista d’insieme
Prima di buttarci a capofitto nell’apprendimento dei concetti di base per programmare, vediamo
qualche informazione in più sui linguaggi di programmazione. Useremo un linguaggio
imperativo procedurale, in grado cioè di effettuare manipolazione dei dati attraverso operazioni
matematiche o logiche, raggruppate in procedure specifiche definite funzioni o metodi.
Si tratta di un linguaggio ad alto livello, che utilizza cioè una sintassi vicina al linguaggio umano
e relativamente distante dal linguaggio assembly e dal linguaggio macchina.
I diversi linguaggi
Linguaggio macchina
0 0100 0000 0000 1000
1 0100 0000 0000 1001
2 0000 0000 0000 1000
3 0001 0000 0000 1001
4 1000 0000 0000 0000
5 0010 0000 0000 1000
6 0101 0000 0000 1000
7 1101 0000 0000 0000
8 0000 0000 0000 0000
9 0000 0000 0000 0000
Linguaggio assembly
ORG 100h ; Punto di inizio standard per un programma DOS
MOV AX, @data ; Inizializza il segmento dei dati DS con DS = DS
MOV DS, AX
MOV CX, 10 ; Calcola i primi 10 numeri di Fibonacci
MOV AX, 0 ; Primo numero di Fibonacci (F(0))
MOV BX, 1 ; Secondo numero di Fibonacci (F(1))
; Ciclo per calcolare e stampare la sequenza di Fibonacci
CALCULATE_LOOP:
; Stampa il valore corrente (AX) a schermo
MOV CX, AX ; Copia il valore di AX a CX per stamparlo
CALL PrintNumber
; Calcola il prossimo numero di Fibonacci e aggiorna i registri
ADD AX, BX ; F(n) = F(n-1) + F(n-2)
XCHG AX, BX ; Scambia i valori di AX e BX per prepararsi al prossimo calcolo
; Decrementa il contatore CX
DEC CX
; Verifica se abbiamo calcolato tutti i numeri desiderati
JNZ CALCULATE_LOOP
MOV AH, 4Ch ; Funzione DOS per terminare il programma
INT 21h
Linguaggio matematico
void printFibonacci(int n) {
int first = 0, second = 1, next, i;
printf("\n");
}
Linguaggio matematico
fn = fn-1 + fn-2
Linguaggio parlato
Scrivere un programma che stampa i primi 100 numeri di Fibonacci
Algoritmo
Algoritmo: sequenza ordinata di azioni elementari non ambigue che producono risultati
deterministici in un tempo finito.
o sequenza ordinata: deriva dalla necessità della CPU di eseguire una istruzione alla volta;
o azioni elementari non ambigue: comprensibili a chi le esegue, nella fattispecie il
microprocessore;
o risultati deterministici: a uguali ingressi corrispondono uguali uscite;
o tempo finito: non solo deve terminare ma anche terminare in un tempo utile.
Böhm-Jacopini
Il teorema di Böhm-Jacopini, enunciato nel 1966 dagli informatici Corrado Böhm e Giuseppe
Jacopini, è un teorema di informatica teorica il quale afferma che qualunque algoritmo può
essere realizzato in fase di programmazione (mediante diagramma di flusso, pseudocodice o
codice sorgente) utilizzando tre sole strutture dette strutture di controllo:
Sequenza
La sequenza o blocco è la normale elencazione di istruzioni perché vengano eseguite una di
seguito all'altra nell'ordine in cui sono state scritte dal programmatore (v. Algoritmo).
Selezione (o condizione)
La selezione è la scelta fra due sequenze da eseguire in alternativa, che dipende da una
condizione che può essere vera o falsa.
Ciclo (o iterazione)
Il ciclo, detto anche iterazione, è una sequenza che viene ripetutamente eseguita fino a che una
certa condizione cambia di stato.
Il suo valore consiste nella capacità di fornire indicazioni generali per le attività di progettazione
di nuovi linguaggi che devono contemplare almeno le tre strutture e di strategie di
programmazione
In effetti, esso ha contribuito alla critica dell’uso sconsiderato delle istruzioni go to e alla
definizione delle linee guida della programmazione strutturata, sviluppate negli anni `70
program MenuProgram
implicit none
integer :: choice
10 write(*,*) "Menu:"
write(*,*) "1. Opzione 1"
write(*,*) "2. Opzione 2"
write(*,*) "3. Opzione 3"
write(*,*) "4. Esci"
write(*,*) "Scegli un'opzione (1-4): "
read(*,*) choice
Un algoritmo con i dati da lui elaborati posto in forma comprensibile al computer mediante un
linguaggio di programmazione viene chiamato programma
Informatica: scienza che si occupa della conservazione, della elaborazione e della trasmissione
dei dati (che poi l’uomo traduce in informazioni) per mezzo dell'elaborazione elettronica.
L'informatica, se ci si concentra sulla sola fase di elaborazione, può essere descritta come lo
studio sistematico dei processi algoritmici che descrivono e trasformano l'informazione
Astrazione: è quel processo di pensiero che porta a considerare di una realtà, un ridotto numero
di dettagli, al fine di concentrare l'analisi e lo studio sui fattori ritenuti importanti in un certo
contesto, a scapito di altri giudicati meno importanti. Il termine astrazione deriva dal latino
abstrahĕre, “abs” da indicante separazione, “trahĕre” trarre.
Tutte le celle di memoria hanno la stessa dimensione o lunghezza. Le celle di memoria dei primi
PC erano lunghe 8 bit ( ossia 1 byte ). Poi si passò progressivamente alle celle di memoria da 16
bit, 32 bit e 64 bit. Ogni locazione è associata in modo univoco a un indirizzo di memoria che
permette al processore di individuarla, leggere o scrivere un dato all'interno della cella.
Cos’è un bit
I computer sono dispositivi elettronici e funzionano solo con valori discreti. Quindi, alla fine,
qualsiasi tipo di dati che il computer vuole gestire viene convertito in numeri. Tuttavia, i
computer non rappresentano i numeri nello stesso modo in cui lo facciamo noi umani. Per
rappresentare i numeri, usiamo il sistema decimale che fa uso di 10 cifre (0, 1, 2, 3, 4, 5, 6, 7, 8,
9). Per rappresentare i numeri, i computer moderni utilizzano un sistema binario composto da
due cifre (0 e 1). “bit” è il nome dato alla più piccola unità di dati che può essere rappresentata
con questo sistema ( bit sta per “binary digit” “cifra binaria”). cioè, in termini di numeri binari,
un po ‘consiste di uno 0 o di un 1. Nell’elettronica che compone il computer, un bit può essere
rappresentato dall’avere due tensioni. Uno stato “off” (0 volt) potrebbe rappresentare uno 0
binario e uno stato “on” (con una tensione massima) potrebbe rappresentare un 1 binario.
7
Cos’è un Byte
Il bit é l’unità fondamentale di memoria per un computer: esso corrisponde ad un valore a cui
viene associato lo stato di 1 e 0 o quello di vero e falso. Un numero binario è una sequenza di
cifre binarie (0 o 1, dette bit). Un numero binario può assumere 2n valori dove n è il numero di
bit da cui è composto, per esempio un numero binario a 3 bit può assumere 23=8 valori da 0 a 7:
Un byte è una sequenza di bit. Dal 1964 il byte è formato da 8 bit ed è pertanto in grado di
assumere 28 = 256 possibili valori (da 0 a 255).
Un byte è una quantità di informazione elementare per molti scopi. Ad esempio, in Java, il tipo
di dati byte viene utilizzato per rappresentare un numero compreso tra -128 e 127. In C / C ++,
anche il tipo di dati char è composto da 8 bit. In C / C ++, char viene utilizzato per memorizzare
un singolo carattere. Il carattere “B” è rappresentato utilizzando 01000010 e il carattere “b”
utilizzando 01100010. Servono circa 200 livelli di grigio per passare dal bianco al nero, perché
l'occhio umano pensi di vedere una sfumatura continua. Servono circa 200 caratteri diversi
(lettere accentate comprese) per scrivere un qualsiasi testo di una lingua occidentale. Servono
circa 200 livelli di intensità per riprodurre un suono abbastanza fedele.
I byte, d’altro canto, sono utilizzati per esprimere le capacità di memoria (ma anche la velocità di
trasmissione come multiplo del b/s: 1 B/s = 8 b/s). Inoltre il byte rappresenta l’unità minima di
memoria indirizzabile.
La CPU
v. documento Il Microprocessore
8
Architettura Harvard
L’architettura di Harvard è un’architettura di computer con programmi e dati sono memorizzati
fisicamente su memorie separate. Il termine ha avuto origine dal computer basato su relè Harvard
Mark I, che memorizzava istruzioni su nastro perforato (24 bit di larghezza) e dati in contatori
elettromeccanici. I computer moderni riprendono questa architettura e normalmente hanno
memorie dedicate al programma e memorie dedicate ai dati (memorie Cache)
Architetture a confronto
L’architettura Von Neumann ha il vantaggio di essere flessibile: posso far girare grandi
programmi con pochi dati o, viceversa, piccoli programmi con una grande quantità di dati.
Per contro sul bus di comunicazione viaggiano sia codice che dati comportanfdo un
rallentamento della comunicazione tra microprocessore e RAM.
L’architettura Harvard è rigida: le dimensioni massime di programmi e dati sono vincolate a
quelle delle RAM che li ospitano.
Per contro i bus di comunicazione sono dedicati portando a un aumento della velocità. Infatti
questa architettura viene usata all’interno del processore (memorie cache) per velocizzare
l’esecuzione di cicli e dati ripetitivi.
9
Quasi tutti i linguaggi ad alto livello (Pascal, FORTRAN, COBOL...) sono linguaggi compilati.Il
BASIC, il Perl e il Python sono invece linguaggi interpretati: ciò vuol dire che non è possibile
creare un file eseguibile vero e propio con questi linguaggi, ma, ogni volta che voglio eseguire
un tale algoritmo, devo ricorre ad un interprete, ossia un programma che traduce istantaneamente
il codice ad alto livello in linguaggio macchina.
La via di mezzo è Java: una volta scritto un programma in Java, ho bisogno di compilarlo (ad
esempio, con il comando javac): da questo processo ho un file con estensione .class, scritto in un
linguaggio simile al linguaggio macchina, ma che non è linguaggio macchina (bytecode). A
questo punto posso eseguire il mio programma con l'interprete Java, che esegue il codice
contenuto nel file class. E' uno dei punti di forza del Java, che lo ha reso portabile verso ogni
piattaforma.
Ovviamente, i linguaggi compilati e interpretati hanno i loro pregi e difetti. Con un linguaggio
compilato posso creare un file eseguibile vero e proprio, totalmente indipendente dal linguaggio,
ma la procedura di precompilazione-compilazione linkaggio è spesso molto lenta (soprattutto
quando si tratta di compilare programmi dotati di GUI, di interfaccia grafica). Inoltre, il file
eseguibile che ho ottenuto dalla compilazione è ottimizzato per la macchina dove l'ho compilato,
non per un'altra. In poche parole, se compilo un file C su Linux, lo stesso file eseguibile non
funzionerà su Windows.
o, in alternativa,
Un ambiente di sviluppo integrato
10
Come editor di testo vanno bene anche l'EDIT del DOS o il Notepad su Windows, oppure, nel
caso si desideri un editor più avanzato, si può ricorrere a EditPlus o simili. Su Linux o un sistema
UNIX-like, le scelte sono molte: dagli editor storici, EMACS e VI, ad editor più user-friendly
(KWrite, KEdit, Kate, Gedit...). Di compilatori è possibile trovarne molti in rete, anche freeware
(il compito del compilatore è quello di tradurre il vostro programma scritto in C in linguaggio
macchina, creando quindi un file eseguibile). Sui sistemi Unix lo standard è il GCC, il
compilatore C della GNU che trovate pre-installato in molte installazioni standard. Su Windows
potete scaricare un porting gratuito di GCC per sistemi MS come DJGPP, oppure Dev-C++
(sempre GCC-based), o BCC (della Borland) oppure Visual C++.
Sintassi di base
Statement
Lo statement è l’entità di base del linguaggio: in parole povere, si tratta di un’istruzione
completa che, in qualche modo, elabora dei dati. Uno statement termina sempre con un ; (punto e
virgola) e può effettuare qualsiasi tipo di operazione: può addizionare due numeri, paragonare
due stringhe di caratteri, richiamare una funzione, dichiarare o assegnare un valore a una
variabile e molto, molto altro. In altre parole, ciascuno statement contiene un “comando” di
manipolazione dati. Per convenzione, ciascuno statement dovrebbe risiedere su una riga diversa
del codice sorgente.
char – è il tipo di dati che denota un singolo carattere e occupa 8 bit in memoria. Per
specificare una variabile di questo tipo, è sufficiente impartire lo statement char
nomeVariabile;.
int – questo tipo di dati denota i numeri interi. Occupa 16 bit di dati e può contenere
valori che vanno da -32767 a +32767.
float e double – identificano i numeri in virgola mobile semplici e i numeri in virgola
mobile doppi. Occupano rispettivamente 32 e 64 bit di dati e possono rappresentare
decimali con precisione di 6 cifre (float) e di 10 cifre (double).
void – , questo tipo di dati identifica il non valore, cioè un’entità che non occupa alcuno
spazio in memoria. In genere, il void viene specificato come valore di ritorno o come
input nelle funzioni che non io prevedono (come a voler dire al compilatore “fai
attenzione, questa funzione o questa operazione non ritorneranno valore alcuno o non
accettano valori in input”).
Tipi di dati strutturati
I dati strutturati (o strutture di dati) sono ottenuti mediante composizione di altri dati (di tipo
semplice, oppure strutturato).
Analizziamo la definizione; innanzitutto il concetto di "collezione" implica che tali oggetti siano
dello stesso tipo; prendendo spunto dal mondo reale, potremmo definire un array di mele, che,
quindi non può contenere nessun "oggetto pera"; un array è una collezione di variabili dello
stesso tipo. "Ordinata" implica che sia possibile identificare univocamente tutti gli oggetti
dell'array in modo sistematico; questo viene fatto tramite l'uso di indici numerici che, in un array
di dimensione N, vanno da 0 ad N-1 (fare attenzione che il primo elemento viene identificato da
0 e non da 1).
12
Definizione di variabili
Volendo formalizzarne il concetto, una variabile altro non è che un “contenitore”, situato in
memoria, che deve contenere valori, appartenenti a uno dei tipi visti sopra, che possono essere
modificati durante il flusso di esecuzione di un programma.
• Nome: identifica la variabile. Esempio: area (normalmente lettere maiuscole e minuscole sono
distinte – area è diverso da Area).
•Tipo: specifica il tipo del dato. Esempio: int area specifica il fatto che area rappresenta un
valore intero.
•Indirizzo: della cella di memoria che contiene il dato. Se il dato occupa più celle, questo è
memorizzato in celle consecutive e l’indirizzo è quello della prima cella (p.es. i float).
•Valore: dato rappresentato dalla variabile in certo momento dell’esecuzione. Può cambiare
(variabile) durante l’esecuzione.
Nome: Etichetta
Tipo: Forma del contenitore (es. capienza)
Indirizzo: Posizione sullo scaffale
Valore Contenuto
13
Dichiarare una variabile è semplicissimo: basta digitare uno statement che specifichi il tipo di
dato che la variabile deve contenere, il suo nome e, se necessario, il valore iniziale (specificato
con l’operatore di assegnazione =); dell’indirizzo non dobbiamo preoccuparci, viene assegnato
automaticamente al momento dell’esecuzione del programma. Per esempio, puoi dichiarare una
variabile intera vuota attraverso lo statement int variabile;, oppure assegnarle il valore iniziale
di 10 impartendo invece int variabile = 10. Per quanto riguarda gli array la dichiarazione è
leggermente diversa: int v[10] per dichiarare un array contenente 10 elementi; se vogliamo
anche inizializzare il contenuto un modo è: int v[3] = { 20, 30, 40 }
Una volta dichiarata una variabile, è possibile manipolarla nelle parti successive del codice
utilizzando l’assegnazione con l’operatore = (ad es. variabile=51; per gli array bisogna indicare
anche quale elemento viene modificato: v[2] = 51), gli operatori di incremento, di decremento e
tutto quanto previsto dal tipo di dati specificato.
Le stringhe
v. documento Le stringhe in C
Si definisce scope di una variabile l'area del codice nel quale un identificatore resta associato ad
un indirizzo di memoria (e quindi l'area di codice entro il quale una variabile mantiene il suo
valore).
In genere ogni blocco (cioè ogni gruppo di linee di codice racchiuso da parentesi graffe {})
definisce uno scope e ogni variabile locale ha come scope l'area di codice che inizia dalla
definizione della variabile stessa e termina con il blocco corrente.
14
Operatori principali
Come un po’ tutti i linguaggi di programmazione esistenti al mondo, anche la programmazione
richiede l’utilizzo degli operatori, cioè di alcuni caratteri che, all’interno di uno statement,
possono acquisire un significato “speciale”, combinando dunque i dati dello statement stesso. Il
più importante è l’operatore di assegnamento, cioè =, che permette di assegnare un valore (o il
risultato di una espressione più o meno complessa) a una variabile o una serie di variabili. Per
esempio, digitare lo statement int a = 1+2+3+4; significa dichiarare una variabile intera a e
attribuirle il valore iniziale di 10.
1. Operatori aritmetici – come il nome stesso lascia intendere, sono quelli che permettono
l’esecuzione di operazioni aritmetiche di base: il + denota l’addizione, il – la sottrazione,
il * la moltiplicazione, il / la divisione, il ++ l’incremento di uno (può essere posto a
destra o a sinistra della variabile da incrementare), il - - il decremento di uno (stessa
regola precedente) e, infine, il % restituisce il resto della divisione intera tra i valori.
2. Operatori relazionali – sono utili per mettere in relazione un valore rispetto all’altro, e
sono: > e < (maggiore/minore), >= e <= (maggiore/minore uguale a), == e != (uguale e
diverso). Essi si basano sul concetto di vero/falso: per esempio, in uno statement,
l’espressione var = 10>5 assegna a var il valore true o 1, poiché il numero 10 è
maggiore del numero 5.
3. Operatori logici – seguono lo stesso approccio vero/falso visto poc’anzi, ma vengono
utilizzati per confrontare il collegamento tra due relazioni, tipico dell’algebra booleana.
Essi sono && (and logico), || (or logico) e ! (il not).
4. Operatori bit a bit – essi permettono di intervenire sui singoli bit dei tipi di dato char e
int, effettuando esclusioni, spostamenti a destra/sinistra e confronti. Principalmente,
questi vengono impiegati nella scrittura di moduli per sistemi operativi o di driver di
periferica, in quanto permettono di intervenire singolarmente sui bit (o sulle word di file
scritti in assembly).
If/else
Impartire questa istruzione significa, di fatto, comunicare al programma qualcosa di simile a “se
si verifica una precisa condizione, allora esegui questa/e istruzione/i”. Sintatticamente, il
controllo si presenta nella forma
if (condizione) {
istruzioni
15
Se lo desideri, puoi specificare un’ulteriore condizione avvalendoti della clausola else: unita
all’if, essa permette infatti di astrarre il concetto “se si verifica una precisa condizione, allora
esegui queste istruzioni, altrimenti (else) esegui queste altre)”.
if (condizione) {
istruzioni
} else {
altre istruzioni
}
Puoi esprimere la condizione avvalendoti degli operatori che restituiscono risultati di tipo
true/false, cioè quelli logici e quelli relazionali. Tanto per farti un esempio concreto, ti propongo
una parte di codice che può determinare il valore massimo tra due variabili, stampandola a video.
if (a>=b) {
printf("Il valore della variabile a è maggiore o uguale di quello della variabile b\n");
}
else {
printf("Il valore della variabile a è minore di quello della variabile b\n");
}
Se necessario, puoi “arricchire” il costrutto visto poc’anzi con la clausola else if: sintatticamente
simile all’if, essa permette di specificare ulteriori condizioni specifiche da prendere in
considerazione, come spiegato di seguito.
if (condizione1) {
istruzioni per gestire condizione1
}
else if (condizione2) {
istruzioni per gestire condizione2
}
else {
istruzioni per gestire tutti gli altri scenari possibili
}
Switch/case
Il costrutto switch/case, esattamente come quello visto in precedenza, può influenzare
l’esecuzione sequenziale delle istruzioni presenti nel codice sorgente di un programma. Sebbene
il suo funzionamento sia simile a quello dell’if/else (cioè “saltare” a un dato insieme di istruzioni
al verificarsi di determinate condizioni), viene utilizzato in scenari abbastanza differenti: lo
switch/case, infatti, è adatto nelle decisioni dipendenti dal valore di una variabile e non da
un’operazione booleana. Nel linguaggio umano, puoi intenderlo un po’ come “salta al caso
(case) X se la variabile da analizzare (switch) assume il valore X, altrimenti esegui l’operazione
predefinita (default)”.
Per esempio, è possibile utilizzare lo switch/case nel caso di gestione dell’input: per esempio, il
programma assume un comportamento alla pressione del tasto a, un altro alla pressione del tasto
16
b, un altro ancora alla pressione del tasto c e così via. Sintatticamente, questo controllo
condizionale è espresso nel modo seguente.
switch(variabile) {
case: valore_variabile1
istruzione1;
istruzione2;
...
break;
case: valore_variabile2
istruzione3;
istruzione4;
...
break;
default:
istruzioni per gestire altre condizioni
break;
}
Al termine di ogni set di istruzioni per la gestione dei vari casi, è fondamentale specificare la
clausola break per uscire dal ciclo di esecuzione e non bloccare il programma in un loop
infinito, cioè in una condizione dalla quale non è in grado di uscire.
Ad esempio, questo estratto di codice potrebbe essere utilizzato per un programma che offre più
funzioni contemporaneamente, variabili in base all’input da tastiera (in questo caso, all’utente
viene chiesto di digitare le lettere a o c per scegliere la funzionalità desiderata).
char scelta;
printf("Scegli la funzione da utilizzare - a per aprire un file, c per chiuderlo\n");
scanf(%c, &scelta);
switch(scelta) {
case 'a':
istruzioni per aprire un file;
break;
case 'u':
istruzioni per chiudere un file;
break;
default
printf("Comando non riconosciuto!");
break;
}
Controlli Iterativi: i cicli while, for
In questa lezione presentiamo i costrutti che permettono di eseguire ciclicamente istruzioni, o
blocchi di istruzioni, fino al verificarsi di alcune condizioni. Questi costrutti prendono il nome di
strutture di controllo iterative o istruzioni di ciclo.
17
1. il while, che continua il suo ciclo fino a quando l'espressione associata non risulta falsa
2. il do-while, che agisce come il while, ma assicura l'esecuzione delle istruzioni associate
almeno una volta
3. il for, che è il costrutto più usato, versatile e potente tra i tre, ma che proprio per questo è
quello a cui bisogna prestare un po' più di attenzione.
Come per le istruzioni condizionali, quando parliamo di "istruzione" ci riferiamo ad una o più
istruzioni associate, ricordando che se le istruzioni sono due o più bisogna usare le parentesi
graffe per delimitarle, mentre una singola istruzione non ne ha necessariamente bisogno.
While
La struttura del while è la seguente:
while (condizione)
istruzione;
Generalmente l'istruzione o le istruzioni all'interno del while agiscono sulla condizione che il
while aspetta essere falsa per poter uscire dal ciclo, questo perché altrimenti il ciclo non
terminerebbe. Ad esempio per stampare a video una successione di cifre da 0 a 99, proponiamo il
codice seguente:
int i = 0;
while (i != 100)
{
printf("%d n", i);
i++;
}
Do - While
Molto simile al while è il do-while che ha la seguente struttura:
do
istruzione;
while (condizione)
18
Tenendo valide tutte le considerazioni fatte per il while, va notato che in questo modo si esegue
l'istruzione all'interno del do-while almeno una volta, indipendentemente dal fatto che la
condizione associata al while risulti vera o falsa. L'esempio seguente mostra come sia possibile
utilizzare il do-while, ad esempio, per porre una domanda all'utente e continuare nell'esecuzione
solo se l'utente risponde correttamente, oppure ripetere la domanda:
do {
printf("Premere 1 per continuare : ");
scanf("%d", & valore);
printf("n");
} while (valore !=1)
Ciclo For
Prima di spiegare le caratteristiche del for, ne proponiamo la struttura generale:
inizializzazione
while (condizione)
istruzione;
incremento
Il for, quindi, potrebbe tranquillamente essere sostituito da un while, se non fosse che è più
conciso ed è concettualmente usato quando sappiamo a priori il numero di iterazioni che
vogliamo fare. I parametri all'interno del for hanno diversi compiti, e sono separati da un punto e
virgola:
1. Il primo viene eseguito prima di entrare nel ciclo, ed inizializza una variabile,
generalmente la variabile è usata come variabile di controllo del ciclo, in poche parole
servirà per tenere traccia del numero di iterazioni del ciclo; a differenza del C++, dove la
dichiarazione può essere fatta contemporaneamente all'inizializzazione, nel C, in questo
costrutto, la variabile deve essere dichiarata fuori, prima del for;
2. Il secondo è la condizione (che coinvolge anche la variabile di controllo), che se risulta
falsa interrompe l'esecuzione del ciclo;
3. Il terzo parametro è l'istruzione di incremento, che viene eseguita dopo ogni ciclo del for;
questa istruzione agisce generalmente sulla variabile di controllo incrementandone (o
decrementandone) il valore.
Per chiarire meglio l'uso del for presentiamo un semplice codice che conta da zero a cento,
stampando a video la variabile di controllo:
Ognuna delle tre istruzioni all'interno del for può essere omessa, con il risultato di condizionare
il ciclo, o non modificando la variabile, o facendo essere sempre vera la "condizione" del for; in
questo ambito bisogna citare un uso particolare del ciclo for, che è quello del cosiddetto "ciclo
for infinito". Un ciclo che non termina, abbiamo detto precedentemente, è da considerarsi errore,
ma esiste una situazione in cui vogliamo, invece, ciclare all'infinito fino a quando l'utente non
decide di interrompere volontariamente il programma, ad esempio avendo un menu generale che
ogni volta deve presentarsi sullo schermo. Il for utilizzato in questo caso ha una struttura
particolarissima, ovvero non ha nessuno dei tre parametri sopra esposti, ma contiene solamente
due caratteri punto e virgola:
main ()
{
for ( ; ; )
{
printf ("premi CtrL+Z per interromperen");
}
}
è anche possibile utilizzare all'interno delle tre istruzioni, più di una variabile di controllo, in
modo da avere un ciclo dinamico; un esempio può essere quello di due variabili, "high" e "low"
che convergono:
int high;
int low;
for (high=100, low=0; high >= low; high--, low++)
printf("H=%d - L=%d n", high, low);
Le funzioni
Quando un problema si presenta immediatamente complesso una tecnica per trovarne la
soluzione consiste nel scomporre il problema in sottoproblemi più semplici, tenendo presente le
loro relazioni. In questo modo è possibile realizzare un algoritmo di risoluzione complessivo
ottenuto da una serie di algoritmi più semplici, opportunamente assemblati.
Vengono definite, dunque, le parti fondamentali di cui si compone il programma (top) si procede
poi allo sviluppo dettagliata di ogni singola parte (down). Per scomporre un problema in
sottoproblemi si evidenzia cosa debba essere fatto piuttosto che come realizzarlo.
20
In altre parole, la funzione è l’insieme di statement atti a manipolare i dati in ingresso (alla
funzione) e produrre dati in uscita (dalla funzione) che siano in linea con le necessità del
programmatore
Il valore che la funzione deve restituire è espresso attraverso la clausola return, specificata
subito prima della fine del codice della funzione stessa; tutti gli statement che ne fanno parte,
inoltre, devono essere racchiusi tra due parentesi graffe. Dopo essere stata dichiarata e definita,
la funzione può essere utilizzata in qualsiasi altra parte del codice sorgente, specificando ove
necessario gli eventuali valori d’ingresso e, se richiesto, assegnando il valore di uscita a una
variabile. Nel codice seguente, il valore della variabile somma viene impostato a 9.
Dopo il nome della funzione sono obbligatorie le parentesi tonde che contengono l'elenco degli
argomenti passati alla funzione, detti parametri formali.
I parametri attuali, sono quelli nominati nel punto dell'invocazione della funzione.
Le istruzioni eseguite dalla funzione sono contenute all'interno delle parentesi graffe. Il corpo
della funzione contiene come ultima istruzione la parola return seguita dal valore della variabile
contenente il risultato restituito dalla funzione.
Al suo interno possono essere utilizzate variabili ed espressioni condizionali di ogni tipo,
richiamate funzioni appartenenti alle librerie specificate tramite la clausola #include, funzioni
21
dichiarate e definite all’interno dello stesso corpo del codice sorgente e così via. Una funzione
main non deve essere dichiarata, ma deve essere definita direttamente all’interno del codice
(solitamente questo viene fatto dopo la dichiarazione di variabili e funzioni “globali”, cioè
utilizzabili da tutte le funzioni del sorgente, e prima della loro definizione).
A questo punto, è assolutamente semplice intuire che un codice sorgente, affinché possa essere
eseguito, deve contenere almeno una funzione main().
int somma(a,b) {
return a+b;
}
int main(void) {
int x = 5, int y = 6, int somma;
somma = somma (x, y);
printf("Il valore finale è %i", somma);
return 0;
}
Perché si usano le funzioni?
Naturale
Una tecnica naturale per trovarne la soluzione di un problema consiste nel scomporre il problema
in sottoproblemi più semplici, tenendo presente le loro relazioni. In questo modo è possibile
realizzare un algoritmo di risoluzione complessivo ottenuto da una serie di algoritmi più
semplici. Per scomporre un problema in sottoproblemi si evidenzia cosa debba essere fatto
piuttosto che come realizzarlo.
Affidabile
Un sistema è tanto più affidabile quanto più raramente si manifestano malfunzionamenti. Il
numero di malfunzionamenti è tanto minore quanto minore è il codice. Voglio che sia facile
modificare una parte senza influire sul resto del sistema.
Riusabile
Una volta che un problema è stato risolto, la soluzione va riutilizzata.
Manutenibile
Dal 60% all' 80%, il tempo impiegato su un programma va in manutenzione. Lo sviluppo occupa
solo il 20% - 40%! Più localizzati sono gli errori meglio riesco a intervenire. Meno codice scrivo
meno sbaglio! Se riscrivo il contenuto di una funzione (miglioro l’algoritmo) pur di mantenere
l’interfaccia il resto del sw funziona lo stesso e anche meglio.
Estendibile
Il software deve crescere e cambiare nel tempo per rimanere utile. Voglio poter aggiungere
funzionalità in modo trasparente.
Tempestivo
Più rapido è il ciclo di sviluppo pur fornendo software affidabile e riusabile, prima arrivo sul
mercato. Più team lavorano in parallelo, prima finisco. Meno scrivo, prima finisco (meno errori,
meno test).
22
Esercizi
Controlli condizionali
Esercizio 1
Scrivi una funzione che dati due numeri interi ne stampi il maggiore; se i due numeri
sono uguali deve essere stampata la somma dei due numeri.
Esercizio 2
Scrivi una funzione che dato il prezzo netto di un prodotto lo sconti del 35% se tale
prezzo è maggiore di 100€ e poi aggiunga l'IVA del 20% stampando a video il
risultato finale.
Esercizio 3
Scrivi una funzione che dati tre numeri interi scriva in uscita chi è il più alto, il più
basso e il medio fra i tre numeri inseriti.
Esercizio 4
Scrivi una funzione che dati due numeri interi esegua :
A) La loro somma se uno dei due è pari.
B) Il loro prodotto se tutti e due sono pari.
C) La loro divisione se nessuno dei due è pari.
Iterazioni
Esercizio 5
Scrivere una funzione che riceve in ingresso un numero e ne calcola la sua potenza n-
esima.
Esercizio 6
Scrivi una funzione per il calcolo del fattoriale di un numero dato.
Esercizio 7
Usa un ciclo do-while per eseguire il prodotto di due numeri interi attraverso somme
successive.
Esercizio 8
Utilizza un ciclo while per dividere due interi attraverso sottrazioni successive,
fornendo in uscita il quoto e il resto della divisione.
Esercizio 9
Scrivi una funzione che inserito un numero intero da tastiera dica se tale numero è
primo o no.
23
Altri Esercizi
1. Determinare il valore assoluto di un numero
2. Determinare se un anno è bisestile
3. Convertire in binario un numero decimale. Visualizzare le cifre nell’ordine corretto.
4. Dato un numero intero positivo stampare le singole cifre
5. Dato un numero determinare se è triangolare (somma dei primi N interi)
6. Dato un umero determinare se è quadrato (somma dei primi N interi dispari)
7. Determinare se un numero è sferico (il quadrato termina con le stesse cifre: 62 =36, 762 =
5776)
8. Trovare quel numero che moltiplicato per 9 da un numero di soli 1 (12345679)
9. Trovare il primo numero ciclico (numero di n cifre che moltiplicato per un numero da 1 a
n, dà come risultato n numeri con le stesse cifre, in ordine traslato, del numero di
partenza e moltiplicato per n+1, dà come risultato una sequenza di n cifre 9 - ovvero 10n-
1- ex 142857).
10. Dato un numero determinare se appartiene alla serie di fibonacci
11. Trovare quante volte un numero è divisore di un altro
12. Espandere un numero nei suoi fattori primi
13. Radice quadrata: Q = SQRT(R) Qi = ½ (Qi-1 + R/Qi-1) (ex: Q2 = (4.5 + 9/4.5)/2 =
(6.5)/2 = 3.25 )
14. Calcolo di pi greco: arctan(1) = 4 ( 1 – 1/3 + 1/5 – 1/7 · · · ) .
15. Algoritmo di Euclide per il MCD
16. Calcolare il minimo comune multiplo di tre nuermi dati.
24
Esercizio 1
Dato un vettore di 8 numeri interi stampare in uscita la somma e la media dei valori in esso contenuti.
Esercizio 2
Dato un vettore di 8 numeri interi individuare e stampare a video il massimo e il minimo con la loro posizione nel
vettore.
Esercizio 3
Caricare in modo random un vettore di 10 numeri interi e stamparlo a video: individuare quanti numeri pari e quanti
numeri dispari ci sono nel vettore.
Esercizio 4
Sviluppare un'algoritmo che permetta di riempire un vettore di 5 elementi interi con 5 numeri compresi fra 0 e 9
(inclusi) in modo casuale (random) e senza che vi siano ripetizioni. (questo algoritmo è molto importante i fini della
simulazione di fenomeni come il gioco del lotto o l'estrazione di una carta da un mazzo: ossia estrazioni che non
prevedono la possibilità della reimmissione dell'elemento scelto da un dato insieme).
Esercizio 5
Eseguire l'ordinamento di un vettore di interi tramite l'algoritmo di scambio (bubble sort).
Esercizio 9
Realizza un programma per convertire un numero binario nel corrispondente valore decimale (somma delle
potenze).
Altri Esercizi
1. Determinare se una stringa è palindroma.
2. Invertire il contenuto di un array numerico.
3. Crivello di Eratostene
4. Distanza di Hamming
5. Moltiplicare per 11
6. Complemento a uno
7. Mescolare un mazzo di carte