Introduzione alla programmazione con linguaggi di alto livello -- Introduzione al C -- Vito Perrone
Indice Obiettivi della programmazione in linguaggi di alto livello La macchina astratta C Struttura di un programma C Istruzioni principali del C Primi, semplici, esempi Primi esempi … un po meno semplici
Obiettivi della programmazione in linguaggi di alto livello (1) E meglio questo: 0READ 1STORE101 2LOAD=0 3STORE102 4LOAD101 5BEQ13 6READ 7ADD102 8STORE102 9LOAD101 10SUB=1 11STORE101 12BR4 13LOAD102 14WRITE 15END
Obiettivi della programmazione in linguaggi di alto livello (2) O questo: READ STORECONTATORE LOAD=0 STORESOMMA CICLO_SOMMA: LOADCONTATORE BEQSTAMPA_FINALE READ ADDSOMMA STORESOMMA LOADCONTATORE SUB=1 STORECONTATORE BRCICLO_SOMMA STAMPA_FINALE: LOADSOMMA WRITE END
Obiettivi della programmazione in linguaggi di alto livello (3) Altri aspetti sgradevoli del linguaggio di von Neumann : Esegui (a+b)*(c+d) LOADA ADDB STORETEMP LOADC ADDD MULTTEMP RIPETI LA SEQUENZA DI OPERAZIONI FINCHE LA SEQUENZA DEI DATI NON E ESAURITA BEQ …. …..
La macchina astratta (del nucleo) del C
Elementi -e terminologia- essenziali Standard Input, Standard Output Stringa: una successione finita di caratteri (per esempio Giorgio, ieri, alfa- beta…. E immagazzinata in celle consecutive, ciascuna contenente un singolo carattere della stringa. Le celle di memoria vengono chiamate anche variabili ( /= dallomonimo concetto matematico!) Le variabili, le istruzioni e altri elementi del programma che saranno introdotti più avanti sono indicati tramite identificatori simbolici. identificatore simbolico: una successione di lettere e cifre, in cui al primo posto vi è una lettera. Il carattere speciale _ viene considerato come cifra. Esempi: a, x, alfa, pippo, a1, xy23, Giuseppe, DopoDomani …. Per il momento: numero di celle illimitato e ogni singola cella può contenere un qualsiasi valore numerico (sia intero sia reale) o un qualsiasi carattere Identificatori predefiniti e riservati (Per esempio, scanf e printf ) Parole chiave (Per comodità -di lettura umana, non del calcolatore!- le parole chiave sono in neretto)
Struttura sintattica di un programma C Un programma C è composto da: unintestazione seguita da una sequenza di istruzioni racchiusa tra i simboli { e }. Lintestazione è costituita dallidentificatore predefinito main seguito da una coppia di parentesi ( ) (per il momento vuote) Le istruzioni sono frasi del linguaggio di programmazione; ognuna di esse termina con il simbolo ;.
Le principali istruzioni del C (1) 1. Istruzione di assegnamento Assegna a una variabile il valore di unespressione Esempi : x = 23; w = 'a'; y = z; r3 = (alfa*43–xgg)*(delta–32*ijj); x = x+1; Se la cella a contiene il valore 45 e la cella z il valore 5, listruzione x = (a–z)/10 fa sì che nella cella x venga immagazzinato il valore 4. NB: per distinguere il valore carattere a dallidentificatore della variabile a, il primo viene indicato tra apici.
Le principali istruzioni del C (2) 2. Istruzioni di ingresso e uscita Consistono negli identificatori predefiniti scanf o printf seguiti da una coppia di parentesi che racchiude lidentificatore di una variabile. Determinano la lettura o scrittura del valore di una variabile dallo Standard Input o sullo Standard Output. Alcune comode abbreviazioni: printf((a–z)/10); per: temp = (a z)/10; printf(temp); dove temp denota una variabile non usata altrimenti nel programma. printf("alfa"); per: printf('a'); printf('l'); printf('f'); printf('a'); differenza tra listruzione printf(2) e listruzione printf('2') ?
Le principali istruzioni del C (3) 3. Istruzioni composte producono effetti diversi a seconda che siano verificate o meno certe condizioni sul valore delle variabili. Condizione (o espressione booleana): unespressione il cui valore può essere vero o falso. Costruita mediante – i normali operatori aritmetici, – gli operatori di relazione ( ==, !=,, = ), – gli operatori logici ( !, ||, && ), corrispondenti, nellordine, alle operazioni logiche NOT, OR, AND. Esempi di condizioni: x == 0 alfa > beta && x != 3 !((a + b)*3 > x || a < c)
Le principali istruzioni del C (4) 3.1 Istruzione condizionale costituita dalla parola chiave if, seguita da –una condizione racchiusa tra parentesi tonde, –una prima sequenza di istruzioni racchiusa tra parentesi graffe, –la parola chiave else, –una seconda sequenza di istruzioni racchiusa tra parentesi graffe. –Il ramo else dellistruzione può essere assente. –Le parentesi graffe vengono in genere omesse quando la successione di istruzioni si riduce a unistruzione singola.
Esempi di istruzioni condizionali: if(x == 0) z = 5; else y = z + w*y; if(x == 0) {z = 5;} else {y = z + w*y;} if ((x+y)*(z-2) > (23+v)) {z = x + 1; y = 13 + x;} if ((x == y && z >3) || w != y) z = 5; else {y = z + w*y; x = z;} Istruzioni scorrette: if (x == 0) else y = z; y = 34; if (x == 0) a; else b + c; Esecuzione di unistruzione condizionale: La macchina valuta la condizione, Nel caso vero esegue solamente la prima sequenza di istruzioni, Nel caso falso esegue la seconda sequenza di istruzioni. Se manca il ramo else e la condizione è falsa, la macchina prosegue con listruzionesuccessiva allistruzione condizionale. Le principali istruzioni del C (5)
3.2 Listruzione iterativa (ciclo o loop). Permette la ripetizione dellesecuzione di una sequenza di istruzioni ogni volta che una certa condizione è verificata. È costituita dalla parola chiave while, seguita dalla condizione e da una sequenza di istruzioni fra parentesi graffe (detta corpo del ciclo). Esempi: while (x >= 0) x = x – 1; while (z != y) {y = z – x; x = x*3;} Esecuzione di unistruzione iterativa: – Valutazione della condizione – Se questa è falsa non viene eseguito il corpo del ciclo e si passa direttamente allistruzione successiva. – Altrimenti si esegue una prima volta il corpo del ciclo; si valuta ancora la condizione e, nuovamente, si esegue il corpo del ciclo se essa è risultata vera. Quando la condizione risulta falsa si esce dal ciclo, ovvero si passa allistruzione successiva allistruzione iterativa. Il ciclo viene ripetuto finché la condizione rimane vera. Le principali istruzioni del C (6)
Alcune osservazioni: In unistruzione ciclica, lesecuzione potrebbe non terminare mai! – Listruzione condizionale e listruzione iterativa sono dette istruzioni composte perché esse sono costruite componendo istruzioni più semplici; – Molto utile per la costruzione di programmi complessi – Unistruzione composta può contenere al suo interno una qualsiasi altra istruzione, eventualmente essa stessa composta. Le macchine reali sono però costruite sullo schema di von Neumann: Chi si occupa di colmare il gap tra la macchina astratta C e la macchina reale -del tipo di v.N.? Il software di base. Più precisamente: –il compilatore, o, più raramente, –linterprete Le principali istruzioni del C (7)
I primi, semplici, programmi in quasi C (1) /*Programma NumeroMaggiore – prima versione */ main() { scanf(x); scanf(y); if (x > y) z = x; else z = y; printf(z); } /*Programma NumeroMaggiore – seconda versione */ main() { scanf(x); scanf(y); if (x > y) printf(x); else printf(y); }
I primi, semplici, programmi in quasi C (2) /*ProgrammaCercaIlPrimoZero */ main() { uno = 1; scanf (dato); while (dato !=0) scanf(dato); printf(uno); }
I primi, semplici, programmi in quasi C (3) /*ProgrammaSommaSequenza */ main() { somma = 0; scanf(numero); while (numero != 0) { somma = somma + numero; scanf(numero); } printf(somma); }
I primi, semplici, programmi in quasi C (4) /*Programma per la valutazione di un triangolo */ main() { /*Lettura dei dati di ingresso */ scanf(X); scanf(Y); scanf(Z); /* Verifica che i dati possano essere le lunghezze dei lati di un triangolo */ if ((X < Y + Z) && (Y < X + Z) && (Z < X + Y)) /*Distinzione tra i vari tipi di triangolo */ if (X == Y && Y == Z) printf("I dati letti corrispondono a un triangolo equilatero"); else if (X == Y || Y == Z || X == Z) printf("I dati letti corrispondono a un triangolo isoscele"); else printf("I dati letti corrispondono a un triangolo scaleno"); else printf("I dati letti non corrispondono ad alcun triangolo"); }
Come interpretare la seguente istruzione? if (C1) if (C2) S1; else S2; Prima possibilità (l else è attribuito all if più esterno): if (C1) if (C2) S1; else S2; Seconda possibilità (l else è attribuito all if più interno): if (C1) if (C2) S1; else S2; Convenzione: il primo ramo else viene attribuito allultimo if. Altrimenti, scriviamo esplicitamente: if (C1) {if (C2) S1;} else S2; Nota sullistruzione if
Le variabili strutturate Un primo arricchimento della macchina astratta C
Gli array Un array viene identificato come qualsiasi altra variabile Però anche i suoi elementi sono variabili Ad essi si accede mediante un indice: esempi: scanf(s[2]); a[3] = s[1] + x; if (a[4] > s[1] + 3) s[2] = a[2] + a[1]; x = a[i]; a[i] = a[i+1]; a[i*x] = s[a[j+1]–3]*(y – a[y]); In C il primo elemento di ogni array è sempre lo 0-esimo
/* Programma InvertiSequenza */ main() { indice = 0; scanf(x); while (x != '%') { sequenza[indice] = x; indice = indice + 1; scanf(x); } while (indice > 0) { indice = indice - 1; printf(sequenza[indice]); } Programma InvertiSequenza
Primi esempi… un po meno semplici Sviluppo e codifica incrementali di algoritmi Uso dello pseudocodice per raffinamenti successivi
Dal problema… Lo Standard Input contiene una serie di dati relativi a delle fatture: per ogni fattura una cella dello Standard Input ne contiene limporto e le tre successive la data di emissione, nella forma giorno (un numero compreso tra 1 e 31), mese, anno. Il % è posto dopo lanno dellultima fattura. Si vogliono stampare, nellordine, sullo Standard Output: –la dicitura: IMPORTI FATTURE EMESSE; –la sequenza di tutti gli importi, nello stesso ordine di ingresso, preceduti dal carattere $ ; –la dicitura: TOTALE FATTURE EMESSE; –il totale delle fatture stesse, precedute dal carattere $ ; –la dicitura: DATE DI EMISSIONE; –la sequenza delle date di emissione, nello stesso ordine di ingresso. I tre elementi di ogni data devono essere separati da una / ; alla fine di ogni data va scritto il carattere #.
…allalgoritmo… 1.Un primo ciclo esegue la lettura dei vari dati, quattro per volta (infatti ogni fattura è descritta da quattro elementi). Ognuno dei quattro dati letti viene memorizzato, rispettivamente, in una cella di quattro diversi array, denominati fatture, giorno, mese e anno. Lindice di ognuno di questi array viene incrementato di 1 a ogni iterazione del ciclo. Durante questo ciclo viene anche calcolato limporto totale delle varie fatture. 2.Viene stampata la dicitura IMPORTI FATTURE EMESSE mediante ununica istruzione printf. 3.Un primo ciclo di scrittura stampa nellordine tutti gli elementi dellarray fatture intercalandoli con il simbolo $. 4.Viene stampata la dicitura TOTALE FATTURE EMESSE seguita da $ e dal valore del totale precedentemente calcolato. 5.Viene stampata la dicitura DATE DI EMISSIONE. 6.Un secondo ciclo di scrittura stampa le varie terne di valori giorno, mese, anno, prelevandole nellordine dai corrispondenti array e introducendo il carattere / tra giorno e mese e tra mese e anno, e il carattere # tra lanno della data corrente e il giorno della terna successiva.
…al codice (1) /* Programma Fatture */ main() { contatore = 0; totale = 0; scanf(dato); while (dato != '%') { fatture[contatore] = dato; totale = totale + dato; scanf(dato); giorno[contatore] = dato; scanf(dato); mese[contatore] = dato; scanf(dato); anno[contatore] = dato; scanf(dato); contatore = contatore + 1; } printf("IMPORTI FATTURE EMESSE"); NumFatture = contatore; contatore = 0; while (contatore < NumFatture) { printf('$'); printf(fatture[contatore]); contatore = contatore + 1; } printf("TOTALE FATTURE EMESSE"); printf('$'); printf(totale);
…al codice (2) printf("DATE DI EMISSIONE"); contatore = 0; while (contatore < NumFatture) { printf(giorno[contatore]); printf('/'); printf(mese[contatore]); printf('/'); printf(anno[contatore]); printf('#'); contatore = contatore + 1; }
Dal problema … Si vuole costruire un analizzatore di testo che sostituisca sistematicamente una parola con unaltra. Lo Standard Input della macchina abbia il contenuto seguente: In primo luogo è scritta (carattere per carattere) una parola (per parola intendiamo qui una sequenza di caratteri alfabetici); segue il carattere $ ; poi unaltra parola seguita dal carattere # ; successivamente vi è una sequenza di parole separate tra di loro da uno spazio e chiusa da un % (cioè, dopo lultima parola cè uno spazio seguito dal terminatore finale % ). Il programma deve riscrivere su Standard Output il testo costituito dalla sequenza di parole dopo il #, sostituendo a ogni occorrenza della prima parola dello Standard Input la seconda. Se la prima parola manca, il programma deve stampare un messaggio che avverte lutente dellerrore e sospendere lesecuzione. Al contrario, può essere mancante la seconda parola: in tal caso si ottiene leffetto di cancellare ogni occorrenza della prima parola; è ammesso il caso particolare che lintero testo da riscrivere sia assente. NB: Possibilità di dati errati o, di condizioni eccezionali.
…allalgoritmo… 1.Verifica se prima del carattere $ esiste una parola. In caso negativo stampa il messaggio MANCA LA PAROLA DA SOSTITUIRE. In caso positivo procedi come segue. 2.Memorizza la prima parola del testo in un array di caratteri. 3.Memorizza la seconda parola in un altro array. 4.A questo punto fai la scansione dellintero testo parola per parola (fino allindividuazione del carattere % ). 4.1Memorizza ogni parola in un array. 4.2Confronta la parola letta con la prima parola. Se le due parole coincidono, scrivi nello Standard Output la seconda parola, altrimenti scrivi la parola appena letta.
…a una prima parziale codifica…(1) /* Programma SostituzioneParole */ main() { scanf(carattere); if (carattere == '$') printf("MANCA LA PAROLA DA SOSTITUIRE"); else { [memorizza nell'array PrimaParola la sequenza di caratteri fino a '$']; [memorizza nell'array SecondaParola la sequenza di caratteri seguenti '$' fino a '#']; scanf(carattere); while (carattere != '%') { [memorizza nell'array ParolaCorrente la sequenza di caratteri fino al prossimo spazio]; [confronta PrimaParola con ParolaCorrente]; if([PrimaParola == ParolaCorrente]) [printf(SecondaParola)]; else [printf(ParolaCorrente)]; printf(' '); …
…a una prima parziale codifica…(2) /* Programma SostituzioneParole (continuazione)*/ … /* Sia nel caso che si sia scritta la parola appena letta (ParolaCorrente), sia nel caso che si sia scritta la seconda parola al posto della prima, si scrive uno spazio per separarla dalla parola successiva */ scanf(carattere); /* Inizia la lettura della prossima ParolaCorrente, a meno che il carattere letto non sia % */ }
…alla codifica dei sottoproblemi (1)… Memorizzazione di una parola in un array, Scrittura di una parola Confronto tra due parole
…alla codifica dei sottoproblemi (2)… (confronto tra due parole) if (LunghPrimaPar == LunghParCorr) { contatore = 0; while ((contatore < LunghPrimaPar) && PrimaParola[contatore] == ParolaCorrente[contatore])) contatore = contatore + 1; if(contatore >= LunghPrimaPar) printf("Le due parole coincidono"); else printf("Le due parole sono diverse"); } else printf("Le due parole sono diverse");
…al codice completo (1) /* Programma SostituzioneParole */ main() { scanf(carattere); if(carattere == '$') printf ("MANCA LA PAROLA DA SOSTITUIRE"); else { contatore = 0; while (carattere != '$')
…al codice completo (2) /* Programma SostituzioneParole (continuazione)*/ … /* Memorizzazione della prima parola */ { PrimaParola[contatore] = carattere; contatore = contatore + 1; scanf(carattere); } LunghPrimaPar = contatore; scanf(carattere); contatore = 0; while (carattere != '#') /* Memorizzazione della seconda parola */ { SecondaParola[contatore] = carattere; contatore = contatore + 1; scanf(carattere); } LungSecPar = contatore;
…al codice completo (3) /* Programma SostituzioneParole (continuazione)*/ … /* Si entra ora nella fase di scansione del testo */ scanf(carattere); while (carattere != '%') { contatore = 0; while carattere != ' ' /* Memorizzazione della parola corrente */ { ParolaCorrente[contatore] = carattere; contatore = contatore + 1; scanf(carattere); } LungParCorr = contatore;
…al codice completo (4) /* Programma SostituzioneParole (continuazione)*/ … /* Si confronta la prima parola con la parola corrente */ if(LunghPrimaPar == LunghParCorr) { contatore = 0; while (contatore < LunghPrimaPar && PrimaParola[contatore] == ParolaCorrente[contatore]) contatore = contatore + 1; if(contatore >= LunghPrimaPar /* Si ricopia la seconda parola */ { contatore = 0; while (contatore < LungSecPar) { printf(SecondaParola[contatore]); contatore = contatore + 1; } else
…al codice completo (5) /* Programma SostituzioneParole (continuazione)*/ … /* Si ricopia la parola corrente */ { contatore = 0; while (contatore < LungParCorr) { printf(ParolaCorrente[contatore]); contatore = contatore + 1; } else
…al codice completo (6) /* Programma SostituzioneParole (continuazione)*/ … /* Si copia la Parola corrente: in questo caso le due parole sono differenti perché le lunghezze sono differenti */ { contatore = 0; while (contatore < LungParCorr) { printf(ParolaCorrente[contatore]); contatore = contatore + 1; } printf(' '); /*Sia nel caso che si sia scritta la parola appena letta (ParolaCorrente), sia nel caso che si sia scritta la seconda parola al posto della prima, si scrive uno spazio per separarla dalla parola successiva*/ scanf(carattere); /* Inizia la lettura della prossima ParolaCorrente, a meno che il carattere letto non sia % */ }