Iniziamo la lezione introducendo la struttura generale di un programma in linguaggio C :
< istruzioni per il preprocessore >
[ < definizione delle funzioni > ]
< dichiarazione di tipi e variabili >
main ()
{
< dichiarazione di tipi e variabili >
[ < chiamata delle funzioni > ]
< istruzioni direttamente eseguibili >
}
dove main () e’ la funzione principale, ossia la funzione che deve essere sempre
presente in un programma C e che viene eseguita sempre per prima . Le istruzioni direttamente eseguibili sono una lista di istruzioni (che verranno tradotte nel programma
in LM ). Sugli altri concetti di preprocessore, di funzione si tornera’ piu’ in la' nel corso.
Il C ammette:
dichiarazioni per associare locazioni di memoria centrale ( indirizzi ) disponibili
con nomi
;
istruzioni di lettura da Tastiera ( UL = Tastiera ) , scrittura sul video ( US = Video ), somma del contenuto di due locazioni in MC (memoria centrale) ed inserendo il risultato
in una terza locazione.
Dati due numeri , calcolare e stampare la somma fra essi.
Sicuramente abbiamo bisogno di due locazioni di memoria , una per il primo numero
e l’ altra per il secondo numero; inoltre ci servira’ una locazione per memorizzare la somma fra i due numeri.
Come si puo’ intuire associamo la locazione :
per il primo numero al simbolo primo
per il secondo numero al simbolo secondo
per il numero somma al simbolo somma
1) leggere un numero da input e memorizzarlo nella cella di memoria associata al
simbolo primo
2) leggere un numero da input e memorizzarlo nella cella di memoria associata al
simbolo secondo
3)
eseguire la addizione fra primo e secondo
somma < ----- primo + secondo
mettere il risultato nella cella di memoria
associata al simbolo somma
4) stampare somma.
L’ algoritmo presentato non puo’ essere tradotto in linguaggio macchina per 2 diversi
motivi
a) perche’ non abbiamo deciso gli indirizzi IND
b) perche’ il passo 3) dell'algoritmo non e’ esprimibile con una istruzione macchina.
Trascurando l’operazione di addizione SUM il programma in LM corrispondente, una
volta decisi gli indirizzi e’ :
associazione indirizzi ai simboli
primo < --- > IND1 = 01010
secondo < --- > IND2 = 01011
somma < --- > IND3 = 01100
Programma in linguaggio macchina
Read da input in 01010
Read da input in 01011
Load da 01010 in R1
Load da 01011 in R2
SUM
Store R1 in 01100
Print in output 01100
Il compilatore il linker ed il loader si occupano della traduzione di un programma da un linguaggio ad alto livello come puo’ essere il C nel corrispondente programma in
linguaggio macchina in MC pronto per l’ esecuzione.
L’esecuzione di un programma coincide con l‘esecuzione di una sequenza di istruzioni (flusso di esecuzione).
Per descrivere l’algoritmo come sequenza di passi si usano delle forme grafiche in cui
il flusso di esecuzione e’ rappresentato dal simbolo freccia ---- > .
Introduciamo delle convenzioni grafiche per esprimere un programma in termini del suo flusso di esecuzione ( programma espresso attraverso i diagrammi di flusso ):
Fig. 7.1
Consideriamo il seguente diagramma in Fig. 7.3 che esprime, in forma grafica, un programma che :
Stampa D ( il nuovo valore di D )
poi ripete
e cosi’ via fino all’infinito
Seguendo il flusso , nel tratto di destra in rosso, avremo che:
Ripete all’ infinito
In entrambi i casi i programmi sono NON TERMINANTI
Esempio di comportamento del programma percorrendo il flusso di sinistra :
Input |
7 |
4 |
3 |
. |
Output |
8 |
5 |
4 |
. |
Esempio di comportamento del programma quando si percorre il flusso di destra :
Input |
7 |
|
|
|
Output |
8 |
9 |
10 |
. |
Il flusso di esecuzione puo’ essere deviato per seguire strade alternative. Questo viene realizzato con un TEST , il quale coincide con la verifica di una condizione logica, che
puo’ valere SI oppure NO. Se la CONDIZIONE e’ vera ( SI ) il flusso di esecuzione
prosegue in una direzione altrimenti, nel caso in cui la condizione vale falso ( NO )
si prosegue nell’altra direzione.
Indichiamo per brevita’ la CONDIZIONE LOGICA con COND.
Progettiamo adesso un programma, con i diagrammi di flusso ,che dati due numeri
interi stampi il maggiore fra i due. Nel TEST indichiamo il simbolo PRIMO con PR. ed il simbolo SECONDO con SEC. .
Il diagramma e’ abbastanza esplicativo del programma che in italiano potremmo cosi’ tradurre:
1) AVVIO
2) Lettura del PRIMO numero
3) Lettura del SECONDO numero
4) Se il PRIMO numero e’ minore del SECONDO numero allora:
5) Stampa SECONDO numero
6) Altrimenti Stampa PRIMO numero
7) FINE
Questo programma sia che lo esprimiamo come diagramma che come algoritmo e’ traducibile in qualsiasi linguaggio di programmazione.
Ad esempio in linguaggio Pascal avremo il seguente programma (traducibile automaticamente in un programma eseguibile maggiore.exe; notate che e’ anche
automatica la scelta degli indirizzi) :
program maggiore;
var PRIMO, SECONDO;
begin
read
( PRIMO );
read
( SECONDO );
if
( PRIMO < SECONDO )
then
write ( SECONDO
)
else
write (
PRIMO );
end ;
end.
Nel linguaggio C lo stesso programma potremmo cosi’ tradurre, anticipando che l’istruzione if - then - else del Pascal diventa
if - else in C, che ha la seguente sintassi:
if (< espressione> )<istruzione1> else <istruzione2> .
Ossia se risulta vera l’espressione fra parentesi tonde “ CONDIZIONE LOGICA VERA”
allora viene eseguita l’istruzione1 altrimenti “ CONDIZIONE LOGICA FALSA”, viene
eseguita l’istruzione2. Inoltre l’ istruzione di lettura nel C e’ scanf(…) e l’istruzione di
stampa e’ printf(..) ; o meglio scanf(..) e printf(..) sono delle funzioni di libreria del C
( contenute nel file stdio.h “ standard di input output ” ) che usiamo come se fossero
delle semplici istruzioni.
#<include stdio.h>
void main(void)
{
int PRIMO, SECONDO;
scanf (“\n%d”,&PRIMO);
scanf (“\n%d”,&SECONDO);
if ( PRIMO<SECONDO)
printf (“\n %d”, SECONDO);
else
printf (“\n %d”, PRIMO);
}
Traduzione in linguaggio macchina LM del programma rappresentato in Fig. 7.5
Supponiamo di avere come istruzione di salto condizionato una JUMP tale che:
Salta in A se R1 < 0 ( con codice operativo 1111 ) ;
traduciamo il programma in codice simbolico e poi in LM vero e proprio (solo zeri e uni);
associamo ai simboli PRIMO l’indirizzo IND1 e SECONDO l’indirizzo IND2
PRIMO < ---- > IND1 ; SECONDO < ----- > IND2 ;
introduciamo due etichette ETIC1 ed ETIC2 per contrassegnare due righe di codice
del programma ( gli indirizzi di memoria ).
programma in LM simbolico :
Read da I in IND1
Read da I in IND2
Load da IND1 in R1
Load da IND2 in R2
Sub ( una istruzione che dovrebbe eseguire la R1 < --- R1 - R2 )
Salta in ETIC1 ( se R1 < 0 )
Write IND1
Salta in ETIC2 ( incondizionatamente )
ETIC1: Write IND2
ETIC2: STOP
In sostanza se R1 < 0 l’esecuzione del programma prosegue dall’indirizzo di memoria avente etichetta ETIC1 ( stampa del contenuto di IND2 ) altrimenti prosegue con la
istruzione successiva ossia viene stampato nel nostro caso il contenuto di IND1 e
poi si salta in ETIC2 dove ci si ferma .
Adesso associando a IND1 il valore 11001 e ad IND2 il valore 11010
introducendo il codice operativo della istruzione Sub che e’ pari a 0101 e
conoscendo quello dell’istruzione di Salta in ETIC1 ( se R1 < 0 ) che e’ pari a 1111
e supponendo che il programma sia caricato in memoria all’indirizzo 1010 in base 2,
si puo’ scrivere il vero e proprio programma in linguaggio macchina, poiche’ gli altri
codici operativi dovrebbero essere noti,dalla tab. 3.2 della lezione 3.
nota: abbiamo deciso arbitrariamente di usare tali codici operativi.
Tab. 7.3
Indirizzo Cella |
Codice operativo |
Indirizzo operando |
|
10 |
01010 |
1000 |
11001 |
11 |
01011 |
1000 |
11010 |
12 |
01100 |
0000 |
11001 |
13 |
01101 |
0001 |
11010 |
14 |
01110 |
0101 |
------- |
15 |
01111 |
1111 |
10010 |
16 |
10000 |
1001 |
11001 |
17 |
10001 |
0111 |
11010 |
18 |
10010 |
1001 |
11010 |
19 |
10011 |
0110 |
------- |
… |
……. |
…… |
…… |
25 |
11001 |
PRIMO |
|
26 |
11010 |
SECONDO |
Dati 3 numeri interi, stampare il massimo fra essi.
Scrivere il programma : in forma di algoritmica, di diagramma di flusso e
corrispondente traduzione in linguaggio C.
1) Leggere primo , secondo e terzo numero
2) Se primo > secondo
3) Allora stampare il max tra primo e terzo
4) Altrimenti stampare il max tra secondo e terzo
5) Fine
Nel diagramma trascuriamo la lettura di primo , secondo , e terzo ; indichiamo per
comodita’ i tre simboli rispettivamente con P , S , T .
Programma espresso in
linguaggio C :
#include<stdio.h>
void main(void)
{ /* BEGIN */
int P, S, T; /* commento: dichiarazione dei simboli ( variabili) */
scanf (“\n %d”,&P); /* lettura del primo numero intero da input */
scanf (“\n %d”,&S); /* lettura del secondo numero intero da input */
scanf (“\n %d”,&T); /* lettura del terzo numero intero da input */
if ( P >
S )
{
if
( P > T)
printf (“\n Il numero max e’ = %d ” , P ); /* stampa il primo numero */
else
printf ( “\n Il numero max e’ = %d ” , T ); /* stampa il terzo numero */
}
else
/* le istruzioni successive vengono eseguite se P e’ minore od
uguale ad S */
if ( S
> T )
printf (“\n Il numero max e’ = %d ” , S ); /* stampa il secondo numero*/
else
printf (“\n Il numero max e’ = %d ” , T ); /* stampa il terzo numero */
/* STOP */
}
Esercizio (svolgere da soli)
Scrivere in linguaggio C un programma che stampi il Massimo fra 4 numeri
interi dati in Input.
(Invio Soluzione: tramite e-mail agli iscritti alla mailing list, solo
dopo invio vostra soluzione)
Bozza di algoritmo da seguire
1) Leggere i 4 numeri interi da input e memorizzarli ( Servono 4 variabili P, S ,T, Q)
2) Se P > S
a) stampare il massimo tra P, T e Q .
b) altrimenti stampare il massimo tra S, T e Q
e’ un esercizio un po’ piu’ lungo di quello visto
sfruttare l’ esercizio precedente come sottocaso
raffinamento :
a’) se P > T allora stampare il massimo tra P e Q
b’) altrimenti stampare il massimo tra T e Q
b’’) fatelo voi
a”) fatelo voi
b’’) fatelo voi .
Il
concetto di variabile
Una
variabile e’ una astrazione della cella (locazione) di memoria, ossia
formalmente e’ :
un simbolo , cioe’ un identificatore ( nome ) associato ad un indirizzo fisico della
memoria, che
denota un valore contenuto nella locazione stessa;
(locazioni
di memoria che possono essere di 1 byte, 2 byte, 4 byte ..).
Se chiamiamo L-Valore l’ indirizzo fisico della memoria e chiamiamo R-VALORE il
valore della variabile contenuto nella locazione di memoria, avremo che l’ R-VALORE
puo’ cambiare nel corso dell’ esecuzione di un programma mentre L-VALORE e’
fissato e certamente non cambia durante l’esecuzione.
Cerchiamo
di chiarire meglio quanto detto.
Esempio
La
dichiarazione di variabile intera seguente:
int
somma ;
avvisa
“il sistema di programmazione” che sara’ necessario
-
Riservare una locazione
di memoria (in L-VALORE)
-
Capace di contenere un
numero intero di ( 16 , 32 ,… bit )
-
Associata al nome somma
e che nel programma sara’ possibile
-
Accedere al valore in
essa contenuto (R-VALORE), scrivendo somma
-
Memorizzare un valore
(intero) in essa usando il nome somma .
Tab. 7.4
Identificatore |
Indirizzo |
somma |
00110111 |
L’indirizzo della tabella 7.4 e’ L-VALORE che e’ invariante per il simbolo somma
Tab. 7.5 variabile somma di valore attualmente 1968 memorizzata nella cella di
memoria di L-VALORE = 00110111
Indirizzo |
R-VALORE |
…………. |
….. |
…………. |
.…. |
00110111 |
1968 |
………… |
…… |
…………. |
…… |
E’ diverso scrivere
int somma;
float somma;
Abbiamo dato la dichiarazione di due variabili aventi lo stesso nome, ma nel primo
caso la cella verra’ usata per operazioni in aritmetica intera, mentre nel secondo caso
per operazioni in aritmetica reale o meglio floating point;
in sostanza con la frase definizione di una variabile, si intende che si introduce una
variabile, identificata da un simbolo (nome) e contemporaneamente ne specifichiamo
il tipo ( intero ,reale..).
esempio
int a, b, c, somma; /* dichiarazione di 4 variabili di tipo intero */
float d, e, re; /* dichiarazione di 3 variabili di tipo float (dati reali)*/
char car; /* dichiarazione di una variabile car di tipo carattere */
Cenni
su istruzioni della parte esecutiva
Sono
di tre tipi, istruzioni di:
-
LETTURA/SCRITTURA :
scanf(“%d”,&somma); scanf(“\n%d”,&somma);
printf(“%d”, somma); printf(“\n%d”,somma);
%d
indica che somma e’ un intero; \n
indica prossima linea.
-
ASSEGNAZIONE
operatore
=
Somma
= 30; /* assegna alla variabile somma
il valore intero 30 */
-
CONTROLLO
a) istruzione composta :
gruppi
di istruzioni da eseguire in sequenza in un medesimo contesto contenute fra
le parentesi graffe { istr1; istr2;… }
b) istruzione condizionale : if-else
c) istruzione iterativa: for( ); while (); do - while()