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:
  1. istruzioni per il collegamento al server del laboratorio di calcolo dai computer dell'aula 311;
  2. ripasso dei comandi fondamentali del sistema operativo;
  3. scrivere un programma che utilizza:
  4. controllo del programma su di un file di dati.

Collegamento dall'aula 311

  1. Per accedere ai computer dell'aula con Windows NT, usare gli username e password personali, fornite a lezione.
  2. Una volta ottenuto l'accesso, aprire la cartella Exceed che trovate sul desktop.

  3. Exceed Icon
  4. Fate partire con un doppio click il programma XStart.

  5. Exceed folder
  6. Nella maschera che compare fornire i seguenti dati:
  7. Cliccare su Run! per far partire la connessione.
  8. Ora dovreste avere una finestra terminale.

  9. 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: 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:
  1. il preprocessore,
  2. il compilatore vero e proprio,
  3. 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:
  1. 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:

  2. #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;
  3. 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:
  1. per prima cosa chiama scanf per leggere i dati e metterli in memoria,
  2. 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 <: 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.