Laboratorio di Programmazione 2
proff. A. Andreazza, E. Spoletini
Universita' degli Studi di Milano
Anno Accademico 2001/2002
Lezione 1
Introduzione
Scopo della prima sessione di laboratorio e' di impratichirsi con i comandi
necessari per la compilazione e lo sviluppo di programmi in C in ambiente
Linux.
Questa sessione prevede:
-
istruzioni per il collegamento al server del laboratorio di calcolo dai
computer dell'aula 311;
-
ripasso dei comandi fondamentali del sistema operativo;
-
scrivere un programma che utilizza:
-
include files,
-
input/output da terminale,
-
cicli while,
-
librerie matematiche.
-
controllo del programma su di un file di dati.
Collegamento dall'aula 311
-
Per accedere ai computer dell'aula con Windows NT, usare gli username e
password personali, fornite a lezione.
-
Una volta ottenuto l'accesso, aprire la cartella Exceed che trovate sul
desktop.
-
Fate partire con un doppio click il programma XStart.
-
Nella maschera che compare fornire i seguenti dati:
-
Start Method: telnet
-
Program Type: Terminal Emulator
-
UserID: comunicata a lezione
-
Password: comunicata a lezione
-
Host: labmaster.mi.infn.it
-
Host type: LINUX
-
Command: ls.

-
Cliccare su Run! per far partire la connessione.
-
Ora dovreste avere una finestra terminale.
Siccome state usando un account comune, per prima cosa, createvi una
directory personale e posizionatevi in questa sottodirectory (vi ricordate
come si usano mkdir e cd, vero? altrimenti
guardate l'aiuto in linea con man mkdir o man cd).
Emacs
Purtroppo per il momento non è possibile aprire finestre grafiche
dall'aula di fisica verso l'aula 311, quindi non è possibile utilizzare
nedit come l'anno scorso. Un buon editor alternativo e'
emacs, che potete far partire con il comando
emacs nomefile
Emacs usa combinazioni di tasti per le funzioni piu' importanti. Per
ora, a voi serve sapere:
-
per salvare un file premere Ctrl+x Ctrl+s
-
per uscire da emacs premere Ctrl+x Ctrl+c
-
se per errore premete Ctrl-z, dare il comando fg
per recuperare la finestra di emacs
-
se premete altri tasti sbagliati, usare Ctrl-g per annullare
il comando in corso.
Un altro problema è che sulla tastiera italiana non sono presenti
alcuni caratteri fondamentali per la programmazione in C: le parentesi
graffe! Per ottenerle, basta digitare Alt-123 o Alt-125.
Per avere la ~ invece, il carattere e' Alt-126.
L'esercizio
L'esercizio consiste nello scrivere un programma che richiede come input
i tre coefficienti a, b e c di un'equazione di secondo
grado: ax2+bx+c=0 e scrive in uscita le soluzioni dell'equazione.
Una volta che il programma è stato scritto e compilato con successo,
tutte le sue funzionalità dovranno essere controllate. Quando siete
sicuri che il programma è corretto, potete farlo operare su di un
file di dati già pronto (coefficienti.dat
nella vostra home directory: fatevene una copia personale nella subdirectory
in cui lavorate), contenente in ciascuna riga una terna di coefficienti.
Qui di seguito ci sono dei suggerimenti su come impostare la struttura
del programma e le differenze tra il FORTRAN che avete studiato l'anno
scorso ed il C.
main
In C non esistono programmi o subroutine, ma solo funzioni. Il programma
eseguibile è in effetti una funzione speciale che si chiama main
ed ha un valore di ritorni di tipo int. Quindi scrivere un programma di
fatto significa scrivere una funzione:
int main() {
/* All'interno della funzione deve essere inserito il codice appropriato */
return 0; /* Bisogna fornire un valore di ritorno */
}
La compilazione
Il compilatore C di fatto consta di tre parti fondamentali:
-
il preprocessore,
-
il compilatore vero e proprio,
-
il linker.
I tre passi possono venire effettuati con un comando solo. La maniera più
semplice di invocare il compilatore C è con:
gcc -o nomeeseguibile nomefilesorgente
Il preprocessore di fatto effettua delle sostituzioni letterali
all'interno del programma, definite da alcune direttive, riconoscibile
perché iniziano con un #.
La direttiva #include inserisce nel file in corso di
compilazione il testo di un altro file, di solito un file che contiene
le dichiarazioni delle funzioni da usare (come stdio.h
per esempio). Invece una direttiva
#define PIGRECO 3.1415196
sostituisce tutte le sequenze di caratteri P,I,G,R,E,C,O
incontrate nel testo con la sequenza di caratteri 3,.,1,4,1,5,1,9,6,
e risulta un modo pratico di inserire delle costanti.
Il compilatore trasforma il codice che abbiamo scritto in una
sequenza di istruzioni comprensibili per la macchina (file oggetto). A
volte può essere utile terminare la compilazione dopo questa fase,
utilizzando l'opzione -c del compilatore:
gcc -c -o nomefileoggetto nomefilesorgente
Se il compilatore non riesce a capire che istruzioni generare perché
il file sorgente non rispetta le regole del C, esso fornisce un errore
di sintassi e non crea il file oggetto. Il testo degli errori di sintassi
è molto esplicativo. In più il compilatore dice sempre a
quale riga dell file sorgente è presente un errore. Tranne nel caso
in cui ci si sia dimenticati un ; al termine di una riga, nel qual caso
l'informazione può anche risultare piuttosto criptica.
Il linker si preoccupa di aggiungere l'informazione di tutte
le funzioni utilizzate dal file oggetto e non implementateesplicitamente
al suo interno. Il linker viene chiamato se si invoca il gcc senza l'opzione
-c:
gcc -o nomeeseguibile [lista file sorgente]
[lista file oggetto] -lfilelibreria1 -lfilelibreria2...
Il comando gcc prevede molte opzioni. Una particolarmente consigliata
è l'opzione -Wall che scrive un sacco di messaggi
su tutte le cose che, sebbene perfettamente legali in C, suonano un po'
strane al compilatore. Questi messaggi chiamati warnings sono molto
utili per mettere in evidenza al momento della compilazione molti errori
di logica.
Funzioni di input/output
Per leggere e scrivere dati attraverso la finestra terminale, le funzioni
da usare sono scanf e printf. Il loro utilizzo
è stato introdotto a lezione. Però se avete dubbi, potete
sembre guardare la descrizione dettagliata di queste funzioni con il comando
man
scanf o man printf. Ricordatevi che
scanf
ha bisogno dell'indirizzo della variabile per potervi immagazzinare il
valore letto dal terminale, quindi attenti ad usare correttamente l'operatore
&
(=indirizzo di). Per utilizzare queste funzioni.
Librerie matematiche
Per trovare le radici dell'equazione, avrete bisogno di una funzione per
estrarre la radice quadrata. In FORTRAN molte funzioni matematiche e l'elevamento
a potenza sono funzioni intrinseche del linguaggio. In C non è
cosí semplice. Le funzioni matematiche hanno quasi tutte lo stesso
nome delle analoghe in FORTRAN (la radice quadrata sarà sqrt, per
esempio), ma sono contenute in una libreria separata. Quindi per poterle
utilizzare è necessario fare due cose:
-
a livello di scrittura del codice, ricordarsi di includere il file
header contenente la dichiarazione delle librerie matematiche, di modo
che il compilatore sappia di che cosa stiamo parlando. Questo si fa, inserendo
all'inizio del file la direttiva:
#include <math.h>
che viene interpretata dal preprocessore, che inserisce nel programma
il contenuto del file math.h, che si trova tra i file di sistema;
-
al momento della compilazione, bisogna dire al linker che le funzioni matematiche
si trovano precompilate in un opportuno file di libreria. Poiché
le librerie matematiche sono una delle componenti maggiormente usate del
C, l'opzione da fornire al compilatore è molto semplice: basta aggiungere
-lm
all'istruzione di compilazione.
Una costruzione importante
Nel programma dovrete essere in grado di leggere un numero indeterminato
di righe dallo standard input o da un file. Per fare questo si può
usare un ciclo while del tipo:
while ( scanf( /* Eh, no! il formato esatto non ve lo dico! */ )!=EOF ) {
/* codice da ripetere per ogni riga */
}
Analizziamo per un momento questa espressione. In C ogni funzione ha un
suo valore di ritorno (al limite di tipo void). Per scanf
, questo valore di ritorno è il numero di oggetti letti correttamente,
tranne nel caso in cui venga raggiunta la fine del file. In tal caso, esso
assume un valore speciale, che indica che non ci sono ulteriori dati da
leggere. Tale valore è indicato da EOF (End Of File). Cosa
vale EOF è definito nell file header stdio.h (lo
stesso che contiene le dichiarazione di scanf e printf),
che quindi deve essere inserito con una direttiva #include.
La riga di codice quindi fa due cose:
-
per prima cosa chiama scanf per leggere i dati e metterli in memoria,
-
successivamente prende il valore di ritorno di scanf e lo confronta con
EOF:
se i due valori sono uguali significa che la fine del file è
stata raggiunta ed il ciclo termina.
Per generare un carattere di EOF dal terminale, bisogna
premere la combinazione di tasti Ctrl-D.
È molto utile avere bene in mente come usare questa costruzione,
dato che risulta utilizzabile nel 90% dei temi d'esame.
Test del programma
Una volta riusciti a compilare il programma senza errori, procedete a verificarne
la funzionalità inserendo manualmente delle terne di coefficienti
di cui conoscete le radici e controllando i risultati.
Quando siete sicuri che il programma funziona potete fargli leggere
il file coefficienti.dat che trovate nella vostra home
directory. Per farlo non è necessario modificare il programma, ma
basta ricordarsi l'utilizzo degli operatori di ridirezione >
e <:
-
< nomefile, indica alla shell di usare il file
nomefile come standard input per il comando da eseguire;
-
> nomefile, indica alla shell di usare il file nomefile
come standard output per il comando da eseguire.
Quindi una riga come:
nomeprogramma < coefficienti.dat > nomeoutput
leggerà il file che vi è stato fornito e metterà
i risultati nel file nomeoutput.
Il comando grep
Il grep è un comando UNIX di grande utilità
(anche perché è lo strumento più semplice per rispondere
alle domande del formulario).
La sua sintassi è molto semplice:
grep stringa listadifile
cerca la stringa in tutti i file nella listadifile e
stampa le righe che la contengono.
Opzioni utili sono -v, che anziché selezionare
le righe che contengono stringa, seleziona quelle che non contengono
stringa,
e -c che anziché stampare le righe selezionate,
si limita a contarle.
Ad esempio:
grep " 0 " coefficienti.dat
stampa tutte le righe di coefficienti.dat che contengono uno 0
preceduto e seguito da uno spazio,
grep -c " 0 " coefficienti.dat
stampa il numero di righe di coefficienti.dat che contengono uno
0
preceduto e seguito da uno spazio,
grep -v 1 coefficienti.dat
stampa le righe che non contengono il carattere 1.
Ordinamento (facoltativo)
Per chi fosse particolarmente veloce nello scrivere il programma per le
equazioni di secondo grado, un piccolo task ulteriore è quello di
riordinare le righe del file coefficienti.dat, rispetto
al coefficiente b o al coefficiente c.
Questo si può fare scrivendo un programmino in C, oppure usando
il comando sort (man sort per vedere le
spiegazioni). Se ci riuscite, con un po' di intuizione dovreste essere
in grado di rispondere alle ultime due domande del formulario.
Relazione
Prima di lasciare l'aula, siete pregati di riempire un piccolo formulario
con domande relative allo svolgimento dell'esercizio.