INFORMATICA Dati sulla linea di comando Dati sulla linea di comando • Abbiamo già avuto occasione di osservare come il main sia in realtà una funzione. • Come le altre funzioni il main è in grado di ricevere dei parametri che devono essere indicati tra le parentesi tonde che devono seguire la parola chiave main. • Questi parametri vanno scritti di seguito al nome del programma sulla linea di comando e separati tra loro dallo spazio. • Si tratta evidentemente di un’utile alternativa all’introduzione dei dati da tastiera. • Esempio: bmi 72.60 175 © Piero Demichelis 2 Dati sulla linea di comando • I parametri così indicati vengono passati dal sistema operativo al main tramite la seguente coppia di parametri: int argc char *argv[] argument counter argument vector • argc è una variabile intera che contiene il numero di parametri contenuti nel vettore di stringhe argv. • argv è un vettore di stringhe. • Queste due variabili devono essere dichiarate come parametri della funzione main: int main (int argc, char *argv[]) © Piero Demichelis 3 Dati sulla linea di comando • argc indica il numero di parametri presenti sulla riga di comando, incluso il nome del programma (pertanto vale sempre almeno 1). • il vettore argv[ ] (in realtà è una matrice di caratteri) contiene i parametri veri e propri sottoforma di stringhe di caratteri secondo il seguente formato: - argv[0] è il nome del programma; - argv[n] è la stringa che rappresenta l’ennesimo parametro dopo il nome del programma. • I parametri pertanto sono sempre delle stringhe: per trasformarli in rappresentazioni numeriche occorre usare le funzioni approppriate (atoi, atol, atof) o la sscanf. © Piero Demichelis 4 Dati sulla linea di comando • Struttura - Esempio: c:\> programma.exe 3 pippo.dat 3.2 argc = 4 argv[0] “programma.exe\0” argv[1] “3\0” argv[2] argv[3] “pippo.dat\0” “3.2\0” © Piero Demichelis 5 Dati sulla linea di comando • Scrivere un programma che legga sulla linea di comando due interi N e D, e visualizzi tutti i numeri minori di N che sono divisibili per D. • Il programma dovrà pertanto essere eseguito con il seguente comando: myprog N D dove myprog è il nome del programma, N e D sono i due valori richiesti per il funzionamento del programma. © Piero Demichelis 6 Dati sulla linea di comando #include <stdio.h> int main(int argc, char *argv[]) { int N, D, i; Test per verificare che l’utente abbia introdotto il numero esatto di dati if (argc != 3) { printf (”\nNumero argomenti errato\nSintassi: myprog N D”); return 1; } N = atoi (argv[1]); /* oppure: sscanf (argv[1], “%d”, &N); converte N */ D = atoi (argv[2]); /* oppure: sscanf (argv[2], “%d”, &D); converte D */ } for (i = 1; i < N; i++) if ((i % D) == 0) printf (”\n%d”, i); © Piero Demichelis 7 INFORMATICA typedef Ridefinizione dei tipi: typedef • E’ possibile ridefinire un tipo già esistente mediante la parola chiave typedef, cosicché si può utilizzare un nuovo identificatore (sinonimo) al posto del precedente. • Formato: typedef tipo_esistente nuovo_nome; • Esempio: typedef long int interolungo; interolungo dato_int; /* ridefinizione di tipo */ /* dato_int e' un intero lungo */ • Mediante typedef si possono definire, ad esempio, tipi astratti, integer8, integer16 e integer32, dall'ovvio significato, per poi ridefinirli in funzione del compilatore effettivamente usati. © Piero Demichelis 9 INFORMATICA Il tipo enumerazione Il tipo enumerazione: enum • Nei problemi reali può succedere di dover trattare colori, categorie, dimensioni, ecc., per i quali non esiste un tipo predefinito che si adatti opportunamente alla loro rappresentazione. • In questi casi il C permette di definire le entità su cui il programma deve operare: questa possibilità è data dalla definizione dei tipi enumerati. • La forma generale è: enum [identif_tipo] {identif. [, identif.]} [variabile]; enum identif_tipo variabile; dove le parti opzionali sono tra parentesi quadra. © Piero Demichelis 11 Il tipo enumerazione: enum Esempio: enum colore {bianco, rosso, blu, giallo, verde, nero}; enum week {lunedi, martedi, mercoledi, giovedi, venerdi, sabato, domenica}; ................................ enum colore col_parete; enum week giorno; • La variabile col_parete, di tipo colore, potrà assumere i valori bianco, rosso, blu, ecc. • La variabile giorno di tipo week i valori lunedi, martedi, ecc. © Piero Demichelis 12 Il tipo enumerazione: enum • La definizione può apparire in varie forme: enum {identif. [, identif.]} variabili ; a) • variabile può assumere uno dei valori della lista (espressi in modo simbolico); esempio: enum colore {bianco, rosso, blu, giallo, verde, nero} col_parete; b) enum nome_tipo {identif. [, identif.]}; ............................... enum nome_tipo variabili ; In questo modo si identifica con nome_tipo la lista dei valori assunti, successivamente si definisce variabile di quel tipo enumerativo che potrà assumere soltanto i valori indicati nella lista precedente. © Piero Demichelis 13 Il tipo enumerazione: enum Ad esempio: enum colore {bianco, rosso, blu, giallo, verde, nero}; ................................ enum colore col_parete; c) enum nome_tipo {ident. [, ident.]} variabile_1; ..................................... enum nome_tipo variabile_2, ......, variabile_n ; è una combinazione delle precedenti; esempio: enum colore {bianco, rosso, blu, giallo, verde, nero} parete_A; ................................ enum colore parete_B; © Piero Demichelis 14 Il tipo enumerazione: enum • In un programma si possono pertanto avere istruzioni del tipo: col_parete = verde; giorno = mercoledi; • Nella definizione di tipo si evidenziano due elementi: l’ identificatore di tipo, detto tag, e gli identificatori che verranno usati per esprimere i valori assunti da quel tipo. • I valori assunti da quel tipo corrispondono a delle costanti intere definite in modo automatico dal compilatore, che pertanto devono essere uniche nel programma. Ad esempio, enum festivo {sabato, domenica}; enum feriale {lunedi, martedi, mercoledi, giovedi, venerdi, sabato}; è errato perché sabato è presente in due tipi; analogamente è illecito usare, per esempio, l'identificatore domenica come nome di una variabile. © Piero Demichelis 15 Il tipo enumerazione: enum • La lista degli identificatori che denotano i valori assunti da un tipo enumerato forma un insieme ordinato che il compilatore codifica con valori interi crescenti a partire da 0. • Nella definizione del tipo colore, l'identificatore bianco viene codificato con 0, rosso con 1, blu con 2, ecc. Si possono modificare questi valori, assegnati automaticamente, imponendo i valori interi che si vogliono assegnare direttamente nella definizione. • Si fa seguire al nome dell'identificatore il segno uguale e il valore che si intende assegnare. Il compilatore assumerà questo numero come nuovo valore di partenza per le assegnazioni successive. © Piero Demichelis 16 Il tipo enumerazione: enum • Esempi: enum colore {bianco, rosso, giallo, verde, nero}; bianco vale 0, rosso vale 1, blu vale 2, giallo vale 3, ecc. enum colore {bianco, rosso, blu = 20, giallo, verde, nero}; In questo caso bianco vale 0, rosso vale 1, blu vale 20, giallo vale 21, ecc. • E’ possibile attribuire a più identificatori valori identici, o esplicitamente (ad esempio, blu = 20, giallo = 20), o implicitamente: se, ad esempio, si pone blu = 0 (lo stesso valore di bianco che a questo punto è già stato codificato), giallo assumerà lo stesso valore di rosso, cioé 1). © Piero Demichelis 17 Il tipo enumerazione: enum • In generale quindi questi identificatori agiscono come costanti intere e possono essere usati dovunque è appropriata una costante. • Un'applicazione interessante è la definizione del tipo logico (assente in C) ottenuta combinando la definizione di tipo enumerato con la creazione di un tipo simbolico mediante typedef: typedef enum {FALSO, VERO} boolean; /*boolean è definito come sinonimo di un tipo enumerato a due valori*/ boolean finito, trovato; /* definizione di due variabili logiche */ • Si noti che FALSO e VERO sono definite in un modo implicito e assumono rispettivamente i valori 0 e 1. © Piero Demichelis 18 Il tipo enumerazione: enum • Il linguaggio C definisce gli enumerati come una forma particolare del tipo intero: ne consegue che i valori definiti in tipi enumerati differenti sono a tutti gli effetti dei valori interi. • Sono pertanto lecite (sintatticamente, non semanticamente!) assegnazioni di valori di un tipo enumerato a una variabile di un altro tipo enumerato. • Un'altra conseguenza è che per le operazioni di I/O dei dati di tipo enumerato non sono previsti specificatori ad hoc, in quanto sono a tutti gli effetti degli interi. © Piero Demichelis 19 Il tipo enumerazione: enum • Esempio: enum enum enum enum materia {italiano, latino, storia, geografia}; giorni {lunedi, martedi}; materia corso; giorni domani; main() { corso = storia; corso = 3; domani = latino; } /* /* /* corso assume il valore 2 corso assume il valore 3 domani assume il valore 1 © Piero Demichelis */ */ */ 20 enum: esempio • Esempio: programma per introdurre da terminale i voti acquisiti in ogni materia di insegnamento e calcolo della media. #include <stdio.h> main() { /* definizioni */ enum materia {italiano, latino, storia, geografia}; double sommavoti, voto, numvoti; enum materia corso; numvoti = 0; sommavoti = 0; printf (“\nCalcola la media dei voti. Introdurre i voti.\n"); © Piero Demichelis 21 enum: esempio for (corso = italiano; corso <= geografia; corso++) { printf (“\nVoto di "); /* costruisce il messaggio di richiesta voto */ switch (corso) { case italiano: printf ("italiano: "); break; case latino: printf ("latino: "); break; case storia: printf ("storia: "); break; case geografia: printf ("geografia: "); } scanf ("%lf ", &voto); sommavoti += voto; /* accumula in sommavoti (per la media) */ numvoti++; /* incrementa numvoti (per la media) */ } printf ("Media dei voti: %5.2f\n", sommavoti/numvoti); } © Piero Demichelis 22 INFORMATICA Il costrutto struct Il costrutto struct • Supponiamo di dover realizzare una base dati contenente i nomi e l’anno di nascita di una popolazione studentesca. Non si può ricorrere a una matrice bidimensionale con una dimensione per le variabili di tipo carattere (per i nomi) e l'altra per la variabile intera (anno di nascita): le matrici possono essere di dimensioni qualsiasi ma il tipo-base deve essere unico! • Occorrerebbe definire una matrice (vettore di vettori) di caratteri per i nomi e un vettore di interi per l'anno di nascita: due strutture separate tra loro. Pertanto l'allineamento dei dati inerenti la stessa persona dovrà essere garantito dal programmatore. © Piero Demichelis 24 Il costrutto struct • La difficoltà è superata da un altro tipo di dato strutturato, molto più flessibile: il tipo struct. • Si tratta di una collezione di dati, anche di caratteristiche molto diverse tra loro, che però sono identificati dallo stesso nome. • Una struttura è formata da membri o campi. • I campi di un record possono essere di tipo diverso, scalari, vettori o, a loro volta, altre struct. © Piero Demichelis 25 Il costrutto struct • Sono numerose le informazioni che intrinsecamente possono accordarsi con questo tipo di dato, ad esempio: - una data è composta da giorno (int), mese (char) e anno (int) e quindi si può definire una struttura composta da questi tre campi; - una persona è identificata da nome, cognome, data di nascita, codice fiscale, stato civile, indirizzo, ecc., quindi si può definire una struttura composta da tutti questi campi; - la classifica di una manifestazione sportiva è composta da nome e cognome degli atleti a cui è associata la prestazione (tempo impiegato, punti, ecc.). © Piero Demichelis 26 Il costrutto struct • Una struct viene definita elencando i nomi che si intendono attribuire ai singoli campi (identificatori di campo). Per ogni identificatore di campo poi, occorre specificare il tipo. struct [nome_struct ] { tipo_1 variabile_1; /* campo 1 */ tipo_2 variabile_2; /* campo 2 */ .......... tipo_n variabile_n; /* campo n */ } [variabile] [,variabile]....; dove le parti opzionali sono tra parentesi quadra. • nome_struct costituisce il tag, cioè il nome che identifica quel particolare tipo di struttura in successive dichiarazioni di variabili. © Piero Demichelis 27 Il costrutto struct: esempi • definizione di una variabile di tipo record una_persona: struct { char nome[20]; char cognome[20]; int giorno_nascita; int mese_nascita; int anno_nascita; float peso; int altezza; } una_persona; © Piero Demichelis 28 Il costrutto struct : esempi • definizione di un record denominato data: struct data { int giorno; enum {gennaio, febbraio, marzo, aprile, maggio, giugno, luglio, agosto, settembre, ottobre, novembre, dicembre} mese; int anno; }; © Piero Demichelis 29 Il costrutto struct: esempi • definizione di numero complesso dato come x + iy, dove x e y sono numeri reali e i 1: vengono definiti la struct denominata complesso e la variabile val_complex di quel tipo record. struct complesso { double reale; double immaginario; } val_complex; © Piero Demichelis 30 Il costrutto struct • Le variabili possono essere dichiarate nell’ambito della stessa struttura, come succede per la variabile val_complex, oppure esternamente: ad esempio, dopo le dichiarazioni precedenti, sarebbero lecite le seguenti: struct complesso num_complesso; struct data giorno; • I campi di una struct sono racchiusi in un blocco e quindi il loro campo di esistenza è limitato: è per questo che è lecita la definizione della variabile giorno nell’esempio precedente, pur esistendo un campo con lo stesso nome. • Anche una funzione può essere definita di tipo struct e pertanto può restituire un valore di questo tipo. © Piero Demichelis 31 Il costrutto struct • Un campo di una struct può essere un vettore, una matrice o un’altra struct. Inoltre le struct possono costituire il tipo base per la definizione di vettori. • Nel corso del programma si può far riferimento ad uno specifico campo di una struct indicando l'identificatore della variabile seguito da un punto e dal nome del campo. • Il campo di una struct deve essere trattato come una qualsiasi altra variabile. • Sul tipo struct nel suo complesso non agisce alcun operatore, ma è lecita l'assegnazione di un’intera struct a un’altra struct dello stesso tipo. • I campi delle struct mantengono invece le proprietà, e quindi gli operatori, del tipo a cui appartengono. © Piero Demichelis 32 Il costrutto struct • Esempio: /* uso della struttura complesso struct complesso { double reale; double immaginario; }; */ struct complesso x; .............. x.reale = 12.5; x.immaginario = 0.7; © Piero Demichelis 33 struct: esempio • Esempio: programma che riceve in input da tastiera delle sequenze di caratteri (parole) e calcola il numero di volte che è presente ciascun carattere (frequenza statistica). La fine dell'introduzione delle parole sia segnalata da EOF (<CTRL>+Z). Il programma dovrà visualizzare tutti i caratteri diversi introdotti e la loro frequenza statistica. • La base dati può essere costituita da un vettore di struct avente due campi: un campo carattere, di tipo char, e un campo presenze, di tipo int. • Per ogni carattere letto da tastiera, si controlla se è già presente: se presente, si incrementa il campo presenze di quel carattere, altrimenti lo si memorizza nel campo carattere e si pone il valore 1 nel campo presenze. © Piero Demichelis 34 struct: esempio #include <stdio.h> #define NUM_MAX_CAR 100 main() { typedef enum {FALSO, VERO} boolean; struct memoria { char carattere; int presenze; }; int indice, num_car_pres; struct memoria vett_car[NUM_MAX_CAR]; int carat; char car_letto; boolean trovato; © Piero Demichelis 35 struct: esempio num_car_pres = 0; printf ("Introduci le sequenze di caratteri.\n "); printf (" Usa <CR> per andare a capo e <EOF> per finire:\n"); while ((( carat = getchar()) != EOF) && (num_car_pres < NUM_MAX_CAR)) /* finché non si arriva a EOF */ { if (carat != '\n') { /* carat è diverso da new-line */ car_letto = (char) carat; /* trasforma in char */ /* cerca nella struct memoria se il carattere è già presente */ trovato = FALSO; indice = 0; © Piero Demichelis 36 struct: esempio while ((!trovato) && (indice < num_car_pres)) { if (vett_car[indice].carattere == car_letto) { /* il carattere introdotto esiste già */ trovato = VERO; vett_car[indice].presenze++; /* incr. campo presenze */ } else indice++; /* increm. indice per scansione */ } /* while not trovato... */ if (!trovato) { /* il carattere non c’è ancora nel vettore di struct: lo inserisce */ vett_car[num_car_pres].carattere = car_letto; vett_car[num_car_pres].presenze = 1; num_car_pres++; /* incr. numero di caratteri inseriti nella struct*/ } /* if not trovato */ } /* if carat != eoln */ } /* while not eof... */ © Piero Demichelis 37 struct: esempio /* stampa i risultati */ printf ("Carattere Presenze\n"); for (indice = 0; indice < num_car_pres; indice++) { printf (" %c ", vett_car[indice].carattere); printf ("%6d\n", vett_car[indice].presenze); } } © Piero Demichelis 38 struct come parametro di funzioni • Le struct si comportano a tutti gli effetti come variabili di tipo semplice, con la particolarità che possono essere viste sia come un insieme aggregato, ad esempio nell'assegnazione di un record ad un altro, sia come singoli elementi (campi ). • In quest'ultimo caso, l'identificatore dell'elemento è costituito dal nome dell'intera struttura seguito dal punto e dal nome del campo stesso. • Anche una funzione può essere definita di tipo struct e pertanto può restituire un valore di questo tipo. © Piero Demichelis 39 struct come parametro di funzioni Esempio: somma di due numeri complessi. #include <stdio.h> #include <math.h> typedef struct { double reale; double immagin; } complesso; /* prototipo */ complesso somma_cmplx (complesso dato1, complesso dato2); © Piero Demichelis 40 struct come parametro di funzioni main() { complesso x, y, total; printf ("x.reale = "); scanf ("%lf", &x.reale); printf ("x.immaginario = "); scanf ("%lf", &x.immagin); printf ("y.reale = "); scanf ("%lf", &y.reale); printf ("y.immaginario = "); scanf ("%lf", &y.immagin); total = somma_cmplx (x, y); printf ("La somma vale: %lf", total.reale); /* stampa della parte intera */ if (total.immagin < 0.0) printf (" - i"); /* stampa l’operatore i */ else printf (" + i"); printf ("%lf\n", fabs (total.immagin)); /* stampa parte immaginaria */ } © Piero Demichelis 41 struct come parametro di funzioni complesso somma_cmplx (complesso dato1, complesso dato2) { complesso somma; somma.reale = dato1.reale + dato2.reale; somma.immagin = dato1.immagin + dato2.immagin; return (somma); } © Piero Demichelis 42 struct come parametro di funzioni • Osservazioni: - nel programma principale, i dati di tipo struct vengono usati come parametri di scanf e printf con le stesse modalità delle variabili di tipo semplice; - nella chiamata della funzione di tipo complesso somma_cmplx, i dati di tipo struct vengono passati by value : pertanto quando il controllo dell'esecuzione è passato alla funzione, verrà effettuata una copia delle variabili esterne nelle variabili locali della funzione dato1 e dato2; - la funzione è definita di tipo struct e restituisce un valore di tipo struct, il quale viene assegnato alla variabile total; - mediante typedef si è ridefinito il nome della struct, semplificando l'intestazione della funzione. © Piero Demichelis 43