Sei sulla pagina 1di 24

1

Elementi base di programmazione

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;

for (i = 0; i < n; i++) {


if (i <= 1) {
next = i;
} else {
3

next = first + second;


first = second;
second = next;
}

printf("%d ", next);


}

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 teorema di Bohm-Jacopini ha un interesse soprattutto teorico, in quanto i linguaggi di


programmazione tendono a dotarsi di più tipi di istruzioni, non sempre “rispettose” del teorema,
ma utili per la realizzazione di programmi di facile scrittura e comprensione
4

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

select case (choice)


case (1)
goto 20
case (2)
goto 30
case (3)
goto 40
case (4)
stop
case default
goto 10
end select

20 write(*,*) "Hai scelto l'Opzione 1."


goto 10
30 write(*,*) "Hai scelto l'Opzione 2."
goto 10
40 write(*,*) "Hai scelto l'Opzione 3."
goto 10

end program MenuProgram

Linguaggio di programmazione, programma, elaboratore, informatica.


Linguaggio di programmazione: è un linguaggio formale ovvero
dotato di alfabeto e grammatica ben definiti che viene utilizzato
per tradurre algoritmi dal loro linguaggio a quello comprensibile
dall’elaboratore.
5

Un algoritmo con i dati da lui elaborati posto in forma comprensibile al computer mediante un
linguaggio di programmazione viene chiamato programma

L’Elaboratore elettronico è un dispositivo in grado di ricevere, memorizzare e trasmettere dati e


di svolgere operazioni matematiche e logiche sugli stessi mediante opportuni algoritmi.

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

Dato Informazione Astrazione.


Dato, È il participio passato del verbo dare. Dato: è qualcosa di percepibile dai sensi ma che non
ha alcun valore fino a quando non viene posto in un contesto di riferimento (appropriato). I dati
sono immediatamente presenti alla coscienza prima di ogni elaborazione di pensiero. I dati
nascono dall'osservazione di aspetti e fenomeni elementari. 21 è un dato. Il dato è una entità
autoreferenziale svincolata da ogni contesto. Ovvero è isolata, non è in riferimento con altri dati.
Quando più dati correlati tra di loro sono in relazione, abbiamo un contesto. Il contesto può far
assumere significati diversi al dato: Per lo Stato la maggiore età (18 anni) comporta la possibilità
di votare. Per la Scuola la maggiore età (18 anni) comporta a possibilità di giustificarsi da soli

Informazione: è l’atto di porre uno o più dati in un contesto


di riferimento. L’informazione permette di trarre
conclusioni, il dato no. Se parlo di età, 21 mi dice che
l’individuo è maggiorenne. Se parlo di fogli di carta, posso
concludere che parlo della larghezza di un foglio A4. Se è
la temperatura esterna d’inverno fa caldo. Se è la
temperatura esterna d’estate fa freddo. Se 5 è la temperatura
esterna so che devo vestirmi: se è il voto nel compito so che
…..

Il cambio o l’aggiunta di un nuovo contesto è spesso alla


base dell’umorismo

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.

La memoria del computer


La memoria del computer è suddivisa in memoria centrale e memoria di massa. La memoria
centrale è volatile e non permanente. Si cancella automaticamente quando il computer viene
spento. I dati sono registrati nella memoria centrale sotto forma di bit raggruppati in locazioni
dette celle di memoria.
6

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:

000 = 0, 001 = 1, 010 = 2 , 100 = 4, 011 = 3, 110 = 6, 101 = 5, 111 = 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.

Qual è la differenza tra bit e byte?


A causa della somiglianza dei termini, bit e byte sono facilmente confondibili. I bit sono usati
principalmente per rappresentare i dati in memoria ma anche la velocità di trasmissione in
Internet, telefonia e servizi di streaming. La velocità di trasmissione si riferisce alla quantità di
bit trasmessi per secondo (b/s).

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 Von Neumann


Nell’architettura Von Neumann programmi e dati sono archiviati nella stessa memoria. Il
concetto è stato realizzato da un matematico John Von Neumann nel 1945 e attualmente funge
da base per quasi tutti i computer moderni. La macchina von Neumann è costituita da un
processore centrale, una memoria volatile, archiviazione di massa, input e output.

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

Linguaggi compilati,, interpretati, misti


Il C è un linguaggio compilato: questo vuol dire che, una volta scritto il file sorgente (o i files
sorgenti), occorre che questo venga passato al compilatore C assieme al nome del file in cui si
desidera piazzare l'output. Con GCC faremo gcc -o file_eseguibile file1.c file2.c ... filen.c[1]
Ecco il compilatore cosa fa: Per prima cosa esegue le direttive al preprocessore (quelle che
iniziano con #, come #include #define #if #endif #ifdef... alcune le vedremo nel corso di questo
tutorial). Se non ci sono errori nei sorgenti, traduce il codice C contenuto nei files sorgenti in
linguaggio macchina (in quanto è questo l'unico linguaggio davvero comprensibile al
compilatore. In genere questo processo genera un file oggetto, con estensione .o o .obj, dove
viente piazzato il codice in LM), quindi esegue l'operazione di linking, ossia crea il file
eseguibile vero e proprio.

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.

Un linguaggio interpretato, invece, permette di vedere in real-time se il programma che si sta


scrivendo contiene o no errori, senza a avviare la procedura di compilazione. Inoltre, un listato
scritto, ad esempio, in Perl su un sistema Linux funzionerà anche se lo porto su un sistema
Windows, a patto che vi sia installato l'interprete Perl. Però questi linguaggi hanno lo svantaggio
di non creare un file eseguibile, ossia di non creare un vero e proprio programma da eseguire
facendo un doppio click sopra.

Cosa serve per programmare in C


.Strumenti necessari per la programmazione in C
 Un editor di testo
 Un compilatore
 Un linker

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++.

In alternativa, potete far ricorso ad un ambiente di programmazione integrato (ossia un


programma che ha già incorporato editor e compilatore); su Windows c'è Visual C++, oppure
potete scaricare (gratuitamente) LCC o Rhide, un IDE basato su GCC, o lo stesso Dev-C++. Su
Linux c'è l'ottimo KDevelop (per ambienti KDE), o Anjuta (per ambienti Gnome o Gtk-oriented)
Debugger
Il debugging (o semplicemente debug), in informatica, nell'ambito dello sviluppo software,
indica l'attività che consiste nell'individuazione e correzione da parte del programmatore di uno o
più errori (bug) rilevati nel software,direttamente in fase di programmazione.
L'attività di debug è una delle operazioni più importanti e difficili per la messa a punto di un
programma, spesso estremamente complicata per la complessità deisoftware in uso e delicata per
il pericolo di introdurre nuovi errori ocomportamenti difformi da quelli desiderati nel tentativo di
correggere quelli per cui si è svolta l'attività di debug.
Questa attività è supportata da programmi specifici (debugger) messi a disposizione dall'IDE:
grazie all'uso di breakpoint, ovvero interruzioni suopportune linee di codice, lo sviluppatore può
eseguire il programma passo passo, istruzione per istruzione, e ispezionare il contenuto delle
variabili per verificare la coerenza con i risultati voluti.
In assenza di tali strumenti per le attività di debugging, si ricorre alla più semplice, ma anche
meno efficace tecnica di stampare a video o su file le istruzioni che il programma sta eseguendo,
inserendo a tal scopo nel codice delleistruzioni di debug che evidenzino il punto di arrivo
dell'esecuzione delprogramma fino all'errore. Sempre a questo scopo, il programmatore può,
conl'ausilio dei commenti, far eseguire solo alcune parti del codice o al contrarionon far eseguire
particolari parti del codice, sospette di causare l'errore. Unavolta individuato l'errore nel codice il
programmatore corregge l'errore inmaniera iterativa finché il programma non fa ciò che è
desiderato.
11

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.

Tipi di dati semplici o primitivi


Sono tipi di dati che fanno riferimento a uno e un solo valore: un solo numero, un solo carattere,
un solo valore booleano, .... . Di seguito l’elenco dei tipi di dati primitivi più utilizzati e le
informazioni necessarie su ciascuno di essi.

 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).

Gli array o vettori


Un array o vettore può essere definito come una "collezione ordinata di oggetti".

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.

Una variabile è caratterizzata dalle seguenti proprietà:

• 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, Tipo, Indirizzo non variano a tempo di esecuzione

Esempio: Le variabili come contenitori etichettati e posizionati su scaffali.

Nome: Etichetta
Tipo: Forma del contenitore (es. capienza)
Indirizzo: Posizione sullo scaffale
Valore Contenuto
13

Chiariamo anche la differenza tra la dichiarazione di una variabile e la sua definizione o


assegnazione: nel primo caso, si avvisa il compilatore che bisogna allocare una porzione di
memoria per contenere dei dati; nel secondo caso, invece, si va a riempire la memoria con i dati
richiesti. È possibile dichiarare una o più variabili senza definirle, ma non è possibile definire
una o più variabili senza dichiararle (il compilatore, in tal caso, non saprebbe dove piazzare i dati
in memoria).

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

Scope di una variabile


Tieni sempre ben presente che, per essere utilizzata, una variabile deve essere necessariamente
dichiarata in una parte precedente del codice: ricordiamo che ogni linguaggio di
programmazione è sequenziale, dunque non puoi chiedere a uno statement di operare su un dato
che non conosce!

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.

Oltre all’operatore di assegnamento, esistono numerosi altri operatori : essi si dividono in


quattro categorie ben distinte.

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).

Controllo del flusso


Controlli condizionali
Come spiegato all’inizio, il linguaggio è sequenziale, cioè in grado di eseguire istruzioni nello
stesso ordine in cui compaiono nel codice sorgente del programma. Esistono tuttavia delle
direttive che permettono di modificare il flusso di esecuzione del programma, modificandone
l’ordine al verificarsi/non verificarsi di determinate condizioni: queste direttive prendono
l’ordine di controlli condizionali. Nelle righe a venire ti illustro i più utilizzati.

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

Le istruzioni di ciclo sono una delle componenti fondamentali della programmazione e


permettono di risparmiare la quantità di codice scritta rendendo quindi il programma più leggero
e più facile da comprendere. Le istruzioni di ciclo, come le istruzioni condizionali, hanno
bisogno che alcune condizioni vengano verificate affinché il ciclo continui nella sua opera o si
interrompa.

Le istruzioni di ciclo sono tre:

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++;
}

Affinché il while possa verificare la condizione associata, è necessario aver dichiarato la


variabile prima del while, questo, come nell'esempio, può essere fatto nella riga soprastante o in
un altra parte del programma.

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:

for (inizializzazione ; condizione ; incremento)


istruzione;

che equivale alla rappresentazione con un while con la seguente struttura:

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:

for (int i=0; i<=100; i++)


printf("%d n", i);
19

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.

Questo "modus operandi" di tipo gerarchico viene


chiamato metodo top-down, ossia "dall'alto verso il
basso", dove "alto" e basso" si riferiscono al livello di
dettaglio o astrazione. Il livello pù alto (top) è quello che
descrive la procedura generale di risoluzione del
problema, o problema principale, utilizzando descrizioni
sommarie di quelli che saranno i suoi passi
fondamentali, o sottoproblemi.

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

L'approccio informatico al metodo top-down è quello di utilizzare dei sottoprogrammi da


definire in un secondo momento ed è questo il classico approccio della programmazione
procedurale il cui elemento fondamentale sono le funzioni.

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

int aggiungi(int a, int b) { //funzione per eseguire una somma


return a+b;
}

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.

int somma = aggiungi(5, 4);

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.

La funzione “speciale” main()


Ora che hai tutte, ma proprio tutte le nozioni di base di questo linguaggio, è arrivato il momento
di spiegarti il funzionamento di una funzione piuttosto speciale, quella da cui parte l’esecuzione
di ogni codice sorgente : la funzione main(). Essa rappresenta il flusso di esecuzione principale
di un codice sorgente e contiene tutte le istruzioni necessarie a elaborare i dati richiesti dal
programmatore. In altre parole, la funzione main() è il “motore” di un programma C, che
permette di eseguire l’algoritmo principale progettato dal programmatore stesso.

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

Variabili strutturate: gli array

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

Potrebbero piacerti anche