Sei sulla pagina 1di 279

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.

com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 1
Laboratorio del 3 Ottobre 2011

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 1

1.1 Breve Storia del Linguaggio C


Il Linguaggio di programmazione C stato creato nel 1972 da Dennis Ritchie nei laboratori della Bell Telephone, con lo scopo di realizzare il sistema operativo Unix. Il C si diffuse rapidamente anche al di fuori dei laboratori della Bell e presto diverse aziende iniziarono a utilizzare versioni proprietarie di questo linguaggio, e nacquero diverse sottili differenze di implementazione che causano ancora oggi notevoli problemi agli sviluppatori. Per ovviare a questo problema nel 1983 listituto statunitense degli standard ANSI (American National Standards Institute) ha formato un comitato con lobiettivo di produrre una definizione del C chiara e indipendente dalla macchina, capace per di preservare lo spirito originale, quindi in modo di definire uno standard per il C che da allora si chiam ANSI C. Tranne poche eccezioni, tutti i compilatori moderni si conformano allo standard. Il C considerato un linguaggio di basso livello, questa definizione non peggiorativa ma significa semplicemente che il C adopera lo stesso tipo di oggetti usati da molti elaboratori, come caratteri, numeri e indirizzi, che possono essere combinati tramite gli operatori aritmetici e logici di cui si servono le macchine reali. Il C permette solo il controllo di un singolo flusso di computazione: condizioni, cicli, raggruppamenti e sottoprogrammi, ma non multiprogrammazione parallele, sincronizzazione o co-rutine.

1.2 Prima di iniziare a programmare


Quando si cerca di risolvere un problema bene seguire una cerca metodologia: o o o o definire precisamente il problema, altrimenti non sar possibile individuare la soluzione ideare un piano risolutivo (algoritmo risolutivo) implementare lalgoritmo (metterlo in pratica) controllare i risultati in modo da verificare se il problema stato risolto o meno.

1.3 Ciclo di sviluppo del programma


Il ciclo di sviluppo di un programma si divide in diverse parti: o Creazione del Codice Sorgente: creare attraverso un editor di testo un file contenente una serie di istruzioni o comandi impiegati che combinati in una certa logica per istruire il computer in modo che esegua i compiti desiderati. Ad esempio una riga di codice sorgente C: printf(Forza Cesena!); Questa istruzione ordina al computer di stampare a video la scritta: Forza Cesena!. I file che contengono il sorgente di un programma scritto in C hanno estensione .c, ad esempio: pippo.c Creazione del Programma Sorgente: il computer non in grado di comprendere direttamente quello che lutente indica attraverso il sorgente C, ma necessita di istruzioni binarie o digitali in un particolare formato detto Linguaggio Macchina. Quindi prima che il programma possa funzionare

Pagina 2 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 1

su un computer, deve essere tradotto nel formato sorgente al linguaggio macchina. Questa traduzione effettuata da un programma chiamato compilatore che partendo dal sorgente produce un nuovo file su disco contenente le istruzioni in linguaggio macchina che corrispondono ai comandi impartiti in C. Queste istruzioni vengono chiamate codice oggetto e il file creato sul disco viene chiamato file oggetto. Il file oggetto ha estensione .obj. Creazione del File Eseguibile con il Linking: il linguaggio C costituito da un insieme di librerie che contengono il codice oggetto (ovvero pre-compilato) delle funzioni predefinite. Una funzione predefinita contiene codice C gi scritto e fornito in forma pronta per luso con il compilatore stesso. Ad esempio la funzione printf() utilizzata nellesempio precedente una funzione di libreria. Una funzione potrebbe essere vista come un robot da cucina che esegue determinate operazioni ogni volta che lo si attiva chiamandolo per nome. Queste particolari funzioni svolgono compiti frequenti come la visualizzazione di informazioni sullo schermo o la lettura di dati da tastiera. Se il proprio programma contiene alcune di queste funzioni (difficilmente si pu scrivere un programma di qualche utilit senza utilizzarne almeno una), il file oggetto prodotto a partire dal sorgente deve essere combinato con quello delle funzioni di libreria per generare un programma eseguibile. Questo processo detto Linking ed portato a termine da un programma chiamato Linker. Completamento del ciclo di sviluppo: una volta compilato e linkato il proprio programma possibile eseguirlo. Se dallesecuzione del programma non si ottengono i risultati aspettati bisogna tornare indietro al primo passo e individuare gli errori.

1.4 Primi passi


Proviamo a scrivere il primo programma che stampi a video la scritta: Forza Cesena!. Per risolvere questo problema dobbiamo essere in grado di redigere il testo del programma, compilarlo con successo. caricarlo, eseguirlo e scoprire dove finito il risultato. Questi sono meccanismi essenziali per capire non solo come programmare con il linguaggio C ma con qualsiasi linguaggio di programmazione. Il programma che risolve lesercizio il seguente: #include <stdio.h> int main() { printf(Forza Cesena!\n); return 0; } Il programma una volta compilato ed eseguito (salvo errori!!) dar come risultato: Forza Cesena! Un programma in C di qualunque lunghezza consiste di funzioni e variabili. Una funzione consiste in un insieme di istruzioni o comandi che specificano quali operazioni devono essere effettuate, mentre le variabili memorizzano i valori usati durante lesecuzione del programma. Nellesempio precedente viene definita la funzione main(), di norma si liberi di assegnare qualsiasi nome alle funzioni ma il caso di main particolare in quanto tutti i programmi scritti in C inizieranno con la chiamata alla funzione main(). Quindi ogni programma dovr avere una funzione main.

Pagina 3 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 1

Per svolgere il proprio lavoro la funzione main chiamer altre funzioni in aiuto, alcune scritte dal programmatore altre presenti nelle librerie cui si ha accesso. La prima riga del programma: #include <stdio.h> dice al compilatore di includere le informazioni sulla libreria standard per linput/output. Tutte le istruzioni di inclusione delle librerie devono comparire allinizio del programma. Tutte le funzioni hanno un inizio ed una fine, per individuare allinterno del codice dove una funzione inizia e finisce si utilizzano rispettivamente due marcatori: { (parentesi graffa aperta) e } (parentesi graffa chiusa). (premere pulsanti ALT GR + SHIFT + QUADRA APERTA per parentesi graffa aperta e premere ALT GR + SHIFT + QUADRA CHIUSA per parentesi graffa chiusa). Allinterno delle graffe presente il corpo della funzione (insieme di istruzioni eseguite alla chiamata della funzione). Nel esempio precedente allinterno del corpo della funzione main() troviamo 2 righe di codice, in paricolare la prima consiste nella chiamata della funzione printf() printf(Forza Cesena!\n); Una funzione chiamata attraverso il suo nome, seguito da un elenco degli argomenti racchiuso tra parentesi tonde. Gli argomenti sono il metodo di comunicazione tra le varie funzioni in modo che la funzione chiamante riesca a fornire alla funzione chiamata un insieme di valori detti argomenti. Nel nostro esempio la funzione si chiama printf e largomento passato Forza Cesena!. Si tratta di una funzione che produce (in gergo si dice anche stampa) a video o in uscita dei dati, in questo caso in particolare leffetto finale quello di visualizzare a schermo la scritta. Una successione di caratteri comunemente detta stringa di caratteri ed individuata da un doppio apice in apertura e in chiusura. La sequenza \n che compare nella stringa una notazione del C per accedere alla newline, che ha effetto di spostare in avanti di una riga i dati in uscita (in poche parole di andare a capo!). Senza il \n non si va a capo. La funzione printf() non fornisce in automatico landata a capo. Quindi il programma di prima poteva essere scritto anche nel seguente modo: #include <stdio.h> int main() { printf(Forza ); printf(Cesena!); printf(\n); return 0; } Listruzione return molto importante in quanto consente di restituire un parametro alla funzione chiamante. Nel nostro caso il chiamate il sistema operativo, il nostro programma terminate le proprie operazioni restituisce il valore 0 (zero) informando il sistema operativo che tutte le operazioni si cono svolte senza errori.

1.4.1 Le istruzioni
Da notare che nel programma ogni istruzione presente allinterno della funzione main() termina con un punto e virgola. Unistruzione un comando completo che indica al computer di eseguire un particolare compito. Le istruzioni in C terminano sempre con un punto e virgola (tranne il caso delle direttive del preprocessore #define o #include).

Pagina 4 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi scrivere x = 1 + 3; equivale a scrivere: x = 1 + 3; Mentre scrivere printf(ciao a tutti); non equivale a printf( Ciao a tutti); in quanto genera un errore. Mentre scrivere printf( Ciao\ a tutti);

Corso di Programmazione A.A. 2011-12

Dispensa 1

non genera errore in quanto per andare a capo con una stringa possibile utilizzare il carattere (\) di barra inversa.

1.4.2 La funzione printf()


La stringa di formato della funzione printf() specifica la modalit di formattazione delloutput e ha tre componenti possibili: o o o Il testo letterale, che viene visualizzato esattamente come viene introdotto nella stringa Un indicatore di conversione formato da un segno di percentuale(%) seguito da un carattere. Una sequenza di escape che consente di produrre comandi di formattazione particolari. Le sequenze di escare sono formate da una barra inversa (\) seguita da un carattere. Nella sequenza precedente \n una sequenza di escare chiamata carattere di nuova riga, che significa a capo. Le sequenze di escare sono utilizzate per stampare caratteri particolari.
Comando Descrizione

\t \b \ \\ \n \? \" \a

Tabulazione blank space Apice singolo (come carattere stampato) Barra inversa (come carattere stampato) Avanzamento di riga (a capo) Punto interrogativo (come carattere stampato) I doppi apici stampati (come carattere stampato) Segnale acustico

Pagina 5 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 1

1.4.3 La funzione system()


La funzione system() consente di eseguire comandi della shell del sistema operativo. Per poterla utilizzare allinterno del programma necessario includere la libreria stdlib.h. Il testo letterale che viene passato tra parentesi tonde (argomento della funzione) individua il comando che deve essere eseguito. Ad esempio per attivare il comando pause del sistema operativo DOS: system(pause);

1.5 Come scrivere un programma


La leggibilit del codice di fondamentale importanza, anche il sorgente di un programma che esegue pochi compiti potrebbe richiedere la scrittura di diverse migliaia di righe di codice quindi esistono diverse regole o consuetudini che vale la pena rispettare fin dallinizio con molta rigorosit e pignoleria.

1.5.1 Indentare il programma


Indentare un programma significa mettere in evidenza nel testo del programma mediante opportuni incolonnamenti, i gruppi di istruzioni che svolgono un problema comune, in modo da evidenziare la funzionalit calcolata da ogni gruppo di istruzioni, rispetto al resto del programma.

1.5.2 Commentare il programma


Le frasi prodotte nel corso del progetto per descrivere lalgoritmo a vari livelli di raffinamento possono comparire nella versione definitiva del sorgente sotto forma di commenti. Anche se lalgoritmo stato progettato utilizzando direttamente il linguaggio di programmazione a disposizione conveniente inserire in ogni caso dei commenti in punti particolari del programma che spieghino cosa il programma fa in quei punti, indipendentemente da come lo fa, cio dalla particolare strategia scelta. I commenti non vengono interpretati dal compilatore e quindi non vanno ad incidere sulloperativit o sulla dimensione del file eseguibile finale. Un commento pu essere scritto su una o pi righe: /* Commento su una sola riga*/ /* Commento che occupa pi di una riga*/ printf(Forza Cesena!); /*Commento che occupa parte di una riga*/

Esiste anche una sintassi diversa per commentare i propri programmi utilizzando una doppia barra: // questa riga un commento printf(Forza Cesena!); // il commento inizia con la doppia barra

La doppia barra indica che il testo della riga da considerarsi come un commento. Anche se molti compilatori C supportano questo tipo di sintassi bene evitarla quando necessario garantire aperta portabilit.

Pagina 6 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 1

1.5.3 Usare nomi di variabili autoesplicativi


Per spiegare il ruolo delle variabili, oltre che usare commenti, possiamo scegliere nomi che gi da soli ne spieghino il ruolo. Per esempio il nome della variabile stipendioMensile autoesplicativo, la stessa variabile se fosse stata chiamata xMk il programma sarebbe risultato meno comprensibile ma ugualmente funzionante.

1.5.4 Produrre la documentazione nel corso stesso del progetto


E importantissimo scrivere la documentazione durante lo sviluppo del progetto in quanto documentando le decisioni nello stesso momento in cui vengono prese, si certi della efficacia e della completezza della documentazione. E storia di ogni giorno che la documentazione venga prodotta solo al termine del progetto frettolosamente e senza rispettare alcuno standard.

Pagina 7 di 7

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 2
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 2

2.1 La direttiva #include


La direttiva #include ordina al compilatore C di inserire il contenuto di un file di inclusione nel programma in fase di compilazione. Un file di inclusione un file che contiene le informazioni richieste dal programma o dal compilatore. Sono molti i file di questo tipo (detti anche file di intestazione) supportati dal compilatore. Non mai necessario modificare le informazioni contenute in questi file, ed per questo che vengono mantenuti separati dagli altri file. I file di inclusione dovrebbero avere tutti lestensione .H (ad esempio stdio.h). La direttiva #include significa aggiungi al programma il contenuto del file .h

2.2 La funzione puts()


Per visualizzare i messaggi testuali possibile utilizzare la funzione puts(), anche se questultima non pu stampare valori numerici delle variabili. La funzione puts() richiede come argomento una sola stringa che viene visualizzata con laggiunta automatica dei un a capo. Ad esempio listruzione: puts(Ciao a tutti); svolge lo stesso compito di printf(Ciao a tutti\n); Nella stringa passata alla funzione puts() possibile specificare le sequenze di escare che avranno lo stesso effetto di quelle relative alla funzione printf(). La funzione inclusa nella libreria stdio.h.

2.3 La funzione exit()


Questa funzione termina il programma e restituisce il controllo al sistema operativo; richiede un solo argomento di tipo intero che viene passato al sistema operativo per indicare il successo o il fallimento del programma. Sintassi: exit(status); Se status vale 0, indica che il programma terminato normalmente, Il valore 1 indica che il programma terminato con un errore. Il valore restituito solitamente interrogato da sistema operativo. Per utilizzare exit() il programma deve includere il file stdlib.h che definisce anche due costanti simboliche da utilizzare come argomenti: #define EXIT_SUCCESS #define EXIT_FAILURE 0 1

Pagina 2 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 2

2.4 La memoria del Computer


I computer utilizzano la memoria ad accesso veloce (RAM) per archiviare le operazioni su cui lavorano. La RAM (Random Access Memory) composta da circuiti integrati che si trovano sulla scheda madre del computer e le informazioni in essa salvate vengono cancellate ogni volta che si spegne il computer. Da qui la definizione che la RAM una memoria volatile. E possibile riscrivere le informazioni allinterno della RAM ogni volta che so vuole. Ogni computer ha un certo quantitativo di memoria installata, la dimensione si misura in kbyte (Kb). Il byte lunit di misura fondamentale delle memorie informatiche. Per farsi un idea del quantitativo di memoria necessario allarchiviazione di alcune tipologie di dati ecco alcuni esempi:
Dato Byte Richiesti

La lettera b Il numero 450 il numero 14.3124 La frase il mondo non crea eroi Una pagina di testo

1 2 4 22 Circa 3000

La RAM di un computer organizzata in modo sequenziale: quindi ogni byte messo si seguito allaltro. Ogni byte ha un indirizzo unico attraverso cui viene identificato, che lo distingue da tutti gli altri byte della memoria. Gli indirizzi vengono assegnati dalle locazioni di memoria di ordine progressivo, cominciando dallindirizzo 0. La RAM ha diversi utilizzi in particolare per quanto ci riguarda essa permette di memorizzare i dati. I dati sono le informazioni su cui lavorano i programmi: tutte le informazioni che il programma deve eseguire vengono memorizzate allinterno della RAM durante lesecuzione.

2.5 Le variabili
Una variabile un nome simbolico che si assegna a una allocazione di memoria utilizzata per la memorizzazione dei dati. Le variabili devono essere definite prima di essere utilizzate; la definizione di una variabile indica al compilatore il nome della variabile e il tipo di dati che destinata a contenere.

2.5.1 I nomi delle variabili


I nomi delle variabili in C devono seguire le seguenti regole: o o I nomi possono contenere lettere, numeri e il trattino di sottolineatura (_) I primo carattere di un nome DEVE essere una lettera. Anche il trattino di sottolineatura pu essere utilizzato come primo carattere, ma dato che in questo caso la variabile viene trattata in modo particolare dal compilatore, per il momento il caso di non utilizzarlo come carattere iniziale. Le lettere maiuscole e minuscole sono trattate come entit differenti. Quindi i nomi pippo e Pippo si riferiscono a due variabili differenti. Le parole chiave del C on possono essere utilizzate come nomi di variabili. Le parole chiave sono istruzioni del linguaggio C.
Nome variabile

o o

Ecco degli esempi corretti e non di nomi di variabili: Numero valido

Pagina 3 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


stipendio_mensile g7t7_rfg7 _1978_compleanno testo#progetto double 9vincitori valido valido valido ma sconsigliato Sbagliato: contiene il carattere # Sbagliato: una parola chiave del C Sbagliato: il primo carattere un numero

Corso di Programmazione A.A. 2011-12

Dispensa 2

In C generalmente si utilizzano nomi di variabili scritte tutte in minuscolo (anche se non richiesto dal linguaggio); normalmente i nomi scritti in maiuscolo sono riservati alle costanti. Fate molta attenzione in quanto la stessa lettera in maiuscolo e minuscolo viene considerata diversa. Per la maggior parte dei compilatori i nomi delle variabili non possono superare i 31 caratteri; anche se possibile utilizzare nomi pi lunghi il compilatore considera solo i primi 31. Il nome della variabile aiuta a rendere pi chiaro il suo utilizzo allinterno del programma per il programmatore. Al compilatore non fa differenza avere una variabile con nome y o stipendio_mensile, nel primo caso per il significato della variabile non sarebbe stato cos chiaro per chi avesse letto il codice sorgente come invece accade nel secondo caso.

2.5.2 Tipi di variabili


I valori che un programma pu memorizzare possono essere molto diversi e quindi richiedono spazi in memoria diversi per poterli memorizzare. Il C prevede un numero ristretto di tipi di dati fondamentali:
Tipo di dato Dimensione Descrizione

char int float double

1 byte 2 byte* 4 byte 8 byte

contiene un carattere dellambiente in cui risiede il compilatore un intero, solitamente pari alla dimensione naturale degli interi sulla macchina in cui risiede il compilatore, quindi non detto che la dimensione sia pari a 2 byte numero con virgola mobile, con precisione singola numero con virgola mobile, con precisione doppia

Rappresentazione a virgola fissa: Dato che in un bit non rappresentabile la virgola il metodo pi semplice per rappresentare numeri frazionari quello di scegliere arbitrariamente la posizione della virgola (ad es. se si sceglie di usare 4 bit per la parte intera e 4 per la parte frazionaria) Rappresentazione in virgola mobile: Esistono innumerevoli modi per rappresentare numeri in virgola mobile ma il sistema pi utilizzato lo standard IEEE P754; questo metodo comporta l'utilizzo della notazione scientifica, in cui ogni numero identificato dal segno, da una mantissa e dall'esponente. In aggiunta possibile qualificare questi tipi di dati in vari modi utilizzando i prefissi: o o o short long unsigned

Gli attributi short e long si applicano solo agli interi. Lidea che short e long denotino interi di diverse lunghezze. Dove possibile int invece di solito la grandezza naturale di riferimento per una macchina.

Pagina 4 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 2

Lattributo unsigned (privo di segno) pu essere applicato a tutte le tipologie, i numeri unsigned sono sempre positivi o al pi nulli. il tipo long double denota numeri con virgola mobile a precisione multipla, come per gli interi al dimensione degli oggetti definita dallimplementazione. Tutte le variabili per default sono di tipo signed. Tipi di dati supportati:
Tipo Variabile Parola chiave Byte Intervallo

Carattere Intero* Intero corto* Intero lungo* Carattere senza segno Intero senza segno Intero lungo senza segno Variabile mobile con precisione singola Variabile mobile con precisione doppia *dipende dalla macchina

char int short int (o anche solo: short) long int (o anche solo: long) unsigned char unsigned int unsigned long int (o anche: unsigned long) float double

1 2 2 4 1 2 4 4 8

da -128 a 127 da -32768 a 32767 da -32768 a 32767 da -2147483648 a 2147483647 da 0 a 255 da 0 a 65535 da 0 a 4294967295 da 1.2E-38 a 3.4E308 da 2.2E-38 a 1.8E3082

La dimensione dei tipi di dato pu variare a seconda della piattaforma utilizzata, lo standard ANSI ha definito cinque regole che ogni compilatore tenuto a rispettare a prescindere dalla macchina su cui opera: 1. La dimensione di un char sempre un byte 2. La dimensione di un short minore o uguale a quella di un int 3. La dimensione di un int minore o uguale ad un long 4. La dimensione di un unsigned uguale a quella di un int 5. La dimensione di un float minore o uguale a quella di un double

2.5.3 Dichiarazione di una variabile


Prima di poter utilizzare una variabile in un programma C necessario dichiararla. La dichiarazione indica al compilatore il nome e il tipo di una variabile e opzionalmente il valore di inizializzazione da assegnare. Se il programma tenta di utilizzare una variabile non dichiarata il compilatore genera un errore. La sintassi per dichiarare una variabile: tipoDato nomeVariabile; Dove con tipoDato si specifica il tipo di variabile e deve essere una delle parole chiave definite nel paragrafo precedente. Il nomeVariabile indica il nome della variabile e deve rispettare le regole descritte in precedenza. Alcuni esempi di dichiarazione di variabili: int annoNascita;

Pagina 5 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


char iniziale; int numeroScarpa, anni; double percentuale; float valore; int conta, numero, inizio; Alcuni esempi di dichiarazione con gli attributi: unsigned unsigned unsigned long int unsigned int valoreNumerico; char carattere; double percentualePositiva; valore; long int valoreIntero;

Corso di Programmazione A.A. 2011-12

Dispensa 2

2.5.4 Assegnare un valore ad una variabile


Quando si dichiara una variabile si chiede al compilatore di riservare dello spazio in memoria per la stessa. Il valore presente in questo spazio non prevedibile, potrebbe essere qualsiasi cosa. Quindi bene associare alla variabile un valore certo prima di utilizzarla. Questa operazione pu essere fatta in diversi modi: dopo la dichiarazione o contemporaneamente alla dichiarazione. int annoNascita; annoNascita = 1978 Loperatore uguale (=) serve per associare un valore alla variabile. La variabile sempre posta a sinistra dalloperatore. In C il significato di annoNascita = 1978 significa: assegnare il valore 1978 alla variabile di nome annoNascita e NON annoNascita uguale a 1978. Per dichiarare una variabile durante la dichiarazione: int annoNascita = 1978; double percentuale = 0.01; double tasso = 22.4; Bisogna fare attenzione a non inizializzare una variabile con un valore al di fuori dellintervallo consentito dal tipo. Ecco per esempio due dichiarazioni non valide: int peso = 1000000000000; unsigned int valore = -2132; Attenzione perch il compilatore C non individua questo tipo di errori, il programma potrebbe essere compilato e linkato senza errori ma potrebbe dare risultati inaspettati in fase di esecuzione.

2.6 La funzione sizeof()


La funzione restituisce il numero di byte necessari per il tipo di dato passato come argomento alla funzione. La sintassi della funzione:

Pagina 6 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


sizeof(tipoDato); Un esempio di utilizzo: #include <stdio.h> int main() { printf("\n printf("\n printf("\n printf("\n printf("\n return 0; } Il programma una volta compilato ed eseguito dar come risultato: numero intero (int) byte: 4 numero intero lungo (long int) byte: 4 carattere (char) byte: 1 numero con virgola (float) byte: 4 numero con virgola (double) byte: 8

Corso di Programmazione A.A. 2011-12

Dispensa 2

numero intero (int) byte: %d", sizeof(int)); numero intero lungo (long int) byte: %d", sizeof(long int)); carattere (char) byte: %d", sizeof(char)); numero con virgola (float) byte: %d", sizeof(float)); numero con virgola (double) byte: %d", sizeof(double));

Pagina 7 di 7

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 3
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 3

3.2 Output formattato: printf()


Per visualizzare un numero, occorre utilizzare le funzioni per loutput formattato. La funzione printf() accetta un numero variabile di argomenti, come minimo uno. Il primo e unico argomento richiesto la stringa di formato, che indica come formattare loutput. Gli argomenti opzionali sono variabili ed espressioni di cui visualizzare i valori Ad esempio: Listruzione printf(Ciao a tutti!); visualizza il messaggio: Ciao a tutti sullo schermo. In questo caso utilizzato un unico argomento: la stringa di formato che contiene la stringa letterale da visualizzare sullo schermo Listruzione printf(%d,i); visualizza il valore della variabile intera i. La stringa di formato contiene soltanto lindicatore %d, che indica di mostrare un singolo intero decimale. Il secondo argomento il nome della variabile Listruzione printf(%d pi %d uguale %d,a, b, a+b); visualizza (supponendo che le variabili a e b contengano rispettivamente 2 e 3) 2 pi 3 uguale 5 sullo schermo. In questo caso vi sono quattro argomenti: una stringa di formato contenente il testo letterale e indicatori di formato, due variabili ed unespressione di cui visualizzare i valori. Zero, uno o pi comandi di conversione che indicano come visualizzare un valore dellelenco degli argomenti. Un comando di conversione costituito da % seguito da uno o pi caratteri I caratteri che non fanno parte di un comando di conversione sono visualizzati cos come sono.

La stringa di formato di printf() pu contenere quanto segue:

Di seguito sono descritti i componenti del comando di conversione. Quelli tra parentesi sono opzionali: %[modificatore][campoMinimo][precisione][modificatoreLunghezza]specificaConversione dove: modificatore pu essere una combinazione (in qualsiasi ordine) dei seguenti simboli (tra parentesi i simboli): o o (-) il risultato della conversione e' allineato a sinistra all' interno del campo definito da campoMinimo, il default e' l' allineamento a destra (+) specifica che il segno davanti al numero verr sempre stampato. Il risultato di conversioni di tipi con segno inizia con + (se positivi) o - (se negativi); il default e' che il segno (-) appare solo davanti ai negativi (spazio) inserisce uno spazio davanti al valore se il primo carattere non e' un segno (#) formato alternativo (con piccole variazioni) per la maggior parte delle specifiche di conversione Se il carattere di conversione e' o (ottale) e se il valore da convertire e' diverso da zero, il primo carattere stampato e' 0. Se il carattere di conversione e' x o X (esadecimale) e se il valore da convertire non e' nullo, i primi caratteri stampati saranno 0x o 0X a seconda che la direttiva sia x o X. 0 (zero) il campo viene riempito con zeri invece che con spazi (il default)

o o

campoMinimo: se il valore convertito ha meno caratteri del campo questo viene riempito da spazi, il valore di campoMinimo puo' essere un intero o *. In quest' ultimo caso il valore deve essere un intero inserito nella lista degli argomenti della printf, subito prima dell' espressione interessata da questo formato.

Pagina 2 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


precisione: un valore nella forma .n (con n intero oppure * se viene letto nella lista degli argomenti) dove n e' il : o o o minimo numero di cifre per le specifiche i,d,o,u,x,X minimo numero di cifre dopo il punto decimale per le specifiche a,A,e,E,f,F massimo numero di cifre significative per le specifiche g,G

Corso di Programmazione A.A. 2011-12

Dispensa 3

modificatoreLunghezza: dichiara che la successiva specifica di conversione deve essere applicata a un sottotipo intero particolare modificatore. La lettera h seguita da uno dei caratteri di conversione d, i, u, o, x o X indica che l'argomento e' uno short int o uno unsigned short int. Il modificatore l (elle) con gli stessi caratteri di conversione, indica che l'argomento e' un long int o uno unsigned long int. Il modificatore L seguito da uno dei caratteri di conversione e, E, f, g o G indica che l'argomento e' un long double il successivo specificatore (i o d) si applica ad una espressione o signed o unsigned char o signed o unsigned short int o signed o unsigned long int o signed o unsigned long long int specificaConversione: lunico obbligatorio (a parte %) Sotto sono elencati i caratteri di conversione ed il loro significato: d, i u o x, X c e, E Visualizza un intero con segno in notazione decimale Visualizza un intero senza segno di notazione decimale Visualizza un intero in notazione ottale senza segno Visualizza un intero in notazione esadecimale senza segno. Si utilizza x minuscolo per luotput minuscolo e X maiuscolo per loutput maiuscolo Visualizza un singolo carattere (indicato dal codice ASCII) Visualizza un float o un double in notazione scientifica ( ad esempio 123.45 visualizzato come 1.234500e+002) sei cifre sono visualizzate a destra del punto decimale a meno che non sia specificata unaltra precisione con lindicatore f. si utilizzano e o E per loutput minuscolo o maiuscolo Visualizza un float o un double in notazione decimale (ad esempio 123.45 visualizzato come 123.450000). Sei cifre sono visualizzate a destra. Visualizza una stringa Visualizza il carattere %

f s %

La funzione printf() restituisce un valore che individua il numero di caratteri stampati, se il valore negativo segnala un errore.

3.3 Le Istruzioni
Un istruzione un comando completo che indica al computer di eseguire un particolare compito. In generale in C le istruzioni vengono scritte una per ogni riga anche se alcune possono occupare anche pi righe. Tutte le istruzioni in C (eccetto le direttive #define, #include...) devono terminare con un punto e virgola (;).

Pagina 3 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 3

3.3.1 Spazi bianchi e istruzioni


Con spazi bianchi ci si riferisce ai veri e propri spazi, ai caratteri di tabulazione e alle righe vuote presenti nel codice sorgente. Il compilatore C non considera gli spazi bianchi e quando legge un istruzione dal codice sorgente cerca il carattere terminatore (il punto e virgola) ignorando tutti gli spazi bianchi presenti. Quindi listruzione: y=4+3; equivalente a quella seguente: y = 4 + 3;

che a sua volta equivalente a: y = 4 + 3; In questo modo viene lasciata al programmatore la scelta sulla modalit di formattazione del proprio codice. Comunque non consigliato utilizzare una formattazione come nellultimo esempio: le istruzioni dovrebbero essere introdotte una per riga seguendo uno schema standard per la spaziatura delle variabili e degli operatori. Guardare i vari esempi inseriti nelle dispense o i sorgenti in allegato per rendersi conto di come poter formattare il proprio codice. Il C ignora gli spazi bianchi eccetto quando questi si trovano allinterno di costanti stringa letterali: infatti in questo caso gli spazi vengono considerati come parti delle stringhe (quindi come caratteri della stringa). Una stringa una sequenza di caratteri e in particolare le costanti stringhe letterali sono stringhe racchiuse tra doppi apici che vengono interpretate dal compilatore in maniera assolutamente letterale, spazi compresi. Quindi per esempio listruzione (anche se solitamente non usata): printf( Forza Cesena! ); corretta, mentre quella che segue genera un errore in fase di compilazione: printf(Forza Cesena); Per andare a capo in unistruzione, quando ci troviamo in corrispondenza di una costante stringa letterale occorre utilizzare il carattere barra inversa (\) prima dellinterruzione. La forma corretta per lesempio sopra riportato quindi sar: printf(Forza\ Cesena);

3.3.2 Istruzioni nulle


Se si inserisce un punto e virgola da solo su una riga si ottiene istruzione nulla cio unistruzione che non esegue alcuna operazione.

3.3.3 Istruzioni composte


Unistruzione composta, solitamente chiamata blocco, un gruppo di due o pi istruzioni C racchiuse tra parentesi graffe. Ad esempio la porzione di codice che segue un blocco: { printf(Forza Cesena!);

Pagina 4 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


printf(Devi vincere); }

Corso di Programmazione A.A. 2011-12

Dispensa 3

In C i blocchi possono essere utilizzati in qualsiasi punto in cui sia possibile utilizzare unistruzione singola. Le parentesi graffe possono essere posizionate in qualsiasi punto ad esempio: {printf(Forza Cesena!); printf(Devi vincere);} comunque si consiglia di posizionare le graffe su righe diverse, mettendo cos in evidenza linizio e la fine del blocco. In questo modo, oltre che rendere il codice pi leggibile, possibile accorgersi se ne stata dimenticata qualcuna.

3.4 Le Espressioni
In C unespressione una qualsiasi cosa che deve essere valutata come un valore numerico. Le espressioni in C possono avere qualsiasi livello di complessit.

3.4.1 Espressioni Semplici


Le espressioni pi semplici consistono di un unico oggetto, ad esempio una variabile, una costante letterale o simbolica. Le costanti letterali vengono valutate secondo il loro valore, le costanti simboliche invece con il valore assegnato loro dalla direttiva #define, le variabili vengono valutate con il valore assegnate loro dal programma.
Espressione Descrizione

TASSO 28 stipendio 3.678

Una costante simbolica Una costante letterale Una variabile Una costante letterale

3.4.2 Espressioni Complesse


Le espressioni complesse consistono di pi espressioni semplici combinate tra di loro attraverso degli operatori. Per esempio: 3 + 9 un espressione formata dalle due costanti letterali 3 e 9 e dalloperatore somma +. Lespressione 3 + 9 viene valutata come 10. Si possono scrivere anche espressioni molto pi complesse: 3.78 + 56 stipendio * mesi / giorni Quando unespressione contiene pi operatori, come nellesempio sopra riportato, il risultato dipende dalla precedenza degli operatori. Consideriamo la seguente espressione: y = b + 17; in questo caso nellistruzione viene calcolato il valore dellespressione b + 17 e viene assegnato il risultato alla variabile y. A sua volta listruzione y = b + 17 unaltra espressione che ha il valore nella variabile a sinistra delluguale, quindi possibile anche la scrittura: k = y = b + 17;

Pagina 5 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 3

in questo caso il risultato viene assegnato sia a y che a k, in particolare prima viene valutata lespressione b + 17, il suo valore assegnato alla variabile y, ed in fine il valore di y assegnato alla variabile k. Quindi alla fine dellespressione k e y avranno lo stesso valore. In C sono possibili anche espressioni di questo tipo: k = 8 + (y = 3 + 4); in questo caso dopo lesecuzione dellistruzione la variabile y avr il valore 7, mentre la variabile x il valore 15. In questo caso per le parentesi sono essenziali per la corretta compilazione dellistruzione.

3.5 Gli Operatori


Un operatore un simbolo che indica al linguaggio di eseguire unoperazione, o unazione su uno o pi operandi. In C tutti gli operatori sono visti come delle espressioni.

3.5.1 Operatore di Assegnamento


Loperatore di assegnamento il simbolo uguale (=). Il suo significato e utilizzo allinterno di un programma diverso dal suo consueto utilizzo in matematica. Scrivendo: y = k; NON significa che y uguale a k ma invece assegna il valore di k a y. In unistruzione di assegnamento la parte a destra del segno uguale pu essere una qualsiasi espressione, mentre la parte di sinistra deve essere il nome di una variabile, quindi la sintassi risulta la seguente: variabile = espressione; Quando listruzione viene eseguita prima viene valutata lespressione poi il risultato viene assegnato alla variabile. Quindi per esempio: y = 8 + 9; prima viene calcolata la somma di 8 + 9 poi il risultato viene assegnato alla variabile y. Alla fine dellistruzione avremo che y avr il valore 17. Considerando invece: y = 11; k = y + 7; prima viene assegnato a y il valore 11 poi, nellistruzione successiva, viene calcolato y + 7 e il risultato assegnato a k. Quindi alla fine delle 2 righe di codice sopra riportate avremo che y sar uguale a 11 e k a 18.

3.5.2 Operatori matematici


Gli operatori matematici effettuano operazioni aritmetiche e si dividono in due categorie: operatori unari e operatori binari. Gli operatori matematici binari operano su due operandi. Questi operatori che comprendono anche le principali operazioni aritmetiche sono elencati nella tabella sottostante:
Operatore Simbolo Azione Esempio

Addizione Sottrazione Moltiplicazione

+ *

Somma i due operandi Sottrae il secondo operando dal primo Moltiplica i due operandi

x+y x-y x*y

Pagina 6 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Divisione Resto (modulo) / % Divide il primo operando per il secondo Fornisce il resto della divisione del primo operando per il secondo x/y

Corso di Programmazione A.A. 2011-12

Dispensa 3

x%y

Alcuni esempi di utilizzo: /* Primo esempio di utilizzo degli operatori matematici */ #include <stdio.h> int main() { int a, b, ris; a = 10; b = 5; ris = a b; printf(risultato di %d-%d=%d\n,a,b,ris); a = 7; b = 3; ris = a * b; printf(risultato di %d*%d=%d\n,a,b,ris); a = 21; b = 7; ris = a / b; printf(risultato di %d/%d=%d\n,a,b,ris); a = 21; b = 10; ris = a / b; printf(risultato di %d/%d=%d\n,a,b,ris); a = 13; b = 5; ris = a % b; printf(risultato di %d %% %d=%d\n,a,b,ris); return 0; } Gli operatori matematici unari hanno questo nome in quanto richiedono un unico operando. Il C dispone di due operatori unari: incremento e decremento e possono essere utilizzati solo con le variabili e mai con le costanti. Il loro scopo quello di aggiungere o sottrarre ununit dalloperando specificato.
Operatore Simbolo Azione Esempio

Incrementa Decrementa Ad esempio le istruzioni: ++x; --y; equivalgono a: x = x + 1;

++ --

Incrementa loperando di una unit Decrementa loperando di una unit

y++ y--

++y --y

Pagina 7 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


y = y y;

Corso di Programmazione A.A. 2011-12

Dispensa 3

Gli operatori possono essere postfissi o prefissi. Queste due sintassi non sono equivalenti, ma differiscono per quanto riguarda il momento in cui viene effettuata loperazione: Gli operatori prefissi modificano il proprio operando prima che ne venga utilizzato il valore Gli operatori postfissi modificano il proprio operando dopo avere utilizzato il valore x = 7; y = x++; Dopo lesecuzione di queste due istruzioni x vale 8 e y vale 7. Prima il valore di x stato assegnato a y e solo allora x stato incrementato. Se consideriamo invece le seguenti istruzioni: x = 7; y = ++x; Dopo lesecuzione di queste due istruzioni x vale 8 e y vale 8. Loperatore = loperatore di assegnamento e non un operatore di confronto. Eventuali successive modifiche al valore di x non hanno effetto su y. Consideriamo un esempio: #include <stdio.h> int main() { int a, b; a = 7; b = 7; printf(%d printf(%d printf(%d printf(%d printf(%d return 0; } Il risultato del programma: 7 6 5 4 3 6 5 4 3 2 %d\n,a--,--b); %d\n,a--,--b); %d\n,a--,--b); %d\n,a--,--b); %d\n,a--,--b);

Vediamo un esempio:

La precedenza degli operatori segue il seguente ordine: incrementi e decrementi unari moltiplicazioni, divisioni e resti somme e sottrazioni

Se unespressione contiene pi di un operatore con lo stesso valore di precedenza le relative operazioni vengono eseguite da sinistra a destra. E possibile modificare la precedenza attraverso lutilizzo di parentesi tonde. Ad esempio:

Pagina 8 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


y = 4 + 2 * 3;

Corso di Programmazione A.A. 2011-12

Dispensa 3

Dopo listruzione y vale 10. Prima viene eseguito 2*3 al risultato viene aggiunto 4 e il risultato assegnato a y. Consideriamo: y = 12 % 5 * 2; Loperatore % (modulo) e * (moltiplicazione) hanno la stessa precedenza quindi le istruzioni vengono eseguite da sinistra a destra: prima viene calcolato 12%5 che viene 2 che moltiplicato per 2 fa 4 quindi alla fine a y viene assegnato il valore 4. Se utilizziamo le parentesi: y = 12 % (5 * 2); Prima viene eseguita la sotto espressione individuata dalle parentesi: 5*2 poi il calcolo del modulo. Quindi alla fine dellistruzione a y viene assegnato il valore 2.

Pagina 9 di 9

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 4
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 4

4.2 Input formattato: scanf()


La maggior parte dei programmi necessita di dati forniti dallutente, in particolare da tastiera. Il modo pi flessibile per leggere dati numerici attraverso la funzione di libreria scanf(). La funzione scanf() accetta un numero variabile di argomenti, come minimo due. Il primo una stringa di formato che indica come interpretare linput mediante dei caratteri speciali. Il secondo e li altri sono gli indirizzi delle variabili a cui assegnare i dati di input. Ecco un esempio: scanf(%d,&numero); Il primo argomento %d, la stringa di formato. In questo caso %d indica a scanf() di cercare un valore intero con segno. Il secondo argomento utilizza loperatore di indirizzo (&) per indicare a scanf() di assegnare il valore di input alla variabile intera numero. La stringa di formato pu contenere quanto segue: Spazi e tabulazioni che sono ignorati (possono essere utilizzati per migliorare la lettura della stringa di formato) Caratteri (tranne %), che sono messi in corrispondenza con caratteri diversi da spazi dellinput. Una o pi specifiche di conversione, costituite dal carattere % seguito da caratteri speciali. In genere la stringa di formato contiene ununica specifica di conversione per ogni variabile.

Lunica parte obbligatoria della stringa di formato rappresentata dalle specifiche di conversione, che iniziano con % e contengono componenti opzionali e richiesti in un certo ordine. Scanf() applica specifiche in ordine ai campi di input. Un campo di input una sequenza di caratteri diversi da spazi che termina con il successivo spazio bianco o quando lampiezza del campo, se specificata, viene raggiunta. Modificatori di precisione: Tipo Argomento d i int * int *

Significato
Intero decimale Intero notazione decimale, ottale (inizia con O) o esadecimale (inizia con 0x o 0X) Intero in notazione ottale Intero decimale senza segno Uno o pi caratteri sono letti sequenzialmente nella locazione di memoria indicata dallargomento, senza aggiungere il caarattere di terminazione \0 Una stringa di caratteri diversi da spazi viene letta nella locazione di memoria specificata Un numero in virgola mobile, in notazione decimale o scientifica Una stringa, soltanto I caratteri elencati tra le parentese sono accettati. Linput termina non appena si incontra un carattere non corrispondente, oppure viene raggiunta lampiezza specificata o viene premuto il tasto invio, Per accettare il carattere ] necessario elencarlo per primo: []]. Un terminatore \0 viene aggiunto alla fine della stringa.

o u c

int* unsigned int * char *

char *

e,f,g []

float * char *

Pagina 2 di 4

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


[^] char *

Corso di Programmazione A.A. 2011-12

Dispensa 4

Equivale a [] salvo che sono accettati soltanto i caratteri non elencati tra parentesi. Letterale %: legge il carattere %. Non viene effettuato alcun assegnamento.

nessuno

Modificatori di precisione: h Quando inserito prima dellindicatore di tipo d,i,o,u,x indica che largomento un puntatore di tipo short invece che un tipo int. Sui PC i due tipi coincidono perci non serve Quando inserito prima dellindicatore di tipo d,i,o,u,x indica che largomento un puntatore di tipo long. Quando inserito prima dellindicatore e,f,g indica che largomento un puntatore di tipo double Quando inserito prima dellindicatore di tipo e, f, g, indica che largomento un puntatore al tipo long double

Linput di scanf() bufferizzato, perci non vengono ricevuti effettivamente dei caratteri finch lutente non preme INVIO. Lintera riga di caratteri arriva poi a stdin ed elaborata in ordine da scanf(). Il controllo torna da scanf() soltanto quando stato ricevuto input sufficienza per soddisfare le specifiche della stringa di formato. I caratteri in pi se esistono rimangono in stdin e possono causare problemi. Quando viene eseguita una chiamata scanf() e lutente ha inserito una singola riga, si possono avere tre situazioni: per questi esempi si assume che sia eseguita scanf(%d %d, &x, &y); per cui scanf() in attesa di 2 decimali. Ecco le possibilit: La riga inserita dallutente corrisponde alla stringa di formato. Ad esempio si supponga che lutente immetta 12 14 e prema INVIO. In questo caso non ci sono problemi, scanf() soddisfatta e non rimangono caratteri in stdin. La riga inserita dallutente ha troppo pochi elementi per corrispondere alla stringa di formato. As esempio si supponga che lutente immetta 12 e prema INVIO. In questo caso scanf() continua ad attendere linput mancante e quando lo riceve lesecuzione continua senza che rimangano dei caratteri in stdin. La riga inserita dallutente ha pi caratteri di quelli richiesti dalla stringa di formato. Ad esempio si supponga che lutente immetta 12 14 16 e prema INVIO. In questo caso scanf() legge 12 e 14 e restituisce il controllo lasciando gli altri caratteri (1 e 6) in stdin.

Nella terza situazione possono verificarsi dei problemi, poich i caratteri rimangono in attesa fino alla successiva lettura da stdin. Poi i caratteri rimasti sono i primi ad essere letti, davanti anche allinput dellutente effettuato al momento e questo provoca errori. Per evitare questo problema si utilizza la funzione gets() che legge caratteri rimanenti da stdin, fino al termine della riga incluso. Per eliminare qualsiasi carattere indesiderato possibile utilizzare la funzione fflush() passandogli come parametro stdin.

Pagina 3 di 4

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Esempio utilizzo della funzione scanf(): #include <stdio.h> int main() { int i1, i2; long l1; double d1; char buf1[80], buf2[80]; /*uso del modificatore l per interi long e double*/

Corso di Programmazione A.A. 2011-12

Dispensa 4

puts(Inserire un intero e un numero in virgola mobile.); scanf(%ld %lf,&l1, &d1); printf(Hai inserito %ld e %lf.\n, l1, d1); /* La stringa di formato di scanf() ha utilizzato il modificatore l per memorizzare linput di un tipo long e un tipo double */ fflush(stdin); /*Utilizza la dimensione del campo per suddividere linput*/ puts(inserire un numero di 5 cifre (ad esempio 54321)); scanf(%2d%3d, &i1, &i2); /* Si noti che lindicatore dellampiezza di campo nella stringa di formato di scanf() suddivide linput in due valori */ fflush(stdin); puts(inserire il nome e il cognome separati da uno spazio. ); scanf(%[^ ]%s, buf1, buf2); printf(\n il nome : %s\n,buf1); printf(\n il cognome : %s\n,buf2); /* Si noti che [^ ] nella stringa scanf() escludendo il carattere spazio ha causato la suddivisione dellinput */ return 0; } La funzione fflush() viene utilizzata per eliminare qualsiasi carattere indesiderato dal flusso di input. Poich non ci sono indicatori dampiezza lintero a cinque cifre suddiviso in due interi, uno con due caratteri ed uno con tre. Lultimo esempio utilizza il carattere di esclusione. La stringa %[^ ]%s indica a scanf() di prelevare una stringa fermandosi in corrispondenza di uno spazio, in questo modo linput viene suddiviso in maniera efficace.

Pagina 4 di 4

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 5
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 5

5.2 Operatori compatti


Gli operatori di assegnamento compatti del C forniscono un metodo veloce di combinare operazioni matematiche binarie a operazioni di assegnamento. Ad esempio, si supponga di voler incrementare di 5 il valore di x, o in altre parole voler sommare 5 a x e assegnare il risultato a x. Si pu scrivere: x = x + 5; Utilizzando un operatore di assegnamento compatto, che si pu considerare come un metodo compatto di assegnamento, si scrive: x += 5; In generale gli operatori di assegnamento compatti hanno la sintassi seguente (dove op sta per operatore binario): exp1 op= exp2; che equivale a scrivere: exp1 = exp1 op exp2; Si possono creare degli operatori di assegnamento compatti utilizzando I cinque operatori matematici binari. Alcuni esempi:
Scrittura Compatta Scrittura equivalente

X *= Y Y -= Z * 1 A /= B X += Y / 8 Y %= 3

X=X*Y Y=YZ*1 A=A/B X=X+Y/8 Y=Y%3

5.3 Operatore virgola


La virgola viene utilizzata nel C come semplice carattere di iterputazione che separa le dichiarazioni delle variabili, gli argomenti delle funzioni e cos via. In alcune situazioni per la virgola diventa un vero e proprio operatore: possibile formare unespressione separando due sottoespressioni con una virgola. Il risultato che: entrambe le espressioni vengono valutate, lespressione a sinistra viene valutata per prima lintera espressione assume il valore dellespressione di destra.

Ad esempio, listruzione seguente assegna a x il valore di b, quindi incrementa a e successivamente incrementa b: x = (a++, b++); Dato che loperatore ++ impiegato in modalit postfissa, a x viene assegnato il valore di b prima che questo venga incrementato. Luso delle parentesi necessario dato che loperatore virgola ha una precedenza molto bassa.

Pagina 2 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 5

5.4 Costanti
5.4.1 Direttiva #define
Una costante simbolica una costante rappresentata da un nome o simbolo allinterno del programma. La direttiva #define una direttiva del preprocessore e ha la seguente sintassi: #define NOMECOSTATNE letterale In questo modo si definisce una costante con il nome NOMECOSTANTE il cui valore letterale. Il funzionamento della direttiva #define quello di istruire il compilatore in modo da comportarsi nel modo seguente: nel codice sorgente, sostituisci tutte le occorrenze di NOMECOSTANTE con letterale.

5.4.2 Parola chiave const


Il secondo modo di definire costanti simboliche consiste nellutilizzo della parola chiave const. Const un modificatore che pu essere utilizzato in aggiunta a qualsiasi dichiarazione di variabile. Una variabile dichiarata const non pu venire modificata durante lesecuzione dei programmi, ma pu solo essere inizializzata durante la sua dichiarazione, ad esempio: const int conta = 100; const float pi = 3.14159; const long debito = 1200000, float tasso = 0.21; Leffetto di const si propaga su tutte le variabili della riga di dichiarazione. Nellultima riga debito e tasso sono due costanti simboliche. Se il programma cerca di modificare una variabile const, il compilatore genera un messaggio di errore, come nel caso seguente: const int count = 100; count = 200; /*Il programma non viene compilato! E impossibile rassegnare il valore di una costante!*/ Quali sono le differenze pratiche tra le costanti simboliche create con la direttiva #define e quelle create con il modificatore const? Le differenze riguardano luso dei puntatori e lambito delle variabili.

Pagina 3 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 5

5.5 Esempi utilizzo scanf(), printf()


Esempio 1 #include <stdio.h> #define SECONDI_PER_MINUTO 60 #define SECONDI_PER_ORA 3600 int secondi, ris_ore, ris_minuti, ris_secondi; main() { /*Richiedere il numero di secondi*/ printf(\n Inserire il numero di secondi); scanf(%d,secondi); ris_ore = seconds / SECONDI_PER_ORA; ris_minuti = (seconds / SECONDI_PER_MINUTO) ris_secondi = seconds % SECONDI_PER_MINUTO; % SECONDI_PER_MINUTO;

printf(%d secondi equivale a:, secondi); printf(%d h, %d m e %d s, ris_ore, ris_minuti, ris_secondi); return 0; } Esempio 2 #include <stdio.h> int main() { int numero1, numero2; printf("Inserire 2 numeri separati da uno spazio: "); scanf("%d-%d",&numero1,&numero2); printf("\n numero 1: %d", numero1); printf("\n numero 2: %d", numero2); return 0; } In questo secondo esempio i numeri per essere letti devono essere separati dal carattere -. Esempio 3 #include <stdio.h> int main()

Pagina 4 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ int stringa[80]; printf("Inserire del testo e premere invio: "); scanf("%[^abd]",stringa); printf("\n testo inserito: %s", stringa); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 5

In questo secondo esempio la funzione scanf() rimane in attesa fino a quando uno dei caratteri inseriti tra le parentesi quadre non viene digitato (seguito ovviamente dal tasto invio) Esempio 4 #include <stdio.h> int main() { int stringa[80]; printf("Inserire del testo e premere invio: "); scanf("%[^\n]",stringa); printf("\n testo inserito: %s", stringa); return 0; } In questo secondo esempio la funzione scanf() legge tutti i caratteri, incluso lo spazio, fino alla pressione del tasto invio.

Pagina 5 di 5

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 6
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 6

6.2 Operatori relazionali


Gli Gli operatori relazionali del C vengono utilizzati per confrontare espressioni e rispondere a comande come x pi grande di 100 o y pi piccola di x Unespressione contenente un operatore relazionale viene valutata secondo un valore di verit cio veto (valore 1 o false) o falso (valore 0 o false).

Operatore Uguale
Maggiore di Minore di Minore o uguale a Diverso

Simbolo Domanda sottointesa == Loperando x uguale alloperando y?


> < <= != Loperando x maggiore delloperando y? Loperando x minore delloperando y? Loperando x maggiore o uguale delloperando y? Loperando x minore o uguale delloperando y? Loperando x diverso dalloperando y?

Esempio X ==y
X>y X<y X >= y X<= y X != y

Maggiore o uguale a >=

6.3 Istruzione if
Listruzione if valuta unespressione e dirige conseguentemente il flusso di esecuzione del programma. La sintassi (ridotta) dellistruzione if la seguente: if(espressione) { istruzioni } Se il valore di verit dellespressione vero viene conseguita listruzione presente nel corpo dellistruzione (tra parentesi graffe). In caso contrario il flusso di esecuzione salta allistruzione che segue quella contenuta nellif. Si potrebbe dire che lesecuzione dellistruzione dipende dal risultato dellespressione. #include <stdio.h> int x, y; int main() { /*Input dei valori da tastiera*/ printf(\n Inserire un valore per x: ); scanf(%d,&x); printf(\n Inserire un valore per y: ); scanf(%d,&y); /*Confronta i valori e stampa i risultati*/ if (x == y) { printf(x uguale a y); } if (x > y)

Pagina 2 di 2

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ printf(x maggiore di y); } if (x < y) { printf(x minore di y); } return 0; } La clausola else Listruzione if supporta la clausola else, che ha la seguente sintassi: if (espressione) { istruzione1; } else { istruzione2; }

Corso di Programmazione A.A. 2011-12

Dispensa 6

Dove la clausole else facoltativa. Lespressione valutata; se vera (se cio espressione ha un valore diverso da zero), eseguita istruzione1. Se falsa (espressione zero) e se c una clausola else, viene invece eseguita istruzione2. Stante la natura facoltativa della clausola else in un costrutto if-else, si crea ambiguit quando un else omesso da una sequenza di if annidiata. Per convenzione, il compilatore abbina else al pi vicino if precedente che ne sia privo. Per esempio: if (n > 0) { if (a > b) { z = a; } else { z = b; } } la clausola else va con listruzione if pi interna, come segnala la rientranza. Se si vuole un risultato diverso, necessario ricorrere alle parentesi graffe per imporre il giusto annidiamento: if (n > 0) { if (a > b) { z = a; } } else { z = b; }

Pagina 3 di 3

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 6

Vediamo alcuni esempi, schematizziamo con le lettere maiuscole A, B, C, D dei blocchi di istruzioni di codice:

Esempio 1:
A if (exp) { B } C Se exp restituisce un valore vero (o un numero diverso da zero) la sequenza delle istruzioni A, B, C Se exp restituisce un valore falso (o un numero numero uguale a zero) la sequenza delle istruzioni A, C. Questo perch solo se la condizione vera si accede al corpo dellistruzione if.

Esempio 2:
A if (exp) { B } else { C } D Se exp restituisce un valore vero (o un numero diverso da zero) la sequenza delle istruzioni A, B, D. Se exp restituisce un valore falso (o un numero numero uguale a zero) la sequenza delle istruzioni A, C, D.

Esempio 3:
A if (exp1) { B if (exp2) { C } else { D } E } else { F if (exp3) { G } H } I

Pagina 4 di 4

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


EXP1 V V V F V F F F EXP2 V V F F F F V V EXP3 V F F F V V F V SEQUENZA ISTRUZIONI A B C E I A B C E I A B D E I A F H I A B D E I A F G H I A F G H I A F G H I

Corso di Programmazione A.A. 2011-12

Dispensa 6

Consideriamo il seguente costrutto: if (espresione) istruzione else if (espresione) istruzione else if (espresione) istruzione else if (espresione) istruzione else istruzione Questa sequenza di istruzioni if il modo pi generale di analizzare un ventaglio di possibilit in C. Le espressioni sono sempre valutate nellordine, se una di esse vera, listruzione con cui associata viene eseguita, e ci conclude lesecuzione dellintero costrutto. Come sempre il codice per ogni istruzione pu essere unistruzione singola o un blocco (pi istruzioni tra graffe). Lultima clausola else si occupa del caso nessuno dei precedenti in cui, non essendo soddisfatta nessuna delle condizioni, occorre procedere dufficio. Pu succedere che nessuna azione esplicita sia prevista per questo caso e allora il passo: else istruzione Pu essere tralasciato, oppure lo si pu riservare alla gestione degli errori, per rilevare una condizione impossibile. Vediamo un esempio: presi in ingresso 3 numeri stamparli in ordine crescente: #include <stdio.h> int main() { int num1, num2, num3; int a, b, b; printf(\n Inserire primo valore: ); scanf(%d,&a); printf(\n Inserire secondo valore: ); scanf(%d,&b); printf(\n Inserire terzo valore: ); scanf(%d,&c);

Pagina 5 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


if (a < b) { if (a < c) { num1 = a; if (c < b) { num2 num3 } else { num2 num3 } } else { num1 = c; num2 = a; num3 = b; } } else { if (b < c) { num1 = b; if (c < a) { num2 num3 } else { num2 num3 } } else { num1 = c; num2 = b; num3 = a; } }

Corso di Programmazione A.A. 2011-12

Dispensa 6

= c; = b;

= b; = c;

= c; = a;

= a; = c;

printf(numeri in ordine crescente: %d, %d, %d,num1,num2,num3); return 0; }

Pagina 6 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 6

6.4 Operatore condizionale


Loperatore condizionale lunico operatore ternario disponibile in C. Questo significa che lunico a lavorare su tre operandi senapati. La sua sintassi la seguente: exp1 ? exp2 : exp3; Se exp1 vera (cio non nulla), lintera espressione assume il valore di exp2. Se exp1 falsa (ovvero il suo valore zero), lintera espressione assume il valore di exp3. Ad esempio listruzione seguente assegna a x il valore 1 se y vera, mentre se y falsa assegna ad x il valore 100: x = y ? 1 : 100; Analogamente, per assegnare a x il valore pi grande tra quelli contenuti in x e in y: z = (x > y) ? x : y ; Loperatore condizionale funziona in maniera analoga a listruzione if. Listruzione precedente si sarebbe potuta scrivere anche cos: if (x > y) z = x; else z = y; Listruzione condizionale non pu essere utilizzata al posto di qualsiasi istruzione ifelse, ma quando possibile il codice risultante pi conciso. Loperatore condizionale pu essere utilizzato in alcune situazioni in cui listruzione if non utilizzabile, come allinterno di un istruzione printf(): printf(il valore superiore %d: ((x > y) ? x : y));

6.5 Istruzione switch


Listruzione switch esamina un ventaglio di possibilit. E devia di conseguenza il flusso dellesecuzione. Ogni condizione esaminata esprime la coincidenza del valore di unespressione con una costante intera. switch (espressione) { case espressione-costante: istruzioni; break; case espressione-costante: istruzioni; break; case espressione-costante: istruzioni; break; default: istruzioni } Ogni caso (clausola case) etichettato da una o pi costanti (o espressioni costanti) intere. Se uno di tali casi coincide con il valore dellespressione, lesecuzione prosegue dal caso in questione. Tutte le espressioni delle clausole case devono essere diverse. La clausola default eseguita se nessuna delle altre soddisfatta, ed facoltativa; in sua assenza, se gli altri casi non sono soddisfatti non accade nulla: listruzione switch non ha alcun effetto. I casi e la clausola default possono trovarsi in un qualunque ordine. Listruzione break provoca luscita immediata dal costrutto switch. Poich i casi fungono solo da etichette, quando lesecuzione del codice relativo a un caso finita, essa passa al successivo, a meno che non si decida esplicitamente di fare altrimenti. Questa gestione del flusso detta a cascata. Le istruzioni break e

Pagina 7 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 6

return sono le pi comuni vie duscita da switch. Un istruzione break impone anche luscita immediata dai cicli while, for e do, come si vedr nel seguito delle lezioni. Lesecuzione a cascata presenta vantaggi e svantaggi. Il lato positivo la possibilit di associare molti casi una singola azione, ma implica anche che ogni caso termini con un break per evirare il passaggio in cascata al successivo: tale meccanismo, infatti, non robusto ed espone il brano switch a una possibile disintegrazione in caso di modifica del programma. Fatto salvo luso di pi etichette in una singola computazione, lesecuzione a cascata va usata con parsimonia e corredata con appositi commenti.

Pagina 8 di 8

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 7
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 7

7.2 Istruzione for


Lordine di esecuzione delle istruzioni nei programmi in C per default dallalto verso il basso. Lesecuzione comincia con la prima istruzione di main() e va avanti istruzione per istruzione, fino a quando non viene raggiunta la fine di tale funzione. Nei programmi in C reali, raramente questordine viene rispettato. Il linguaggio prevede infatti una gamma di istruzioni di controllo che permettono di gestire il flusso di esecuzione delle istruzioni. Listruzione for un costrutto di programmazione che esegue un blocco di istruzioni per un certo numero di volte. Spesso il costrutto viene detto ciclo perch il flusso di esecuzione ripete le esecuzioni pi di una volta. Un costrutto for ha la struttura seguente: for (inizio; condizione; incremento) { istruzione; }

inizio, condizione, e istruzione sono espressioni del C, mentre istruzione un blocco di istruzioni. Quando durante lesecuzione viene incontrata listruzione for si verifica quanto segue:
1. Viene valutata lespressione inizio. Solitamente questa un istruzione di assegnamento che imposta una variabile a un particolare valore. 2. Viene valutata lespressione condizione. Questa solitamente una espressione relazionale. 3. Se la condizione falsa (cio se vale zero) listruzione for si conclude e lesecuzione passa alla prima istruzione dopo il fot 4. Se la condizione vera (cio se vale uno) vengono eseguite le istruzioni del blocco istruzione 5. Viene valutata lespressione incremento e lesecuzione torna al punto 2 Vediamo schematicamente una rappresentazione di unistruzione for: Inizio

Valuta inizio

Valuta incremento

Valuta condizione

VERA

Esegue istruzioni

FALSA

Fine

Pagina 2 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 7

Proviamo a scrivere un programma che stampi tutti i numeri che vanno da 1 a 10. Potremo scrivere un listato con 10 istruzioni printf() oppure potremo scrivere un codice pi compatto utilizzando un istruzione for: #include <stdio.h> int conta; int main() { /*Stampa i numeri da 1 a 20 */ for (conta = 1; conta <= 20; conta++) { printf(%d\n,conta); } return 0; } Loutput del programma: 1 2 3 4 5 6 7 8 9 10 La figura sottostante illustra la modalit operativa del ciclo for per il programma descritto sopra: Inizio

Assegna 1 a conta

incrementa conta di 1

conta <= di 20?

VERA

Esegue printf()

FALSA

Fine

Pagina 3 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 7

Un istruzione for pu essere eseguita tranquillamente allinterno di unaltra istruzione for, questo comportamento viene detto annidamento. Ad esempio proviamo a scrivere un programma che stampi a video un area di 5 righe e 20 colonne di caratteri x. #include <stdio.h> int righa, colonna; int main() { for (riga = 1; riga <= 5; riga++) { for (colonna = 1; colonna <= 20; colonna ++) { printf(x); } printf(\n); } return 0; } ecco loutput del programma: xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx

7.3 Break e continue


Torna a volte conveniente interrompere uniterazione in corrispondenza di punti del codice diversi dallinizio o dalla fine del corpo. Listruzione break serve allo scopo del caso di istruzioni for, while e do, in analogia con il suo uso del costrutto switch. Essa provoca limmediata terminazione del ciclo o dellistruzione switch. Listruzione continue legata a break, ma duso meno comune; causa lesecuzione delliterazione successiva del ciclo for. Nel caso for, lesecuzione prosegue con la parte relativa allincremento. Listruzione continue non si applica allistruzione switch. La presenza di un istruzione continue allinterno di un istruzione switch che sia a sua volta contenuta in un ciclo causa lesecuzione delliterazione successiva del ciclo. Per capire la sottile, quanto importante, differenza tra le due istruzioni, presentiamo un semplice codice in cui si legge un numero da tastiera che volgiamo sia compreso tra 0 e 100, se tale valore risulta essere negativo, si esce dal ciclo, mentre se maggiore di cento si richiede di inserire un valore valido; se il valore tra 1 e 100, si stampa a video il suo quadrato, se zero si esce: int valore, quadrato; for (i = 0; i < 20; i++) { scanf("%d", &valore)

Pagina 4 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


if (valore < 0) { printf("Valore non consentito\n"); break; /* esce dal ciclo */ } if (valore > 100) { printf("Valore non consentito\n"); continue; } quadrato = valore * valore; printf("%d \n", quadrato); }

Corso di Programmazione A.A. 2011-12

Dispensa 7

Pagina 5 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Esercizio 1 Scrivere un programma che calcoli la media di 20 numeri inseriti dallutente Esercizio 2 Scrivere un programma che calcoli la somma di n numeri inseriti dallutente Esercizio 3

Corso di Programmazione A.A. 2011-12

Dispensa 7

Scrivere un programma che permetta allutente di inserire 20 numeri, per ogni numero il programma deve indicare se si tratta di un numero pari o dispari. Esercizio 4 Scrivere un programma che estratto un numero a caso chieda allutente di indovinarlo concedendo 3 tentativi. Esercizio 5 Scrivere un programma che stampi tutti i numeri primi compresi nellintervallo da 1 a 100

La soluzione degli esercizi nei file allegati

Pagina 6 di 6

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 8
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 8

Istruzione while
I cicli permettono di ripetere delle operazioni fino a quando una condizione risulta vera. Consideriamo la sintassi del ciclo while: while (espressione) { Istruzione } lespressione viene valutata, qualora sia diversa da zero, istruzione eseguita ad espressione rivalutata. Questo ciclo prosegue finch espressione diventa zero, momento in cui lesecuzione riprende dal punto seguente a istruzione. Consideriamo il seguente esempio: A while (espressione) { B } C Dove A, B e C sono blocchi di codice. Viene eseguita listruzione A, valutata lespressione e se risulta vera (diversa da zero), si accede al corpo del ciclo while eseguendo listruzione B, si procede rivalutando lespressione, se risulta vera si procede nuovamente eseguento listruzione B, diversamente si esegue listruzione C. Per stampare 7 volte il nome Cesena si potrebbe eseguire la seguente porzione di codice: i = 0; while (i < 7) { printf(Cesena\n); i++; //equivale a fare i = i + 1; } Consideriamo ora la sintassi del ciclo for: for (espressione1; espressione2; espressione3) { istruzione; } equivalente a: espressione1; while (espressione2) { istruzione; espressione3; } Ad eccezione del comportamento dellistruzione continue, descritto successivamente. Sul piano grammaticale le tre componenti di un ciclo for sono espressioni. Nel caso pi comune espressione1 e espressione3 sono assegnamenti o chiamate di funzioni, mentre espressione2 un espressione relazionale. Si pu omettere una delle tre, ma i punti e virgola devono rimanere. Omettere espressione1 o espressione3 nellistruzione for corrisponde a ometterle nellequivalente ciclo while. Se per la condizione, cio espressione2, non presente, esse considerata sempre vera:

Pagina 2 di 3

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


for (;;) { istruzione; }

Corso di Programmazione A.A. 2011-12

Dispensa 8

Il ciclo infinito, presumibilmente da interrompere, prima o poi, con altri strumenti come break o return. Se sia meglio usare while o for sostanzialmente una questione di preferenza personale. Per stampare 7 volte il nome Cesena si potrebbe eseguire la seguente porzione di codice: for (i = 0; i < 7; i++) { printf(Cesena\n); }

Istruzione do-while
Come visto nelle parti precedenti, i cicli while e for valutano la condizione di terminazione fin dallinizio. Il terzo ciclo del C, listruzione do-while, esamina invece la condizione in coda, dopo ogni iterazione: il corpo quindi sempre eseguito almeno una volta. La sintassi del costrutto : do { istruzione; } while (espressione); Si esegue dapprima istruzione, e si valuta poi espressione: se vera, si torna a eseguire istruzione, e cos via. Il ciclo termina quando espressione diventa falsa.

Pagina 3 di 3

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 9
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

Funzioni per i flussi


Un flusso una sequenza di caratteri o meglio di byte di dati. Una sequenza di byte che entra in un programma un flusso di input mentre una sequenza di byte che esce dal programma un flusso di output. Concentrandosi sui flussi, si pu fare a meno di preoccuparsi dellorigine o della destinazione dei dati; il vantaggio principale che in questo modo la programmazione indipendente dai dispositivi. I flussi in C si dividono in due categorie: di testo e binari. Un flusso di testo costituito soltanto da caratteri, come i dati inviati allo schermo. I flussi di testo sono strutturati in righe che possono essere lunghe fino a 255 caratteri e sono concluse da un carattere di nuova riga. I flussi binari possono gestire dati di qualsiasi tipo, tra cui anche quelli di testo. I byte in un flusso binario non sono tradotti o interpretati in modo particolare ma letti semplicemente cos come sono. I flussi binari vengono utilizzati principalmente per i file su disco. LANSI C ha tre flussi predefiniti chiamate anche file di input/output standard. Se si programma per un computer PC compatibile con DOS, sono disponibili due flussi in pi, aperti automaticamente quando il programma viene avviato e chiusi autonomamente al termine. Nome stdin stdout stderr stdprn * stdaux * Flusso Input standard Output standard Errore standard Stampante standard Ausiliario standard Dispositivo Tastiera Schermo Schermo Stampante (LPT1:) Porta seriale (COM1:)

*supportati soltanto in DOS Ogni volta che si utilizzano le funzioni printf() o puts() per visualizzare del testo sullo schermo si utilizza stdout. Analogamente con la funzione scanf() si utilizza il flusso stdin. I flussi standard sono aperti automaticamente, ma altri, come quelli utilizzati per manipolare i dati memorizzati su disco devono essere aperti esplicitamente. La libreria standard del C ha molte funzioni per la gestione dei flussi di input e di output. La maggior parte di tali funzioni disponibile in due variet: una che utilizza sempre uno dei flussi standard e laltra che richiede al programmatore di specificare il flusso. Utilizza uno dei flussi standard printf() vprintf() puts() putchar() scanf() gets() getchar() perror() Richiede un nome di flusso fprintf() vfprintf() fputs() putc(), fputc() fscanf() fgets() getc(), fgetc() Descrizione Output formattato Output formattato con un elenco di argomenti variabile Output stringa Output carattere Input formattato Input stringa Input carattere Output stringa solo su stderr

Pagina 2 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

Tutte queste funzioni richiedono linclusione del file stdlib.h. Le funzioni vprintf() e vfprintf() richiedono anche stdargs.h.

Input da tastiera
La maggior parte dei programmi in C richiede qualche forma di input dalla tastiera (ovvero da stdin). Le funzioni di input si dividono in tre livelli gerarchici: input di carattere, di riga e formattato.

Funzioni per linput di carattere


Le funzioni per linput di carattere leggono linput da un flusso un carattere per volta e restituiscono il carattere nel flusso o EOF se stata raggiunta la fine del file p si verificato un errore. EOF una costante simbolica definita in stdio.h come 1- Questi funzioni differiscono per buffer e visualizzazione dei caratteri. Alcune funzioni dispongono di un buffer dove il sistema operativo memorizza tutti i caratteri finche lutente non preme INVIO per inviarli al flusso stdin. Altre sono prive di buffer, quindi ogni carattere inviato a stdin appena viene premuto il tasto corrispondente. Alcune funzioni di input eseguono leco di ogni carattere su stdout non appena viene ricevuto, altri no, perci il carattere viene inviato a stdin e non a stout.

La funzione getchar() Questa funzione ottiene il carattere successivo dal flusso stdin. Fornisce un input di carattere bufferizzato con eco e il suo prototipo : int getchar(void) Luso di getchar() mostrato nel listato sotto: #include <stdio.h> main() { char ch; while ((ch = getchar()) != \n) putchar(ch); return 0; }

Loutput del programma: Questo ci che stato digitato. Questo ci che stato digitato.

La funzione getchar() attende la ricezione di un carattere da stdin. Poich getchar() una funzione di input con buffer, non viene ricevuto alcun carattere finch lutente non preme INVIO. Tuttavia ogni tasto premuto viene immediatamente mostrato sullo schermo. Quando si preme INVIO, tutti i caratteri immessi, incluso quello di nuova riga vengono inviati a stdin dal sistema operativo. La funzione getchar() restituisce i caratteri uno alla volta, assegnando ognuno a ch. Ogni carattere confrontato con il carattere di nuova riga \n e se diverso, visualizzato su schermo con putchar(). Altro esempio dellutilizzo della funzione getchar() per linput di unintera stringa:

Pagina 3 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

#include <stdio.h> #define MAX 80 main() { char ch, buffer[MAX+1]; int x = 0; while (((ch = getchar()) != \n) && x < MAX) buffer[x++] = ch; buffer[x] = \0; printf(%s\n,buffer); return 0; } //carattere di fine stringa

Ecco input e output del programma: Questo ci che stato digitato. Questo ci che stato digitato. Il programma simile a quello visto in precedenza, per quanto riguarda lutilizzo di getchar(). Il ciclo presenta una condizione in pi: questa volta accetta i caratteri da getchar() finch viene riconosciuta una nuova riga o dopo 80 caratteri. Larray buffer ha dimensione MAX+1 e non MAX perch con la dimensione MAX+1 la stringa pu contenere 80 caratteri pi il terminatore \0. La funzione getch() Questa funzione ottiene il carattere successivo dal flusso stdin. Fornisce un input di carattere bufferizzato senza eco e il suo prototipo : int getch(void) Questa funzione non fa parte dello standard ANSI quindi potrebbe non essere disponibile su tutti i sistemi; in pi potrebbe richiedere file dinclusione diversi. In generale il prototipo di getch() definito in conio.h. Luso di getch() mostrato nel listato sotto: #include <stdio.h> main() { char ch; while ((ch = getch()) != \n) putchar(ch); return 0; }

Input e output del programma:

Pagina 4 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

Questo ci che stato digitato.

Questo quello che si visualizza in quanto la funzione non ha buffer e getch() restituisce ogni carattere senza attendere la pressione di INVIO. Poich non viene eseguito leco dellinput i caratteri non sono visualizzati sullo schermo. La funzione getche() Questa funzione del tutto simile a getch(), a parte il fatto che esegue leco dei caratteri su stdout. Anche questa non una funzione ANSI, ma molti compilatori la supportano.

Funzioni per linput di riga


La funzione gets() Questa funzione si limita a leggere una riga da stdin e la memorizza in una stringa (array di caratteri). LA funzione legge caratteri finch non incontra un carattere di nuova riga (\n) o la fine della riga. Prima di utilizzare gets() necessario allocare uno spazio in memoria sufficiente per la stringa. #include <stdio.h> main() { char input[81]; puts(scrivere il testo e premere INVIO:); gets(input); printf(E\ stato inserito: %s\n,input); return 0; }

Ecco input e output del programma: Scrivere il testo e premere INVIO: Questo il testo E stato inserito: Questo il testo

In questo esempio largomento di gets() lespressione input, che rappresenta il nome di un array di tipo char.

Funzioni per linput formattato


La funzione scanf() Funzione vista nelle lezioni precedenti, utilizzata soprattutto per manipolare i numeri.

Pagina 5 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

Output su schermo
Le funzioni di output sullo schermo si dividono in tre categorie principali come quelle di input: output di caratteri, di riga e di formato.

Output di caratteri
Le funzioni di outpur di caratteri del C inviamo un singolo carattere a un flusso. La funzione putchar() Il prototipo della funzione si trova su stdio.h ed il seguente: int putchar(int c); questa funzione scrive il carattere memorizzato nel parametro su stdout. Anche se nel prototipo specificato un argomento int, in realt si passa un char. Si pu passare un int, purch abbia valore appropriato per un carattere (cio sia compreso tra 0 e 255). La funzione restituisce il carattere scritto, o EOF in caso di errore.

Output di riga
La funzione puts() Funzione vista nelle lezioni precedenti.

Output formattato
La funzione printf() Funzione vista nelle lezioni precedenti.

Pagina 6 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 9

Funzioni matematiche
La libreria standard del C contiene numerose funzioni che svolgono calcoli matematici. I loro prototipi di trovano nel file di intestazione math.h. Tutte le funzioni matematiche restituiscono un valore double. Nelle funzioni trigonometriche gli angoli sono espressi in radianti (un radiante equivale a 57,96 gradi; langono giro di 360 gradi misura 2 radianti) Funzioni trigonometriche Funzione Prototipo acos() double acos(double x) Descrizione Restituisce larcocoseno dellargomento. Largomento deve essere compreso tra 1 e 1. Il valore restituito compreso tra 0 e . Restituisce larcoseno dellargomento. Largomento deve essere compreso tra 1 e 1. Il valore restituito compreso tra /2 e /2. Restituisce larcotangente dellargomento. Il valore restituito compreso tra -/2 e /2. Restituisce larcotangente di x/y. Il valore restituito compreso tra - e . Restituisce il coseno dellargomento. Restituisce il seno dellargomento. Restituisce la tangente dellargomento.

asin()

double asin(double x)

atan()

double atan(double x)

atan2()

double atan2(double x, double y)

cos()

double cos(double x)

sin() tan()

double sin(double x) double tan(double x)

Funzioni esponenziali e logaritmiche Funzione Prototipo exp() double exp(double x)

Descrizione Restituisce lesponente naturale dellargomento cio e^x (e elevato alla x) dove e uguale a: 2,7182818284590452354 Restituisce il logaritmo naturale dellargomento. Largomento deve essere maggiore di zero. Restituisce il logaritmo in base 10 dellargomento. Largomento deve essere maggiore di zero

log()

double log(double x)

log10()

double log10(double x)

Pagina 7 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


frexp() double frexp(double x, int* y)

Corso di Programmazione A.A. 2011-12

Dispensa 9

Calcola un numero decimale normalizzato che rappresenta il valore di x. Il valore restituito r compreso tra 0,5 e 1. La funzione assegna ad y un esponente intero tale che c = r * 2 ^ y. Se largomento x vale 0 anche r ed y verranno posti a 0. Restituisce x * 2 ^ y

ldexp()

double ldexp(double x, int y)

Funzioni ipergoliche Funzione cosh()

Prototipo double cosh(double x)

Descrizione Restituisce il coseno iperbolico dellargomento Restituisce il seno iperbolico dellargomento Restituisce la tangente iperbolica dellargomento

cosh ()

double cosh(double x)

tanh()

double tanh(double x)

Altre funzioni matematiche Funzione Prototipo sqrt() double sqrt(double x)

Descrizione Restituisce la radice quadrata dellargomento che deve essere positivo e non nullo. Restituisce il pi piccolo intero non inferiore allargomento. Ad esempio ceil(4.5) restituisce 5.0; ceil(-4.5) restituisce -4.0 . Restituisce il pi grande numero interno non superiore allargomento. Ad esempio floor(4.5) restituisce 4.0 mentre floor(-4,5) restituisce 5.0. Restituisce il valore assoluto dellargomento. Restituisce il valore assoluto dellargomento. Separa x in una parte intera e una decimale ciascuna con lo stesso segno di x. La parte decimale il valore restituito; la parte intera assegnata a *y.

ceil()

double ceil(double x)

floor()

double floor(double x)

abs()

int abs(int x)

labs()

long labs(long x)

modf()

double modf(double x, double *y)

Pagina 8 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


pow() double pow(double x, double y)

Corso di Programmazione A.A. 2011-12

Dispensa 9

Restituisce x ^ y (x elevato alla y); si genera errore se x = 0 e y <= 0 o se x < 0 e y non intero. Restituisce il resto in formato decimale di x/y. Con lo stesso segno di x. Restituisce 0 se x uguale a 0.

fmod()

double fmod(double x, double y)

Pagina 9 di 9

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 10
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 10

Array
Un array un insieme di celle di archiviazione, ciascuna dello stesso tipo di dati e con lo stesso nome. Le celle di un array prendono il nome di elementi. Un array si dice monodimensionale quando ha in solo indice di riferimento. In indice un numero tra parentesi quadre che segue il nome di un array e identifica ciascun elemento. Un esempio: nel programma di gestione delle spese mensili si utilizzerebbe la riga che segue per dichiarare un array di tipo float: float spese[12]; Tale array di nome spese, pu contenere 12 elementi, ciascuno dei quali assolutamente equivalente ad una variabile di tipo float. E possibile creare array di qualsiasi tipo riconosciuto dal C. In C gli indici vengono numerato a partire da 0, per cui i 12 elementi di spese sono indicizzati da 0 a 11. Nellesempio precedente le spese relative al mese di gennaio potrebbero essere memorizzate nellelemento spese[0], quello di febbraio in spese[1] e cos via. Quando viene dichiarato un array, il compilatore riserva un blocco di memoria abbastanza grande da contenerlo interamente. Gli elementi dellarray vengono memorizzati allinterno di locazioni di memoria contigue. Il punto in cui vengono dichiarati gli array allinterno del codice sorgente molto importante. Cos come accade per le variabili semplici, il punto dove avviene la dichiarazione influisce sullambito dellarray. Gli elementi dellarray possono essere utilizzati ovunque possa esserlo una variabile dello stesso tipo. Si accede ai singoli elementi riportando il nome dellarray seguito tra parentesi quadre. Ad esempio listruzione seguente memorizza il valore 78.16 allinterno del secondo elemento dellarray (bisogna ricordarsi che il promo elemento spese[0] e non spese[1]): spese[1] = 78.16; Allo stesso modo, listruzione: spese[10] = spese[7]; assegna allelemento dellarray spese[10] una copia del valore contenuto nellelemento spese[7]. Quando ci si riferisce ad un elemento di un array, lindice pu essere una costante letterale, come in questo esempio oppure unespressione (compreso il valore di un altro elemento dellarray). Ecco alcuni esempi: float spese[12]; int a[10]; ... spese[i] = 100; /* i una variabile intera */ spese[2 + 3] = 100; /* equivale a spese[5] */ spese[a[2]] = 100; /* a[] un array intero */ Considerando lultimo esempio, prendendo un array di interi denominato a[], se allinterno dellelemento a[2] contenuto il valore 8 listruzione: spese[a[2]] = 100; ha lo stesso significato di quella seguente: spese[8] = 100; Quando si utilizzano gli array si tenga ben presente lo standard di numerazione: in un array di n elementi gli indici possibili vanno da 0 a n 1. Se si cerca di utilizzare lindice n, il programma da errore. Vediamo un esempio: programma che legga 5 valori interi inseriti da un utenti e successivamente li stampi nello stesso ordine. #include <stdio.h> int main()

Pagina 2 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ int elenco[5]; int i,; /*lettura dei valori*/ for (i = 0; i < 5; i++) { printf(\n Inserire numero %d: ,i); scanf(%d,&elenco[i]); } /*stampa dei valori inseriti*/ for (i = 0; i < 5; i++) printf(\n %d,elenco[i]); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 10

Vediamo una variazione del programma precedente: la stampa dei valori inseriti deve essere in senso inverso. #include <stdio.h> int main() { int elenco[5]; int i,; /*lettura dei valori*/ for (i = 0; i < 5; i++) { printf(\n Inserire numero %d: ,i); scanf(%d,&elenco[i]); } /*stampa dei valori in senso inverso*/ for (i = 4; i >= 0; i--) printf(\n %d,elenco[i]); return 0; } Vediamo un altro un esempio: programma che permetta ad un utente di inserire 10 numeri e successivamente ne calcoli la somma. #include <stdio.h> int main() { int elenco[10]; int i, somma; /*lettura dei valori*/ for (i = 0; i < 10; i++) { printf(\n Inserire numero %d: ,i);

Pagina 3 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


scanf(%d,&elenco[i]); } /*calcolo della somma*/ somma = 0; for (i = 0; i < 10; i++) somma += elenco[i]; printf(\n la somma e\ %d,somma); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 10

Quando viene dichiarato un array, possibile inizializzarlo in totalmente o in parte facendo seguire la dichiarazione da un segno di uguale e da un elenco di valori racchiusi tra parentesi graffe e separati da virgole. I valori in elenco vengono assegnati in ordine agli elementi dellarray a cominciare da quello con indice 0. Ad esempio listruzione seguente dichiara un array di dimensione 4 e associa allelemento di indice 0 il valore 100, allelemento di indice 1 il valore 200, allelemento di indice 2 il valore 300 e allelemento di indice 3 il valore 400: int array[4] = {100, 200, 300, 400}; oppure int array[] = {100, 200, 300, 400}; Nel secondo caso la dimensione viene omessa e il compilatore crea un array della dimensione sufficiente per contenere i valori con cui lo si inizializza. E comunque possibile specificare un numero di valori di inizializzazione interiore alla capacit: int array[10] = {1, 2, 3}; Scrivere un programma che permetta di cercare un numero allinterno del vettore. In particolar il programma deve restituire lindice della cella in cui il valore cercato memorizzato. #include <stdio.h> int main() { int elenco[10] = {13,6,98,2,31,5,88,64,26,91}; int i, cerca, trovato; printf(inserire il numero da cercare: ); scanf(%d,&cerca); trovato = 0; for (i = 0; i < 10; i++) { if (elenco[i] == cerca) { trovato = 1; break; } } if (trovato) printf(\n Il numero e\ nella cella %d,i); else printf(\n Il numero cercato non e\ stato trovato);

Pagina 4 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 10

Scrivere un programma che conti tutte le occorrenze di un numero allinterno di un vettore di interi.

#include <stdio.h> int main() { int elenco[10] = {13,6,98,2,31,5,88,64,26,91}; int i, cerca, conta; printf(inserire il numero da cercare: ); scanf(%d,&cerca); conta = 0; for (i = 0; i < 10; i++) { if (elenco[i] == cerca) conta++; } printf(\n Il numero compare %d volte,conta); return 0; }

Esercizio 1 Scrivere un programma che inverta lordine degli elementi di un array Esercizio 2 Scrivere un programma che inverta di posto due elementi di un array Esercizio 3 Scrivere un programma che confronti 2 array di interi.

Pagina 5 di 5

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 11
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Array multidimensionali
Un array si dice multidimensionale sono quelli i cui elementi sono distinti da pi di un indice. Un array bidimensionale avr 2 indici, uno tridimensionale 3 e cos via. Non esiste alcun limite al numero di dimensioni di un array in C, tuttavia esiste un limite per la grandezza totale dellarray. Si supponga di voler scrivere un programma che giochi a scacchi. La scacchiera contiene 64 caselle disposte su otto righe e otto colonne. Il programma pu rappresentare la scacchiera sotto forma di un array bidimensionale nel modo seguente: int scacchi[8][8]; Larray risultante contiene 64 elementi: scacchi[0][0], scacchi[0][1], scacchi[0][2], ... scacchi[7][6] e scacchi[7][7]. Analogamente un array tridimensionale pu essere considerato come un cubo. A prescindere dal numero di dimensioni, gli array vengono disposti in memoria sequenziale. Stampa di una matrice #include <stdio.h> int main() { int matrice[3][4]; int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf(matrice[%d][%d] = %d \n,i,j,matrice[i][j]); } } return 0; }

Tipo di dato char


Per memorizzare dei caratteri, il linguaggio C utilizza il tipo char, il quale rappresenta uno dei tipi di dati interi numerici del C. Se char un tipo numerico, come pu essere utilizzato per memorizzare caratteri? La risposta sta nel modo in cui il C memorizza i caratteri. La memoria del computer registra tutti i dati in un formato numerico, non esiste un modo diretto per memorizzare i caratteri. Tuttavia, per ciascun carattere esiste un codice numerico, chiamato codice ASCII, o set di caratteri ASCII (lacronimo ASCII significa American Standard Code for Information Interchange). Tramite questo codice vengono assegnati dei valori compresi tra 0 e 255 per le lettere maiuscole e minuscole, per le cifre numeriche, per i segni di punteggiatura e altri simboli. La tabella ASCII riportata in fondo a questa dispensa. Ad esempio 97 rappresenta il codice ASCII per la lettera a. Quando si registra il carattere a in una variabile di tipo char in realt viene memorizzato il numero 97. Poich la sequenza dei valori ammessi per il tipo char corrisponde esattamente al set di caratteri ASCII standard, il tipo char si adatta perfettamente a contenere i caratteri.

Pagina 2 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Tutto ci pu risultare motivo di confusione. Se il C registra i caratteri come numeri, come fa il programma a sapere se una variabile di tipo char rappresenta un carattere o un numero? Come verr chiarito in seguito, dichiarare una variabile di tipo char non sufficiente, necessario eseguire altre operazioni. Se una variabile char viene utilizzata in un pinto del programma C dove ci si aspetta un carattere, viene interpretata come carattere. Se la variabile char viene utilizzata in un punto del programma C dove ci si aspetta un numero, viene interpretata come un numero.

Come per altre variabili, prima di utilizzare le variabili di tipo char necessario dichiararle, ed possibile inizializzarle nel momento stesso in cui vengono dichiarate. Di seguito vengono riportati alcuni esempi: char a, b, c; /*dichiarazione di ter variabili di tipo char non inizializzate*/ char codice = x; /*Dichiarazione delle variabile char di nome codice e memorizzazione del carattere x al suo interno*/ codice = ! /*memorizzazione del carattere nella variabile codice*/ Per creare costanti di carattere di tipo letterake, necessario racchiudere un unico carattere tra una coppia di apici singoli. Per creare costanti composte da simboli si pu utilizzare sia la direttiva #define sia la parola chiave const: #define EX x char codice = EX; const char A = z; Nel listato sotto, viene illustrata la natura numerica delle operazioni di memorizzazione dei caratteri, tramite lutilizzo della funzione printf(). La funzione printf() pu essere utilizzata per stampare sia caratteri che numeri. La stringa di formato con il comando di conversione %c comunica a printf() di stampare un carattere, mentre %d indica di stampare un intero decimale. La sequenza dei valori ammessi per una variabile di tipo char si estende solo fino a 127, mentre i codici ASCII raggiungono il valore 255, I codici ASCII sono in realt divisi in due parti: quelli standard si estendono solo fino a 127; questa sequenza include tutte le lettere, i numeri, i segni di punteggiatura e altri simboli della tastiera. I codici compresi tra 128 e 255 sono dei codici ASCII estesi e rappresentano caratteri speciali, quali lettere straniere e simboli grafici. Quindi per i dati di testo standard, si possono utilizzare variabili di tipo char; se si vogliono stampare caratteri del codice ASCII esteso occorre utilizzare unsigned char. Nel listato sottostante vengono stampati i caratteri del codice ASCII esteso: #include <stdio.h> unsigned char x; int main() { for (x = 128; x <= 255; x++) { printf(Codice ASCII %d il carattere %c \n,x,x); } return 0; } Le variabili di tipo char possono contenere un unico carattere, perci hanno un utilizzo limitato, Pi uyille risulta la memorizzazione di Stringhe, rappresentate da sequenze di caratteri. Il mome e lindirizzo di una persona rappresenta un esempio di stringa. Anche se non esiste un tipo di dato speciale per le stringhe, il C gestisce questo tipo di informazioni tramite gli array di caratteri. /* imposta codice uguale a x*/

Pagina 3 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Per registrare una stringa di sei caratteri, ad esempio necessario dichiarare un array di tipo char composto da sette elementi. La dichiarazione degli array di tipo char identica a quella degli altri tipi. Ad esempio listruzione: char stringa[10]; dichiara un array di tipo char composto da 10 elementi, utilizzabile per contenere una stringa di un numero di caratteri pari o inferiore a nove. Qualcuno potrebbe chiedersi perch un array di 10 elementi possa contenere solo nove caratteri. Nel linguaggio C, una stringa definita come una sequenza di caratteri che termina con il carattere null, un carattere speciale rappresentato da \0. Nonostante sembri composto da due caratteri (barra inversa e zero), il carattere null viene interpretato come un unico carattere e possiede il valore ASCII 0 (zero). Quando un programma C, ad esempio, memorizza la stringa Alabama, registra i sette caratteri A, l, a, b, a, m, a, seguiti dal carattere null \0, per un totale di otto caratteri. In tal modo, un array di caratteri pu contenere una stringa composta da un numero di caratteri inferiore di ununit al numero di elementi che compongono larray. Una variabile di tipo char occupa lo spazio di un byte, perci il numero fi byte che compongono un array di variabili di tipo char pari al numero di elementi dellarray. Analogamente agli altri tipi di dati del C, gli array di caratteri possono essere inizializzati al momento della dichiarazione. A esse possibile assegnare un valore, elemento per elemento, come viene mostrato in seguito: char stringa[10] = {A, l, a, b, a, m, a, \0}; Conviene tuttavia utilizzare una stringa letterale, rappresentata da una sequenza di caratteri racchiusi tra doppi apici: char stringa[10] = Alabama; Quando allinterno del programma si utilizza una stringa letterale, il compilatore aggiunge automaticamente il carattere null finale. Se al momento della dichiarazione di un array non viene specificato il numero di componenti, il compilatore calcola automaticamente la dimensione dellarray. Quindi listruzione che segue crea e inizializza un array di otto elementi: char stringa[] = Alabama; Si tenga presente che le stringhe richiedono il carattere null finale. Le funzioni del C per la gestione delle stringhe stabiliscono la lunghezza delle stringa in base alla posizione del carattere null. Queste funzioni non anno altro modo per riconoscere la fine di una stringa. Se il carattere null viene omesso, il programma suppone che la stringa si estenda fino al successivo carattere null presente in memoria, e questo causa spiacevoli errori di programmazione.

Funzioni per le stringhe


Tutti i prototipi delle funzioni descritte successivamente si trovano allinterno della libreria string.h. La funzione strLen() Per determinare la lunghezza della stringa, cio il numero di caratteri che la compongono, si ricorre alla funzione di libreria strlen() il cui prototipo, in string.h, il seguente: size_t strlen(char *str);

Pagina 4 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Il tipo di ritorno size_t definito in string.h come unsigned. Largomento passato un puntatore alla stringa di cui si vuole sapere la lunghezza. La funzione restituisce il numero di caratteri compresi fra str e il carattere nullo, questultimo escluso. La funzione strcpy() La funzione di libreria strcpy() copia unintera stringa in una nuova posizione di memoria. Il suo prototipo il seguente: char * strcpy(char *destinatin, char *source); La funzione strcpy() copia la stringa a cui punta source (compreso il carattere nullo terminale) nella posizione in cui punta destination. Il valore restituito il puntatore alla stringa di destinazione. Per utilizzare strcpy() occorre prima allocare lo spazio in memoria per la stringa di destinazione. La funzione strncpy() La funzione di libreria strncpy() simile a strcpy(), ma richiede di specificare quanti caratteri devono essere copiati. Il suo prototipo il seguente: char * strncpy(char *destinazione, char *origine, size_t n); Gli argomenti destinazione e origine sono puntatori alle stringhe di destinazione e dorigine. La funzione copia al pi i primi n caratteri della stringa dorigine in quella di destinazione. Se la stringa origine contiene meno di n caratteri vengono aggiunti caratteri nulli fino a giungere a n. Se origine contiene pi di n caratteri, nella destinazione non viene inserito il carattere nullo terminale. La funzione restituisce un puntatore alla destinazione. La funzione strdup() La funzione di libreria strdup() simile a strcpy(), ma effettua da se lallocazione di memoria per la destinazione. Il suo prototipo il seguente: char * strdump(char *origine); Largomento origine un puntatore alla stringa dorigine. La funzione restituisce un puntatore alla stringa di destinazione, cio allo spazio allocato, oppure NULL se lallocazione fallita. La funzione strcat() Il prototipo di strcat() il seguente: char *strcat(char *str1, char *str2); La funzione di libreria strcat() appende una copia di str2 in coda a str1, spostando il carattere di terminazione alla fine della nuova stringa. Si deve allocare per str1 uno spazio sufficiente a contenere il risultato. La funzione restituisce un puntatore a str1. La funzione strncat() Il prototipo di strncat() il seguente: char *strncat(char *str1, char *str2, size_t n); La funzione di libreria strncat() concatena due stringhe ma permette di specificare quanti caratteri della seconda stringa devono essere concatenati alla prima. Se str2 contiene pi di n caratteri, solo i primi n vengono copiati a str1. Se str2_ contiene meno di n caratteri, lintera str2 appesa a str1. In entrambi i casi il carattere nullo viene appeso alla fine della stringa risultante, Si deve allocare per str1 spaizo sufficiente a contenere il risultato. La funzione restituisce un puntatore a str1.

Pagina 5 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

La funzione strcmp() La funzione strcmp() confronta due stringhe, carattere per carattere. Il suo prototipo il seguente: int strcmp(char *str1, char *str2); Gli argomenti str1 e str2 sono puntatori alle stringhe da confrontare. La funzione restituisce rispettivamente: Un valore minore di 0 se str1 minore di str2 Un valore uguale a 0 se str1 uguale a str2 Un valore maggiore di 0 se str1 maggiore di str2

La funzione strncmp() La funzione strncmp() confronta un numero specificato di caratteri di una stringa con una seconda stringa. Il suo prototipo il seguente: int strncmp(char *str1, char *str2, size_t n); La funzione confronta i primi n caratteri di str2 in str1. Il confronto procede finch non sono stati confrontati n caratteri oppure stata raggiunta la fine di str1. Il criterio di confronto e il valore restituito sono gli stessi di strcmp(). La funzione strchr() La funzione strchr() cerca la prima occorrenza di un carattere dato in una stringa. Il suo prototipo il seguente: char *strchr(char *str1, int ch); La funzione esplora str da sinistra a destra finch trova il carattere ch oppure fino a incontrare il carattere nullo terminare. Se ch stato trovato, la funzione restituisce un puntatore alla posizione di ch, altrimenti restituisce NULL. La funzione strrchr() La funzione strrchr() simile a strrchr(), ma cerca lultima occorrenza del carattere dato nella stringa. Il suo prototipo il seguente: char *strrchr(char *str1, int ch); La funzione strrchr() restituisce un puntatore allultima occorrenza di ch in str oppure NULL se ch con compare in str. La funzione strcspn() La funzione strcspnr() cerca la prima occorrenza in una stringa di uno dei qualsiasi dei caratteri che compongono una seconda stringa. Il suo prototipo il seguente: size_t strcspn(char *str1, char *str2); La funzione comincia confrontando il primo carattere di str1 con tutti i caratteri di str2. Si sottolinea che la funzione non cerca loccorrenza di tutta la stringa Str2, ma solo dei singoli caratteri che la compongono. Se viene trovato uno di tali caratteri la funzione restituisce la sua distanza dallinizio di str1.Se non ci sono corrispondenze, la funzione restituisce il valore di strlen(str1) o in altre parole la posizione nella stringa del carattere nullo terminale. La funzione strstr()

Pagina 6 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

La funzione strstr() cerca la prima occorrenza in una stringa entro una seconda stringa. Il suo prototipo il seguente: char *strstr(char *str1, char *str2); La funzione restituisce un puntatore alla prima occorrenza di str2 in str1. Se str2 non compare in str1 la funzione restituisce NULL. Se la lunghezza di str1 0, la funzione restituisce str1. Le funzioni strlwr() e strupr() Molte librerie per il C contengono due funzioni che convertono le maiuscole in minuscole e viceversa. Queste funzioni non rientrano nello standard ANSI e possono quindi mancare o essere lievemente diverse in un particolare compilatore. Per i compilatori C della Microsoft, i prototipi delle funzioni sono: char *strlwp(char *str); char *strupr(char *str); La funzione strlwp() pone in minuscolo tutte le lettere di str, mentre la funzione strupr() converte in maiuscolo tutte le lettere. I caratteri diversi dalle lettere dellalfabeto restano invariati. Entrambe le funzioni restituiscono un puntatore a str. La funzione strrev() La funzione strrev() rovescia lordine dei caratteri in una stringa. Il suo prototipo il seguente: char *strrev(char *str); Lordine dei caratteri di str viene rovesciato. Il carattere nullo rimane nellultima posizione. La funzione restituisce un puntatore a str. La funzione atoi() La funzione atoi() trasforma una stringa in un numero. Il suo prototipo il seguente: int atoi(char *str); La funzione converte la stringa a cui punta str in un numero intero. Se la funzione non contiene caratteri convertibili restituisce 0. La funzione atol() La funzione atol() seguente: opera come atoi() solo che restituisce un valore di tipo long. Il suo prototipo il

long atol(char *str); La funzione atof() La funzione atof() rasforma una stringa in un valore double. Il suo prototipo il seguente: double atof(char *str); Funzioni che esaminano caratteri Il file di librerie ctype.h contiene i prototipi di numerose funzioni che esaminano un carattere e restituiscono TRUE o FALSE a seconda che il carattere verifichi o meno una data definizione. I prototipi di queste funzioni sono del tipo seguente: int isxxxx(int ch);

Pagina 7 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Largomento ch il carattere da esaminare. La funzione restituisce TRUE (un valore diverso da zero) se ch soddisfa la condizione o FALSE (zero).

Funzione

Descrizione Restituisce TRUE se ch una lettera o una cifra Restituisce TRUE se ch una lettera Restituisce TRUE se ch un carattere ASCII standard (compreso tra 0 e 127) Restituisce TRUE se ch un carattere di controllo Restituisce TRUE se ch una cifra Restituisce TRUE se ch un carattere stampabile diverso da uno spazio Restituisce TRUE se ch una lettera minuscola Restituisce TRUE se ch un carattere stampabile compreso lo spazio Restituisce TRUE se ch un segno di interputazione Restituisce TRUE se ch un carattere bianco,, cio spazio o tabulazione Restituisce TRUE se ch una lettera maiuscola Restituisce TRUE se ch una cifra esadecimale

isalnum() isalpha() isscii() iscntrl() isdigit() isgraph() islower() isprint() ispunct() isspace() isupper() isxdigit()

Pagina 8 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 11

Esercizio 1 Scrivere un programma che calcoli la somma di tutti gli elementi di una matrice. Esercizio 2 Scrivere un programma che determini quale la riga di una matrice cui la somma degli elementi massima. Esercizio 3 Presa una matrice quadrata a valori interi determinare se si tratta di una matrice magica. Una matrice si dice magica se la somma di ogni riga uguale alla somma di ogni colonna e questo valore anche uguale alla somma degli elementi della diagonale principale e alla somma degli elementi della diagonale. Esercizio 4 Scrivere un programma che prese in ingresso 10 parole da un utente dica quali parole si ripetono almeno 2 volte nellelenco inserito.

Pagina 9 di 9

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 12
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 12

Esempio: Gioco dellImpiccato


Il gioco
L'impiccato un gioco per due giocatori. Uno dei giocatori sceglie segretamente una parola o una frase; l'altro deve indovinarla entro un certo numero di tentativi. La frase viene mostrata al secondo giocatore mascherata: mostrando al posto delle vocali il simbolo + e al posto delle consonanti il simbolo -, gli altri caratteri (spazi o punteggiatura) rimangono invariati. Il gioco termina quando la parola viene indovinata, o si finiscono i tentativi a disposizione. Lalgoritmo risolutivo per il gioco potrebbe essere: 1. Chiedere al primo giocatore di inserire la frase da indovinare 2. Mascherare la frase inserita, sostituendo al posto delle vocali il simbolo + e al posto delle consonanti il simbolo - 3. Chiedere al secondo giocatore di inserire una lettera 4. Verificare che la lettera inserita sia presente allinterno della frase, se la lettera presente sostituire il simbolo con la lettera indovinata 5. Stampare la frase mascherata con le eventuali lettere indovinate 6. Se la frase non stata indovinata vai al punto 3, altrimenti comunica allutente che ha indovinato la frase 7. Esci dal gioco Lalgoritmo sopra descritto non risolve a pieno il gioco in quanto non tiene traccia del numero massimo di tentativi che un giocatore pu fare, in particolare quando si supera il numero di tentativi bisognerebbe interrompere il gioco in quanto lutente ha perso. Per tenere traccia del numero di tentativi si potrebbe utilizzare un contatore incrementato ogni volta che lutente inserisce una nuova lettera. Lalgoritmo potrebbe diventare quindi: 1. Chiedere al primo giocatore di inserire la frase da indovinare 2. Mascherare la frase inserita, sostituendo al posto delle vocali il simbolo + e al posto delle consonanti il simbolo - 3. Chiedere al secondo giocatore di inserire una lettera 4. Incrementare il contatore di un unit 5. Verificare che la lettera inserita sia presente allinterno della frase, se la lettera presente sostituire il simbolo con la lettera indovinata 6. Stampare la frase mascherata con le eventuali lettere indovinate 7. Se la frase stata indovinata comunicare la vittoria e andare al punto 10 8. Se il numero di tentativi (e quindi il valore del contatore) uguale al massimo dei tentativi disponibili comunica allutente che ha perso e vai al punto 10 9. Se la frase non stata indovinata vai al punto 3 10. Esci dal gioco Questo secondo algoritmo per permette di indovinare la frase inserendo esclusivamente lettera per lettera, potrebbe capitare che il numero di lettere diverse in una frase sia superiore al numero di tentativi e quindi il gioco non consentirebbe allutente di arrivare in fondo e quindi di vincere. La cosa migliore da fare potrebbe essere quella di dare la possibilit allutente di indovinare la frase ad un certo punto del gioco o meglio di

Pagina 2 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 12

poter scegliere se inserire una nuova lettera o indovinare la frase. In questo modo possibile assegnare anche un punteggio sulla base dei tentativi fatti. Il nuovo algoritmo potrebbe essere: 1. Chiedere al primo giocatore di inserire la frase da indovinare 2. Mascherare la frase inserita, sostituendo al posto delle vocali il simbolo + e al posto delle consonanti il simbolo - 3. Chiedere al secondo giocatore se vuole indovinare la frase o inserire una lettera 4. Se il giocatore vuole indovinare la frase vai al punto 5 altrimenti al punto 7 5. Chiedere allutente di inserire la frase 6. Se la frase stata indovinata comunicare la vittoria e andare al punto 14 altrimenti vai al punto 3 7. Chiedere allutente di inserire una lettera 8. Incrementare il contatore delle lettere inserite di un unit 9. Verificare che la lettera inserita sia presente allinterno della frase, se la lettera presente sostituire il simbolo con la lettera indovinata 10. Stampare la frase mascherata con le eventuali lettere indovinate 11. Se le lettere sono state tutte scoperte comunicare la vittoria e andare al punto 14 12. Se il numero di tentativi (e quindi il valore del contatore) uguale al massimo dei tentativi disponibili comunica allutente che ha perso e vai al punto 14 13. Se la frase non stata indovinata vai al punto 3 14. Esci dal gioco Questo algoritmo per consentirebbe ad un utente di provare allinfinito di indovinare la frase, senza mai inserire una lettera, potremo quindi tenere traccia anche del numero di tentativi di indovinare la frase tutto in un colpo. Lalgoritmo finale potrebbe essere: 1. Chiedere al primo giocatore di inserire la frase da indovinare 2. Mascherare la frase inserita, sostituendo al posto delle vocali il simbolo + e al posto delle consonanti il simbolo - 3. Chiedere al secondo giocatore se vuole indovinare la frase o inserire una lettera 4. Se il giocatore vuole indovinare la frase vai al punto 5 altrimenti al punto 8 5. Chiedere allutente di inserire la frase 6. Se la frase stata indovinata comunicare la vittoria e andare al punto 15 altrimenti vai al punto 7 7. Dato che la frase non stata indovinata incrementare il contatore che tiene traccia dei tentativi, se tale numero supera il massimo consentito comunicare allutente che ha perso e andare al punto 15 altrimenti andare al punto 3 8. Chiedere allutente di inserire una lettera 9. Incrementare il contatore delle lettere inserite di un unit 10. Verificare che la lettera inserita sia presente allinterno della frase, se la lettera presente sostituire il simbolo con la lettera indovinata 11. Stampare la frase mascherata con le eventuali lettere indovinate 12. Se le lettere sono state tutte scoperte comunicare la vittoria e andare al punto 15 13. Se il numero di tentativi (e quindi il valore del contatore) uguale al massimo dei tentativi disponibili comunica allutente che ha perso e vai al punto 15 14. Se la frase non stata indovinata vai al punto 3

Pagina 3 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


15. Esci dal gioco

Corso di Programmazione A.A. 2011-12

Dispensa 12

Sicuramente potrebbero essere fatte molte altre migliorie, per per implementare il gioco di base questo ultimo algoritmo dovrebbe rispondere a tutte le esigenze.

Implementazione lalgoritmo
Suddivisione in sottoproblemi
Lultimo algoritmo potrebbe essere scomposto in 3 sotto problemi: 1. Richiesta e lettura della frase da indovinare 2. Sostituzione delle vocali con il carattere + e delle consonanti con il carattere - 3. Ricerca di una lettera allinterno di una frase Richiesta e lettura della frase da indovinare Il linguaggio C non ha un tipo di dato primitivo per la gestione delle stringhe, infatti esse vengono considerate come una sequenza di caratteri con laggiunta di un carattere speciale di terminazione \0 per individuarne la fine. Quindi per esempio volendo memorizzare la frase ciao mondo in un array di 15 caratteri il risultato che otterremo sar: c
0

i
1

a
2

o
3 4

m
5

o
6

n
7

d
8

o
9

\0
10 11 12 13 14

Ipotizzando che la frase da indovinare non superi i 20 caratteri (incluso spazi, caratteri di punteggiatura...) per la memorizzazione della stringa dovremo dichiarare un array di caratteri di dimensione 21: 20 per memorizzare la stringa e 1 per il carattere terminatore: char frase[21]; Literazione con lutente molto importante e quindi bisogna comunicare attraverso delle stampe a video le istruzioni che dovranno essere eseguite, quindi attraverso una funzione printf(): printf(Inserire la frase da indovinare e premere INVIO: ); La lettura della frase inserita dallutente potrebbe essere fatta attraverso la funzione scanf() utilizzando il carattere di conversione %s apposito per le stringhe: scanf(%s,frase); Da notare che allinterno della chiamata alla funzione scanf() la variabile frase non viene preceduta dalloperatore dindirizzo & (e-commerciale) in quanto tale operatore va utilizzato esclusivamente per le variabili ti tipo primitivo.

Pagina 4 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


#include <stdio.h> int main() { char frase[21]; printf(Inserire la frase da indovinare e premere INVIO: ); scanf(%s,frase); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 12

Questa soluzione non ottimale in quanto utilizzando il carattere di conversione %s la funzione scanf() considera come fine della frase NON solo la pressione del tasto INVIO ma anche il carattere spazio. Quindi inserendo la frase: ciao mondo, il programma memorizzer allinterno dellarray frase solamente la parola ciao. Per ovviare a questo problema potremo utilizzare il carattere di conversione %[^...] il quale ci consente di specificare quali caratteri non sono ammessi nella lettura e quindi quelli che verranno considerati come fine stringa (al posto dei puntini vanno inseriti i caratteri non ammessi). In questo caso lunico carattere non ammesso lINVIO (o meglio nuova linea o ritorno a carrello) individuato dal carattere speciale \n: scanf(%[^\n],frase); La funzione scanf() non lunica funzione che consente la lettura da tastiera, infatti esistono altre funzioni per linput di riga, ad esempio gets(). Tale funzione si limita a leggere una riga da stdin e la memorizza in una stringa (array di caratteri). La funzione legge caratteri finch non incontra un carattere di nuova riga (\n) o la fine della riga: gets(frase); In questo modo tutti i caratteri inseriti, spazi inclusi verranno letti e memorizzati allinterno dellarray frase. Potemmo ottimizzare il nostro programma inserendo una costante per gestire la dimensione per la stringa dato che tale dimensione sicuramente dovr essere utilizzata in altri punti del nostro codice. Quindi il sorgente potrebbe essere: #include <stdio.h> #define LUNGHEZZA_FRASE 20 int main() { char frase[LUNGHEZZA_FRASE+1]; printf(Inserire la frase da indovinare e premere INVIO: ); gets(frase); return 0; } Potrebbe capitare che un utente inserisca una frase con un numero di caratteri maggiore di 20, per evitare tale problema potremo stampare a video il limite massimo consentito ma questo non ci garantirebbe al 100% che linserimento da parte dellutente risulti corretto. Sia la funzione scanf() che la funzione gets() entrano in azione SOLO dopo la pressione del tasto INVIO, accedendo al buffer di lettura fino a trovare il carattere terminatore. Per ovviare al problema potremo utilizzare la funzione getchar() la quale ci consente di leggere un carattere alla volta dal buffer. La funzione getchar() attende la ricezione di un carattere da stdin. Poich getchar() una funzione di input con buffer, non viene ricevuto alcun carattere finch lutente non preme INVIO. Tuttavia ogni tasto premuto viene immediatamente mostrato sullo schermo. Quando si preme INVIO, tutti i caratteri immessi, incluso

Pagina 5 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 12

quello di nuova riga vengono inviati a stdin dal sistema operativo. La funzione getchar() restituisce i caratteri uno alla volta: i = 0; while ((ch = getchar()) != frase[i++] = ch; frase[i] = \0; I caratteri di volta in volta letti vengono inseriti allinterno dellarray fino ad arrivare al carattere terminatore o alla dimensione massima dellarray. In questo modo riusciamo ad evitare che allinterno dellarray frase che ci siano pi caratteri di quelli consentiti ma non ad impedire che lutente scriva pi di 20 caratteri. Per evitare questo ultimo problema potremo utilizzare le funzioni getch() e getche() le quali consentono di leggere direttamente il carattere successivo dal flusso stdin. In poche parole man mano che lutente inserisce i caratteri questi vengono letti dalla funzione. Utilizziamo la funzione getche() in quanto funzione con eco cio mostra i caratteri letti nel flusso stdout: i = 0; while ((ch = getche()) != 13 && i < LUNGHEZZA_FRASE) frase[i++] = ch; frase[i] = \0; Lutente potrebbe inserire una frase vuota cio quindi premere direttamente INVIO, ovviamente questa cosa non dovrebbe essere consentita. Potremo risolvere anche questultima problematica controllando il valore della variabile i dopo il ciclo while: se la variabile vale 0 allora non sono stati inseriti caratteri utili: i = 0; do { while ((ch=getche()) != 13 && i < LUNGHEZZA_FRASE) frase[i++] = ch; if (i == 0) printf(Hai inserito una frase vuota! \n); } while(i == 0); frase[i] = \0; Il tutto viene inserito allinterno di un ciclo do while per ripetere loperazione fino a quando lutente non inserir un valore corretto. Per fare in modo che lutente possa decidere ad un certo punto dellinserimento della frase di uscire dal programma potremo considerare per esempio che alla pressione del tasto - (meno) il programma termini. Per ottenere questo effetto dovremo controllare di volta in volta il carattere inserito dallutente: i = 0; do { while ((ch=getche()) != \n && ch != \r && i < LUNGHEZZA_FRASE) { if (ch == -) exit(0); frase[i++] = ch; } if (i == 0) printf(Hai inserito una frase vuota! \n);

13 && i < LUNGHEZZA_FRASE)

Pagina 6 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


} while(i == 0); frase[i] = \0; Quindi il programma per linserimento della frase diventa: #include <stdio.h> #include <stdlib.h> #include <conio.h> #define LUNGHEZZA_FRASE 20 int main() { char frase[LUNGHEZZA_FRASE+1]; int i; char ch;

Corso di Programmazione A.A. 2011-12

Dispensa 12

printf(Inserire la frase da indovinare e premere INVIO [- esci]: ); i = 0; do { while ((ch=getche()) != 13 && i < LUNGHEZZA_FRASE) { if (ch == -) exit(0); frase[i++] = ch; } if (i == 0) printf(Hai inserito una frase vuota! \n); } while(i == 0); frase[i] = \0; return 0; } Sostituzione delle vocali e delle consonanti Per poter sostituire tutte le vocali con il carattere + e tutte le consonanti con il carattere -, dovremo accedere singolarmente ad ogni cella dellarray contenente la stringa per valutarne ogni singolo carattere: for (i = 0; i < LUNGHEZZA_FRASE; i++) { if (frase[i] == a || frase[i] == e || frase[i] == i || frase[i] == o || frase[i] == u) frase[i] = +; else frase[i] = -; } Invece di utilizzare unistruzione if per i controlli potremo utilizzare unistruzione switch: for (i = 0; i < LUNGHEZZA_FRASE; i++)

Pagina 7 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ switch(frase[i]) { case a: case e: case i: case o: case u: frase[i] = +; break; default: frase[i] = -; }

Corso di Programmazione A.A. 2011-12

Dispensa 12

Le soluzioni presentate in precedenza non sono ottimali in quanto vengono considerate indipendentemente dalla lunghezza della frase tutte le celle presenti allinterno dellarray. Per ovviare al problema potremo inserire un controllo ulteriore allinterno della condizione del for per valutare che la cella considerata non contenga il carattere terminatore della stringa: for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { if (frase[i] == a || frase[i] == e || frase[i] == i || frase[i] == o || frase[i] == u) frase[i] = +; else frase[i] = -; } Se lutente inserisce lettere maiuscole, dato che il linguaggio C case sensitive e quindi il carattere a minuscolo risulter diverso dal carattere A maiuscolo, il programma potrebbe rispondere in maniera errata. Potremo quindi allinterno dellif o dello switch considerare tutti i caratteri (in questo caso le vocali) anche in maiuscolo oppure utilizzando la funzione tolower() potremo trasformare per la verifica tutti i caratteri in minuscolo: for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { carattere = tolower(frase[i]); if (carattere == a || carattere == e || carattere == i || carattere == o || carattere == u) frase[i] = +; else frase[i] = -; } La nuova soluzione presenta comunque ancora degli errori in termini di algoritmo: inserendo una frase con spazi o caratteri speciali questi verranno sostituiti con il simbolo - in quanto lunico controllo quello che valuta se il carattere corrisponde ad una vocale. Le soluzioni a questo secondo problema possono essere varie: potremo inserire altri controlli allinterno dellistruzione else per valutare se il carattere una costante oppure potremo creare allinterno dello switch tanti case uno per ogni costante (allo stesso modo che stato fatto per le vocali). Dato che i caratteri vengono memorizzati attraverso dei numeri descritti attraverso la tabella ASCII, e dato che le lettere dellalfabeto (in minuscolo) allinterno di tale tabella hanno codici che variano da 97 a 122 il programma potrebbe essere ottimizzato nel seguente modo:

Pagina 8 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { carattere = tolower(frase[i]); if (frase[i] >= 97 && frase[i] <= 122) { if (carattere == a || carattere == e || carattere == i || carattere == o || carattere == u) frase[i] = +; else frase[i] = -; } }

Corso di Programmazione A.A. 2011-12

Dispensa 12

In questo modo verranno sostituite solamente le lettere appartenenti allalfabeto. Rimangono per escluse tutte le lettere accentate. Per gestire anche questa problematica dovremmo utilizzare variabili di tipo unsigned char in quanto tali caratteri fanno parte della tabella ASCII estesa. Comunque per evitare di complicare troppo il programma non considereremo le lettere accentate. Ricerca di una lettera allinterno di una frase Per trovare tutte le occorrenze di una lettera allinterno di una frase bisogna eseguire dei confronti singolarmente con tutti i caratteri della stringa data. Considerando quanto detto nei paragrafi precedenti, la stringa inserita dallutente viene modificata sostituendo le varie lettere dellalfabeto con dei caratteri speciali, quindi non sar pi possibile per il programma conoscere quali lettere effettivamente saranno presenti allinterno della frase dopo le varie modifiche a meno che non esita una copia della frase iniziale o la gestione della stringa crittografata, con i simboli + e -, non venga fatta con un altro array: for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { carattere = tolower(frase[i]); if (frase[i] >= 97 && frase[i] <= 122) { if (carattere == a || carattere == e || carattere == i || carattere == o || carattere == u) fraseCrittografata[i] = +; else fraseCrittografata [i] = -; } else fraseCrittografata[i] = frase[i]; } fraseCrittografata[i] = \0; La variabile fraseCrittografata un array della stessa dimensione e tipologia dellarray frase. Ora con questa modifica, presa una lettera da cercare dallutente potremo individuare tutte le occorrenze e sostituire ogni volta nella frase crittografata la lettera indovinata: printf(Iserire una lettera e premere INVIO: ); scanf(%c,&lettera); for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { if (tolower(frase[i]) == tolower(lettera))

Pagina 9 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


fraseCrittografata[i] = frase[i]; }

Corso di Programmazione A.A. 2011-12

Dispensa 12

Da notare che nella funzione scanf() la variabile lettera viene preceduta dalloperatore indirizzo ecommericale (&) e che allinterno del for i confronti delle lettere vengono fatti considerando le lettere in minuscolo sempre per evitare il problema della gestione case sensitive. Per evitare che lutente debba premere INVIO dopo ogni inserimento potremo utilizzare la funzione getche() descritta in precedenza. Inoltre dato che possono essere inseriti caratteri speciali diversi dalle lettere dellalfabeto potremo eseguire un controllo per verificare la correttezza dellinserimento: do { printf(Iserire una lettera: ); fflush(stdin); lettera = tolower(getche()); if (lettera < 97 || lettera > 122) printf(Errore di inserimento: carattere non valido\n); } while(lettera < 97 || lettera > 122); for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { if (tolower(frase[i]) == lettera) fraseCrittografata[i] = frase[i]; } Per poter comunicare allutente se la lettera inserita presente o meno allinterno della frase nascosta bisogna valutare al di fuori del corpo del for se si entrati almeno una volta allinterno del if che confronta la lettera inserita con li-esima della frase. Questo tipo di controllo potrebbe essere fatto con una variabile impostata ad un certo valore prima del for e modificata esclusivamente allinterno del corpo dellistruzione if. Se si accede al corpo dellif, e quindi se esiste almeno un occorrenza della lettera inserita dallutente allinterno della frase, allora la variabile verr modificata altrimenti rimarr con il suo valore iniziale: trovato = 0; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { if (tolower(frase[i]) == lettera) { fraseCrittografata[i] = frase[i]; trovato = 1; } } if (trovato) printf(Lettera trovata!); else printf(La lettera non presente nella frase!);

Pagina 10 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 12

Ottimizzazione dei sottoproblemi in relazione al programma


Dato che bisogna dare la possibilit ad un utente di indovinare la frase oltre che inserire una lettera o eventualmente di uscire dal gioco, potremo associare queste funzionalit alla pressione di tasti speciali quindi considerare che se premendo il tasto - (meno) si esce dal gioco, premendo il tasto ? possibile dare la soluzione, altrimenti viene considerata la lettera inserita e fatta una ricerca allinterno della frase: do { printf(Iserire una lettera [- esci; ? indovina]: ); fflush(stdin); lettera = tolower(getche()); if (lettera == -) exit(0); if (lettera != ? && (lettera < 97 || lettera > 122)) printf(Errore di inserimento: carattere non valido\n); } while(lettera != ? && (lettera < 97 || lettera > 122)); if (lettera == ?) { printf(Inserisci la frase nascosta e premere INVIO: ); i = 0; while ((ch =getche()) != 13 && i < LUNGHEZZA_FRASE) fraseUtente[i++] = ch; fraseUtente[i] = '\0'; if (strcmp(strlwr(frase), strlwr(fraseUtente)) == 0) { printf(Hai indovinato!); exit(0); } else printf(Frase non corretta!); } else { /*parte dedicata alla ricerca della lettera*/ .... } La funzione strlwr() consente di trasformare una stringa tutta con caratteri in minuscolo, mentre la funzione strcmp() consente di confrontare due stringhe. Linserimento della frase da parte dellutente viene gestito come allinizio del programma per inserire la frase da indovinare, per gli stessi motivi descritti in precedenza. Volendo limitare il numero di tentativi per indovinare la frase e un numero massimo di lettere da poter inserire dovranno essere utilizzare due contatori rispettivamente per il numero di lettere inserite e il numero di tentativi di indovinare. Tali contatori verranno di volta in volta confrontati con i massimi consentiti (tutta la parte del gioco viene inserita allinterno di un ciclo per ripetere gli inserimenti fino a quando lutente non raggiunge i limiti dei tentativi o indovina la frase o decide di uscire dal gioco): numeroLettere = 0; numeroTentativi = 0; do {

Pagina 11 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


do {

Corso di Programmazione A.A. 2011-12

Dispensa 12

printf(Iserire una lettera [- esci; ? indovina]: ); fflush(stdin); lettera = tolower(getche()); if (lettera == -) exit(0); if (lettera != ? && (lettera < 97 || lettera > 122)) printf(Errore di inserimento: carattere non valido\n); } while(lettera != ? && (lettera < 97 || lettera > 122)); if (lettera == ?) { printf(Inserisci la frase nascosta e premere INVIO: ); while ((ch =getche()) != 13 && i < LUNGHEZZA_FRASE) fraseUtente[i++] = ch; fraseUtente[i] = '\0'; numeroTentativi++; if (strcmp(strlwr(frase), strlwr(fraseUtente)) == 0) { printf(Hai indovinato!); exit(0); } else printf(Frase non corretta!); if (numeroTentativi >= MASSIMO_TENTATIVI) { printf(Hai esaurito i tentativi a tua disposizione!); exit(0); } } else { numeroLettere++; trovato = 0; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { if (tolower(frase[i]) == lettera) { fraseCrittografata[i] = frase[i]; trovato = 1; } } if (trovato) printf(Lettera trovata!); else printf(La lettera non presente nella frase!); printf(\n%s, fraseCrittografata); if (numeroLettere >= MASSIMO_INSERIMENTI) { printf(Hai raggiunto il numero massimo di inserimenti!); printf(Prova a indovinare la frase nascosta: ); i = 0; while ((ch=getche())!=\n && ch!=\r && i<LUNGHEZZA_FRASE)

Pagina 12 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


fraseUtente[i++] = ch; if (strcmp(strlwr(frase), strlwr(fraseUtente)) == 0) printf(Hai indovinato!); else printf(Frase non corretta! Hai perso!); exit(0); } } } while(1);

Corso di Programmazione A.A. 2011-12

Dispensa 12

Volendo fare le cose a modo, bisognerebbe che il numero massimo di inserimenti possibili risulti uguale al massimo al numero di lettere differenti presenti allinterno della frase: int elencoLettereAlfabeto[26]; int numeroMassimoInserimentiLettere; .../*altro codice*/ for (i = 0; i < 26; i++) elencoLettereAlfabeto[i] = 0; numeroMassimoInserimentiLettere = 0; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != \0; i++) { lettera = tolower(frase[i]); if (lettera >= 97 && lettera <= 122) { if (elencoLettereAlfabeto[lettera-97] != 1) numeroMassimoInserimentiLettere++; elencoLettereAlfabeto[lettera-97] = 1; } } dove elendoLettereAlfabeto un array di interi, ogni cella associata logicamente ad una lettera dellalfabeto. Tale array verr utilizzato per tenere traccia delle lettere della frase gi considerate nel conteggio.

Unione dei sottoproblemi per ottenere il programma


Sicuramente potrebbero essere fatte molte altre migliorie al programma, comunque quanto detto risulta pi che sufficiente per creare una buona soluzione:
#include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <conio.h> <ctype.h>

#define LUNGHEZZA_FRASE 20 #define MASSIMO_TENTATIVI 3 int main() { char fraseCrittografata[LUNGHEZZA_FRASE+1]; char frase[LUNGHEZZA_FRASE+1]; char fraseUtente[LUNGHEZZA_FRASE+1];

Pagina 13 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


int i, trovato; char carattere, lettera, ch; int numeroMassimoInserimentiLettere; int numeroLettere, numeroTentativi; int elencoLettereAlfabeto[26]; system("cls"); printf("Inserire la frase da indovinare e premere INVIO [- esci]:\n"); i = 0; do { fflush(stdin); while ((ch=getche()) != 13 && i < LUNGHEZZA_FRASE) { if (ch == '-') { exit(0); system("pause"); } frase[i++] = ch; } if (i == 0) printf("\nHai inserito una frase vuota! \n"); } while(i == 0); frase[i] = '\0'; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != '\0'; i++) { carattere = tolower(frase[i]); if (frase[i] >= 97 && frase[i] <= 122) { if (carattere == 'a' || carattere == 'e' || carattere == 'i' || carattere == 'o' || carattere == 'u') fraseCrittografata[i] = '+'; else fraseCrittografata [i] = '-'; } else fraseCrittografata[i] = frase[i]; } fraseCrittografata[i] = '\0'; for (i = 0; i < 26; i++) elencoLettereAlfabeto[i] = 0; numeroMassimoInserimentiLettere = 0; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != '\0'; i++) { lettera = tolower(frase[i]); if (lettera >= 97 && lettera <= 122) { if (elencoLettereAlfabeto[lettera-97] != 1) numeroMassimoInserimentiLettere++; elencoLettereAlfabeto[lettera-97] = 1; } } numeroLettere = 0; numeroTentativi = 0; system("cls"); do {

Corso di Programmazione A.A. 2011-12

Dispensa 12

Pagina 14 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


do {

Corso di Programmazione A.A. 2011-12

Dispensa 12

printf("\n\nFrase da indovinare: \n%s\n\n", fraseCrittografata); printf("Iserire una lettera [- esci; ? indovina]: "); fflush(stdin); lettera = tolower(getche()); if (lettera == '-') { exit(0); system("pause"); } if (lettera != '?' && (lettera < 97 || lettera > 122)) printf("\nErrore di inserimento: carattere non valido\n"); } while(lettera != '?' && (lettera < 97 || lettera > 122)); if (lettera == '?') { printf("\nInserisci la frase nascosta e premere INVIO: "); i = 0; while ((ch =getche()) != 13 && i < LUNGHEZZA_FRASE) fraseUtente[i++] = ch; fraseUtente[i] = '\0'; numeroTentativi++; if (strcmp(strlwr(frase), strlwr(fraseUtente)) == 0) { printf("\nHai indovinato!"); system("pause"); exit(0); } else printf("\nFrase non corretta!"); if (numeroTentativi >= MASSIMO_TENTATIVI) { printf("\nHai esaurito i tentativi a tua disposizione!"); system("pause"); exit(0); } } else { numeroLettere++; trovato = 0; for (i = 0; i < LUNGHEZZA_FRASE && frase[i] != '\0'; i++) { if (tolower(frase[i]) == lettera) { fraseCrittografata[i] = frase[i]; trovato = 1; } } if (trovato) printf("\nLettera trovata!"); else printf("\na lettera non presente nella frase!"); if (numeroLettere >= numeroMassimoInserimentiLettere) { printf("\nHai raggiunto il numero massimo di inserimenti!"); printf("\nProva a indovinare la frase nascosta: "); i = 0; while ((ch=getche())!=13 && i <LUNGHEZZA_FRASE) fraseUtente[i++] = ch;

Pagina 15 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


if (strcmp(strlwr(frase), strlwr(fraseUtente)) == 0) printf("\nHai indovinato!"); else printf("\nFrase non corretta! Hai perso!"); system("pause"); exit(0); } } } while(1); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 12

Pagina 16 di 16

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 13
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

13.1
13.1.1

I Puntatori
La memoria del computer

La memoria RAM di un PC consiste di molte migliaia di locazioni di memoria sequenziali, ciascuna identificata da un indirizzo unico. Questi indirizzi sono compresi tra 0 e un massimo che dipende dalla quantit di memoria installata. Quando si utilizza un computer, il sistema operativo occupa una parte di questa memoria. Quando si avvia un programma, il suo codice (ovvero le sue istruzioni in linguaggio macchina) e i suoi dati (ovvero le informazioni che il programma sta elaborando) occupano delle altre zone di questa memoria. Quando si dichiara una variabile in un programma in C, il compilatore riserva una locazione di memoria puntata da un indirizzo in modo da immagazzinarvi in seguito un valore di una variabile. Quindi, in effetti, il compilatore associa un indirizzo al nome della variabile stessa. Quando il programma utilizza il nome di quella variabile, accede automaticamente alla locazione di memoria relativa. In questa operazione viene utilizzato lindirizzo della locazione di memoria, ma esso viene nascosto allutente perch in genere non lo interessa. La figura riportata sotto mostra questo procedimento in maniera schematica: viene dichiarata e inizializzata al valore 100 una variabile di nome rate. Il compilatore ha riservato per tale variabile una locazione di memoria che parte dallindirizzo 1004 e ha associato al nome rate lindirizzo 1004. 1001 1002 1003 1004 100 1005

rate

13.1.2

Creazione di un puntatore

Si sar notato che lindirizzo della variabile rate(e qualsiasi altra variabile) un numero che, in quanto tale pu essere gestito dalle istruzioni C. Se si conosce lindirizzo di una variabile possibile creare una nuova variabile che lo contenga. Il primo passo quello di dichiarare la variabile che conterr lindirizzo di rate. Le si da il nome p_rate ad esempio. Allinizio questa variabile non inizializzata; le stato riservato uno spazio in memoria, ma il valore al suo interno indeterminato. 1001 ? 1002 1003 1004 100 1005

p_rate

rate

Il passo successivo quello di memorizzare lindirizzo della variabile rate nella variabile p_rate. Dato che a quel punto la variabile p_rate contiene lindirizzo di rate. La prima variabile indicher la posizione di memoria dov stata dislocata rate. 1001 1004 1002 1003 1004 100 1005

p_rate
Pagina 2 di 11

rate

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

In C si adotta una terminologia particolare e si dice che p_rate punta a rate, oppure che p_rate un puntatore a rate. In fine un puntatore una variabile che contiene lindirizzo di unaltra variabile.

13.1.3

Dichiarazioni di puntatori

Nellesempio prima descritto, il puntatore puntava ad una variabile scalare (non ad un array). I puntatori sono variabili numeriche, e come tali, devono essere dichiarati prima delluso, I nomi dei puntatori seguono le stesse regole di quelli delle altre variabili e devono essere unici. Come convenzione negli esercizi che vedremo e a lezione, adotteremo che un puntatore a una variabile di nome nome verr chiamato p_nome. Questo non assolutamente richiesto dal C; per il compilatore i puntatori possono chiamarsi in qualsiasi modo (rispettando comunque le regole del linguaggio). La dichiarazione di un puntatore ha il formato seguente: tipovariabile *numepuntatore;

tipovariabile un qualsiasi tipo di variabile riconosciuto dal C e indica il tipo della variabile cui punta il puntatore. Lasterisco (*) detto operatore di rinvio e indica che nomepuntatore un puntatore di tipo tipovariabile. I puntatori possono essere dichiarati assieme alle altre variabili. Ecco alcuni esempi: char *ch1, *ch2; /*ch1 e ch2 sono entrambi puntatori al tipo char float *valore, percento; /* valore un puntatore al tipo float, e percento una normale variabile float*/ Il simbolo * viene utilizzato sia come operatore di rinvio sia come operatore aritmetico per la moltiplicazione. Non c da preoccuparsi che il compilatore si confonda, poich il contesto in cui si trova il simbolo fornisce abbastanza informazioni da distinguere i due diversi tipi di utilizzo.

13.1.4

Inizializzazione dei puntatori

I puntatori sono inutili finch non li si fa puntare a qualche cosa. Come per le variabli normali, possibile utilizzare puntatori non inizializzati ottenendo risultati imprevedibili e potenzialmente disastrosi. Gli indirizzi non vengono inseriti nei puntatori per magia, il programma che si deve occupare di inserirveli utilizzando questa volta loperatore di indirizzo di e commerciale (&). Questo viene posto prima del nome di una variabile, questo operatore ne restituisce lindirizzo. Perci possibile inizializzare un puntatore con unistruzione del tipo: puntatore = &variabile; Se torniamo allesempio di prima della variabile rate; listruzione che inizializzava la variabile p_rate in modo che punti a rate potrebbe essere scritta come: p_rate = &rate;

13.1.5

Uso dei puntatori

Loperatore di rinvio (*) torna in azione quando questo precede il nome di un puntatore, in questo caso il programma di riferisce alla variabile puntata. Si prenda lesempio precedete, in cui il puntatore p_rate stato impostato in modo da puntare alla variabile rate. Scrivendo *p_rate ci si riferisce alla variabile rate. Se si vuole stampare il valore contenuto in rate (che nellesempio era 100) si ha la possibilit di scrivere: printf(%d, rate);

Pagina 3 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

oppure printf(%d, *p_rate); In C queste due istruzioni sono equivalenti. Laccesso al contenuto di una variabile utilizzando il suo nome si chiama accesso diretto, mentre quando si utilizza un puntatore si parla di accesso indiretto o rinvio. 1001 1004 1002 1003 1004 100 *p_rate 1005

p_rate

rate

Se si ha un puntatore di nome ptr inizializzato in modo da puntare alla variabile var, le frasi seguenti sono vere:

*ptr e var si riferiscono entrambe al contenuto do var (cio a qualsiasi valore il programma abbia memorizzato in quella locazione) ptr e &var si riferiscono entrambe allindirizzo di var

Un puntatore non preceduto dalloperatore di rinvio viene valutato per il suo contenuto, che logicamente lindirizzo della variabile puntata. Vediamo un esempio di un programma: #include <stdio.h> /*dichiara e inizializza una variabile di tipo int*/ int var = 1; /*dichiara un puntatore a int*/ int *ptr; main() { /*inizializza ptr in modo che punti a var*/ ptr = &var; /*si accede a var in modo diretto e indiretto*/ printf(\nAccesso diretto, var = %d,var); printf(\nAccesso indiretto, var = %d,*ptr); /*mostriamo lindirizzo di var in due modi*/ printf(\n\nIndirizzo di var = %d,&var); printf(\nIndirizzo di var = %d,ptr); return 0; }

Ecco loutput del programma : Accesso diretto, var = 1 Accesso indiretto, var = 1 Indirizzo di var = 4264228 Indirizzo di var = 4264228

Pagina 4 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

E molto probabile che sul sistema del lettore lindirizzo di var non sia 4264228!!

13.1.6

Puntatori e tipi di variabili

La trattazione di prima ha ignorato volutamente il fatto che diversi tipi di variabili occupano diversi quantitativi di memoria. Nella maggior parte dei sistemi operativi per PC, un int occupa 2 byte, un float 4 byte e cos via. Ogni singolo byte di memoria ha un proprio indirizzo, per cui una variabile che impiega pi byte occupa diversi indirizzi. Come si comportano i puntatori nel caso di variabili di pi byte? Lindirizzo di una variabile lindirizzo del primo (o pi basso) byte che essa occupa. Questo pu essere illustrato con un esempio; si supponga di dichiarare e inizializzare le tre variabili seguenti: int vint = 12252; char vchar = a; /*ovviamente in memoria troveremo il codice ASCII del carattere*/ float vfloat = 1200.156004; Queste variabili vengono memorizzate come nella figura riportata sotto: la variabile int occupa 2 byte, la char ne occupa uno solo e la float quattro. vint
1000 1001 1002

vchar
1003 1004 1005 1006

vfloat
1007 1008 1009 1010 1011

12252

97

1200.156004

Ora dichiariamo e inizializziamo i puntatori a queste variabili: int *p_vint; char *p_vchar; float *p_vfloat; p_vint = &vint; p_vchar = &vchar; p_vfloat = &vfloat; Ogni puntatore contiene lindirizzo del primo byte della variabile puntata, perci p_vint vale 1000, p_char vale 1003 e p_float vale 1006. Si ricordi per che ogni puntatore stato dichiarato in modo da puntare un certo tipo di variabile. Il compilatore sa che un puntatore a un int contiene lindirizzo del primo di due byte, che un puntatore a un float contiene lindirizzo del rimo di quattro byte e cos via. vint
1000 1001 1002

vchar
1003 1004 1005 1006

vfloat
1007 1008 1009 1010 1011

12252

97

1200.156004

p_vint
(2 byte a partire da 1000)

p_char
(1 byte a partire da 1003)

p_float
(4 byte a partire da 1006)

Pagina 5 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

13.1.7

Puntatori e array

I puntatori possono essere molto utili quando si lavora con variabili scalari, ma sono di sicuro pi utili con gli array. C una relazione particolare tra puntatori e array in C, infatti quando si utilizza la notazione con gli indici degli array tra parentesi quadre si stanno utilizzando dei puntatori senza neanche saperlo. Il nome di un array senza parentesi quadre un puntatore al primo elemento dellarray. Perci se si dichiarato un array di nome data[], data lindirizzo del primo elemento dellarray. Ma non si era detto che per avere un indirizzo necessario loperatore &? In effetti possibile utilizzare anche lespressione &data[0] per ottenere lindirizzo del primo elemento dellarray. In C la relazione (data == &data[0]) sempre vera. In pratica il nome di un array non altro che un puntatore allarray stesso. Si tenga per presente che viene visto come una costante: non pu essere modificato e rimane fisso per tutta la durata del programma. E tuttavia possibile dichiarare un altro puntatore e d inizializzarlo in maniera che punti allarray. Ad esempio il codice seguente inizializza il puntatore p_array allindirizzo del primo elemento dellarray: int array[1000], *p_array; p_array = array; Dato che p_array un puntatore variabile, pu essere modificato e pu puntare ovunque. A differenza di array, p_array non costretto a puntare al primo elemento di array[].

13.1.8

Aritmetica dei puntatori

Quando si ha un puntatore al primo elemento di un array, questo deve essere incrementato del numero di byte necessario per il tipo di dato contenuto nellarray. Per fare questo tipo di operazione viene utilizzata laritmetica dei puntatori. Incrementando un puntatore, si incrementa il suo indice. Ad esempio, quando si incrementa un puntatore di un unit, laritmetica dei puntatori incrementa automaticamente lindirizzo contenuto nel puntatore in modo che punti allelemento successivo dellarray considerato. In altre parole il C conosce (dalla dichiarazione) il tipo di dato puntato dal puntatore e incrementa lindirizzo in base alla sua dimensione. Si supponga che ptr_to_int sia un puntatore a un elemento di un array di dtipo int, Con listruzione: ptr_to_int++; il valore di ptr_to_int viene incrementato della dimensione del tipo int, generalmente 2 byte, per cui ptr_to_int nel momento immediatamente successivo allistruzione punta allelemento seguente. Aggiungendo n ad un puntatore, il C incrementa lindirizzo contenuto al suo interno del numero di byte necessari per puntare allelemento dellarray che segue di n posizioni. Perci: ptr_to_int += 4; incrementa il valore contenuto in ptr_to_int di 8 (sempre supponendo che un int sia lungo 2 byte). Gli stessi concetti appenda descritti per lincremento valgono anche per il decremento di puntatori, che equivale allaggiunta di un valore negativo e, in quanto tale, ricade nellambito degli incrementi. Vediamo un esempio di utilizzo dellaritmetica dei puntatori per accedere agli elementi di un array:

#include <stdio.h> #define MAX 10 /*dichiaro e inizializzo un array di interi*/

Pagina 6 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


int i_array[MAX] = {0,1,2,3,4,5,6,7,8,9}; /*dichiaro un puntatore a int e una variabile int*/ int *i_ptr, count; /*dichiaro e inizializzo un puntatore a float*/

Corso di Programmazione A.A. 2011-12

Dispensa 13

float f_array[MAX] = {0.0,0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}; /*dichiaro un puntatore a float */ float *f_ptr; main() { /*inizializzo i puntatori*/ i_ptr = i_array; f_ptr = f_array; /*Stampo gli elementi dellarray*/ for (count = 0; count < MAX; count++) printf(%d\t%f\n,*i_ptr++,*f_ptr++) return 0; }

Ecco loutput del programma 0 1 2 3 4 5 6 7 8 9 0.000000 0.100000 0.200000 0.300000 0.400000 0.500000 0.600000 0.700000 0.800000 0.900000

Lultima operazione consentita dallaritmetica dei puntatori la cosiddetta differenziazione ovvero la sottrazione tra due puntatori. Se si hanno due puntatori che puntano a due differenti elementi dello stesso array, possibile sottrarli per sapere quanto distano luno dallaltro. Ancora un avolta laritmetica dei puntatori adatta la risposta in maniera automatica in modo da riferirsi agli elementi dellarray. Quindi se ptr1 e ptr2 puntano a due elementi dello stesso array (di qualsiasi tipo), lespressione seguente fornisce la distanza tra gli e elementi stessi: ptr1 ptr2; I confronti tra i puntatori sono validi solo quando tutti puntano allo stesso array. Sono queste condizioni, gli operatori relazionali ==, !=, <, >, >=, <= funzionano correttamente. Gli elementi inferiori degli array (cio quelli con indice minore) hanno sempre indirizzi inferiori; perci se ptr1 e ptr2 puntano a elementi dello stesso array il confronto: ptr1 < ptr2 vero se ptr1 punta a un elemento dellarray che precede quello puntato da ptr2.

Pagina 7 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

Molte operazioni aritmetiche tra variabili normali non avrebbero senso con i puntatori e il compilatore non li permette. Ad esempio se ptr un puntatore listruzione seguente: ptr *= 2; genera un messaggio di errore.

13.1.9

Precauzioni per i puntatori

Nella scrittura di un programma che impiega i puntatori necessario evitare un errore molto serio: lutilizzo di un puntatore non inizializzato sul lato sinistro di unistruzione di assegnamento. Ad esempio listruzione che segue dichiara un puntatore di tipo int: int *ptr; Questo puntatore non ancora inizializzato, quindi non punta a nulla. Per essere precisi non punta a nulla di conosciuto. Un puntatore non inizializzato punta a un certo valore che tuttavia non dato a conoscere a priori. In molti casi questo valore zero. Se si utilizza un puntatore non inizializzato in unistruzionedi assegnamento, ad esempio: *ptr = 12; il valore 12 viene assegnato alla locazione di memoria puntata da ptr, qualunque essa sia. Questo indirizzo pu essere in qualsiasi punto della memoria, nella parte relativa al sistema operativo o dove stato caricato lo stesso programma. Il valore 12 pu aver sovrascritto qualche dato importante e pu provocare strani errori o blocchi della macchina. Il lato sinistro di unistruzione il posto pi pericoloso dove utilizzare puntatori non inizializzati. Quindi assicurarsi sempre che i puntatori siano inizializzati prima di utilizzarli.

13.2

Stringhe e puntatori

Nelle lezioni precedenti si spiegato che una stringa viene definita dal nome dellarray di caratteri che la contiene e da un carattere null. Il nome dellarray rappresenta un puntatore di tipo char allinizio della stringa. Il carattere null segna la fine della stringa. Lo spazio reale occupato dalla stringa nellarray casuale. Infatti lunica funzione dellarray quella di fornire uno spazio in cui collocare la strigna. Che cosa accade se si riesce a disporre di uno spazio di memoria senza allocare un array? Si potrebbe memorizzare l una stringa con il suo carattere null finale. Per specificare linizio della strigna sarebbe sufficiente un puntatore, come se la stringa stessa si trovasse in un array. Come si pu ottenere dello spazio utilizzabile allinterno della memoria? Esistono due modi: uno prevede lallocazione di spazio per una stringa letterale in fase di compilazione del programma e laltro utilizza la funzione malloc() per allocare dello spazio in famedi esecuzione del programma, processo noto come allocazione dinamica.

13.2.1

Allocazione di memoria per le stringhe

Linizio di una stringa, come si detto precedentemente, definito da un puntatore a una variabile di tipo char. Di seguito viene mostrato come dichiarare un puntatore: char *messaggio;

Pagina 8 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13

Tramite questa istruzione viene dichiarato un puntatore a una cariabile di tipo char di nome messaggio. Per il momento non punta a nulla, ma se si modifica la dichiarazione del puntatore nel modo seguente: char *messaggio = Evviva!; La stringa Evviva! (con il carattere null finale) viene memorizzata in una porzione di memoria e il puntatore messaggio viene inizializzato in modo che punti al primo carattere della stringa. Non ha importanza dove venga memorizzata la stringa, questo aspetto viene gestito automaticamente dal compilatore. Una volta che stato definito, messaggio un puntatore alla stringa e come tale pu essere riutilizzato. La dichiarazione/inizializzazione precedente equivalente a quella che segue e le due notazioni messaggio e messaggio[] sono tra loro intercambiabili; entrambi significano puntatore a. char messaggio[] = Evviva!; Questo modo di allocare lo spazio per le stringhe adatto nel momento in cui il programmatore sa esattamente di che cosa ha bisogno gi in fase di stesura del programma. Altrimenti se le esigenze variano in base allinput dellutente bisogna utilizzare la funzione malloc() che consente di allocare dello spazio in memoria al volo. La funzione malloc()

malloc() una delle funzioni per lallocazione di memoria del linguaggio C. Quando viene richiamata, passandole come parametro il numero di byte di memoria necessari, malloc() trova e riserva un blocco di memoria della dimensione richiesta e restituisce lindirizzo del primo byte del blocco. Il programmatore non si deve preoccupare della posizione in cui sia stato recuperato il blocco di memoria, poich questo aspetto viene gestito automaticamente.
La funzione malloc() restituisce un puntatore a void in quanto il tipo void compatibile con tutti i tipi di dati. Poich la memoria riservata da malloc() pu essere utilizzata per memorizzare qualsiasi tipo di dato previsto dal linguaggio C, il tipo void il pi appropriato. La sintassi della funzione malloc() la seguente: void *malloc(dim_t dim) ;

malloc() alloca un blocco di memoria corrispondente al numero di byte definito da dim. malloc() consente di riservare la memoria nel momento in cui se ne presenta la necessita, anzich bloccarla tutta contemporaneamente quando il programma viene avviato.
Per utilizzare malloc() necessario includere il file dintestazione stdlib.h. Se la funzione non riesce a riservare la quantit di memoria richiesta restituisce un valore nullo. Ogni volta che si prova ad allocare della memoria opportuno controllare il valore restituito dalla funzione, anche se la quantit di memoria da riservare limitata. Esempio 1

#include <stdlib.h> #include <stdio.h> main() { /*allocazione di memoria per una stringa di 100 caratteri*/ char *str; str = (char *) malloc(100); if (str == NULL) { printf(Memoria insufficiente\n); exit(1);

Pagina 9 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


} printf(Operazione riuscita!\n); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 13

Esempio 2 /*allocazione di memoria per un array di 50 interi*/ int *numeri; numeri = (int *) malloc (50 * sizeof(int));

Esempio 3 /*allocazione di memoria per un array di 50 valori float*/ float *numeri; numeri = (float *) malloc (50 * sizeof(float));

Esempio 4

#include <stdlib.h> #include <stdio.h> main() { char conta, *ptr, p; /*alloca un blocco di 3 byte. Controlla se ha avuto successo*/ /*la funzione di libreria exit() termina il programma*/ ptr = (char *) malloc(35); if (ptr == NULL) { printf(Memoria insufficiente\n); exit(1); } /*Riempie la stringa con valori da 65 a 90*/ /*che sono i codici ASCII delle lettere dalla A alla Z*/ /*p un puntatore utilizzato per spostarsi lungo la stringa*/ /*Vogliamo che ptr resti nella posizione che indica*/ /*linizio della stringa*/ p = ptr; for (conta = 65; conta < 91; conta++) *p++ = conta;

Pagina 10 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


/*aggiungo il carattere null finale*/ *p = \0; /*visualizza la stringa sullo schermo*/ puts(ptr); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 13

Ecco loutput del programma: ABCDEFGHIJKLMNOPQRSTUVWXYZ

La funzione free() La memoria allocata mediante malloc() presa da un serbatoio a disposizione del programma. Il serbatoio talvolta detto heap (lett. mucchio) a dimensione dinita. Quando un programma finisce di utilizzare un blocco di memoria allocato dinamicamente esso dovrevve liberarlo, cos da rimetterlo a disposizione per eventuali impieghi futuri. Per liberare in blocco di memoria allocato dinamicamente, si richiama la funzione free(). Il suo prototipo il seguente: void free(void *ptr); La funzione free() rilascia il blocco di memoria al quale punta ptr. Questo blocco devessere stato allocato mediante malloc(). Se ptr NULL, free() non ha alcun effetto.

Pagina 11 di 11

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 13 bis
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

Esempi utilizzo puntatori


Esempio 1 #include <stdio.h> #include <stdlib.h> int main() { int A, B; int *ptr; A = 10; B = A; ptr = &A; printf("Prima di modificare A:\n"); printf("A = %d \n",A); printf("B = %d \n",B); printf("*ptr = %d \n",*ptr); A = 35; printf("Dopo la modifica di A:\n"); printf("A = %d \n",A); printf("B = %d \n",B); printf("*ptr = %d \n",*ptr); *ptr = 17; printf("Dopo la modifica di *ptr:\n"); printf("A = %d \n",A); printf("B = %d \n",B); printf("*ptr = %d \n",*ptr); return 0; } Output del programma: Prima di modificare A: A = 10 B = 10 *ptr = 10 Dopo la modifica di A: A = 35 B = 10 *ptr = 35 Dopo la modifica di *ptr: A = 17 B = 10 *ptr = 17 Scrivere listruzione B = A significa associare alla variabile B lo stesso valore della variabile A, quindi creare una copia del valore presente in A e salvarlo allinterno della variabile B. Tutte le modifiche fatte alla variabile

Pagina 2 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

A non verranno sentite dalla variabile B la quale manterr il valore invariato e viceversa. Scrivere ptr = &A significa associare al puntatore ptr lindirizzo di memoria della variabile A, quindi attraverso il puntatore possiamo accedere allarea di memoria in cui memorizzata la variabile A e modificarne il valore. In questo secondo caso non si creano copie. Esempio 2 #include <stdio.h> #include <stdlib.h> int main() { int A; int *ptr; A = 17; ptr = &A; printf("%d \n",A); //valore presente dentro alla variabile A printf("%d \n",&A); //indirizzo di memoria della variabile A printf("%d \n",ptr); //contenuto della variabile ptr, (indirizzo di A) printf("%d \n",*ptr); /*valore presente allinterno della cella con indirizzo in ptr, quindi il valore di A*/ printf("%d \n",&ptr); //indirizzo di memoria della variabile ptr return 0; } Considerando il seguente stato della memoria (supponendo che un intero occupi 2 byte in memoria): 2006
2000 2001 2002 2003 2004 2005 2006

10
2007 2008 2009

ptr *ptr
Loutput del programma : 10 2006 2006 10 2001 Esempio 3 #include <stdio.h> #include <stdlib.h> int main() {

Pagina 3 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


int elenco[5] = {3,14,7,89,10}; printf("%d \n",elenco); //indirizzo di memoria della prima cella printf("%d \n",elenco[0]); //valore della prima cella, quindi 3 printf("%d \n",*elenco); //valore della prima cella, quindi 3 return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

Il nome di un array senza parentesi quadre corrisponde al puntatore alla prima cella di memoria allocata per memorizzare larray stesso. Quindi considerando lesempio riportato sopra, stampando il valore di elenco (senza le parentesi quadre) si ottiene lindirizzo di memoria della prima cella allocata, stampando invece il valore di *elenco si ottiene lo stesso valore stampato con elenco[0], quindi utilizzando laritmetica dei puntatori possiamo accedere alle varie celle dellarray: #include <stdio.h> #include <stdlib.h> int main() { int elenco[5] = {3,14,7,89,10}; int i; for (i = 0; i < 5 i++) printf("\t%d \t%d\n",elenco[i],*(elenco+i)); return 0; } Questo possibile in quanto le celle riservate in memoria per un array sono tutte consecutive. Lo stesso discorso potrebbe essere fatto per una matrice o array multidimensionale: #include <stdio.h> #include <stdlib.h> int main() { int matrice[3][2] = {1,2,3,4,5,6}; int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 2; j++) printf("\t%d\t %d\n",matrice[i][j],*(*(matrice+i)+j)); return 0; }

Esempio 4 #include <stdio.h> #include <stdlib.h>

Pagina 4 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


int main() { int elenco[5] = {1,1,1,1,1}; int valori[5] = {2,2,2,2,2}; int numeri[5] = {3,3,3,3,3}; int i; int *ptr; printf("Prima di modificare:\n"); for (i = 0; i < 5; i++) printf("\t%d \t%d \t%d",elenco[i],valori[i],numeri[i]); ptr = valori; for (i = 0; i < 5; i++) *(ptr + i) = i*2; printf("Dopo la modifica:\n"); for (i = 0; i < 5; i++) printf("\t%d \t%d \t%d",elenco[i],valori[i],numeri[i]); return 0; } Output del programma: Prima di modificare: 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 Dopo la modifica: 1 0 3 1 2 3 1 4 3 1 6 3 1 8 3 Attraverso ptr andiamo a modificare i valori del secondo vettore, il blocco di istruzioni: for (i = 0; i < 5; i++) *(ptr + i) = i*2; funziona per qualsiasi vettore associato a ptr

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

Pagina 5 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

Esempi allocazione dinamica della memoria


Esempio 1 #include <stdio.h> #include <stdlib.h> int main() { int numeroValori, i; int *ptr; printf("Quanti valori vuoi utilizzare:"); scanf("%d",&numeroValori); ptr = (int *)malloc(numeroValori*sizeof(int)); if (ptr == NULL) printf("Errore nell'allocare la memoria!"); else { for (i = 0; i < numeroValori; i++) { printf("Inserisci valore %d:",i + 1); scanf("%d",(ptr + i)); } printf("\nValori inseriti:"); for (i = 0; i < numeroValori; i++) printf("\n%d",*(ptr + i)); } free(ptr); return 0; }

Esempio 2 #include <stdio.h> #include <stdlib.h> #define DIMENSIONE_BUFFER 10 int main() { int dimStringa, i; unsigned char *stringa, *temp, *buffer; unsigned char ch; buffer=(unsigned char *)malloc(DIMENSIONE_BUFFER*sizeof(unsigned char)); stringa = (unsigned char *)malloc(sizeof(unsigned char)); dimStringa = 1; //incluso carattere \0 finale

Pagina 6 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


printf("Inserire testo:\n"); do { i = 0; do { ch = getchar(); *(buffer+i) = ch; i++; } while (ch != '\n' && (i < DIMENSIONE_BUFFER-1));

Corso di Programmazione A.A. 2011-12

Dispensa 13 bis

dimStringa += i; temp = stringa; stringa = (char *)malloc(dimStringa*sizeof(unsigned char)); *stringa = '\0'; strcat(stringa,temp); strcat(stringa,buffer); free(temp); } while(ch != '\n'); printf("\n\nTesto inserito: %s",stringa); free(stringa); free(buffer); return 0; }

Pagina 7 di 7

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 14
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 14

14.1

Le strutture

Una struttura rappresenta linsieme di una o pi variabili raggruppate sotto un unico nome per una pi facile gestione. Le variabili contenute in una struttura, contrariamente a quelle di un array, possono essere di tipi differenti. Una struttura pu contenere qualsiasi tipo di dato ammesso dal C, inclusi gli array e altre strutture. Le variabili contenute in una struttura vengono chiamate elementi.

14.1.1

Definizione e dichiarazione di strutture

Il codice di un programma di grafica deve gestire le coordinate dei vari punti sullo schermo, rappresentate da un valore x, la posizione orizzontale, e da un valore y, quella verticale. E possibile definire una struttura di nome coord che contenga sia il valore x che y di un punto sullo schermo, nel modo seguente: struct coord { int x; int y; }; La parola chiave struct, che identifica linizio della definizione di struttura, deve essere immediatamente seguita dal nome della struttura stessa, o etichetta (che segue le stesse regole degli altri nomi di variabili del C). Tra le parentesi graffe che seguono il nome della struttura sono elencate le variabili da inserire nella struttura stessa. E necessario assegnare a ciascun elemento un nome e un tipo. Le istruzioni precedenti definiscono un tipo di struttura di nome coord che contiene due variabili di tipo intero, x e y. In realt, in questo modo non viene creata alcuna istanza della struttura coord; in altre parole le istruzioni precedenti non dichiarano (assegnando lo spazio in memoria) alcuna struttura. Esistono due modi per dichiarare una struttura: uno consiste nel far seguire la definizione da un elenco di uno o pi nomi di variabili, come viene mostrato di seguito: struct coord { int x; int y; }prima, seconda; Queste istruzioni definiscono il tipo di struttura coord e dichiarano due strutture prima e seconda, di tipo coord. Prima e seconda sono istanze del tipo coord; prima contiene due membri interi di nome x e y e cos pure seconda. Questo metodo di dichiarazione delle strutture combina la dichiarazione con la definizione. Il secondo metodo consiste nel dichiarare le variabili istanza della struttura in un punto differente del codice, separato dalla definizione della struttura stessa. Anche le istruzioni seguenti dichiarano due istanze del tipo coord: struct coord { int x; int y; }; struct coord prima, secnoda;

14.1.2

Accesso agli elementi di una struttura

Ciascun elemento di una struttura pu essere utilizzato come qualsiasi altra variabile del tipo assegnato. Per accedere ai singoli componenti di una struttura, si utilizza loperatore di elemento della struttura (.), anche chiamato operatore punto, inserendolo tra il nome della struttura e il nome del componente. Quindi se si ha una struttura di nome prima con i riferimenti a un punto dello schermo di coordinate x=50, y=100, si pu scrivere:

Pagina 2 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


prima.x = 50; prima.y = 100;

Corso di Programmazione A.A. 2011-12

Dispensa 14

Di seguito mostrato come visualizzare le posizioni dello schermo memorizzate nella struttura prima: printf(%d, %d, prima.x, prima.y); Uno dei vantaggi principali nellutilizzare strutture consiste nel fatto che si possono copiare le informazioni tra strutture dello stesso tipo utilizzando una semplice istruzione di eguaglianza. Proseguendo nellesempio precedente, listruzione: prima = seconda; equivale a: prima.x = seconda.x; prima.y = seconda.y; In generale le strutture risultano utili ogni volta che si devono gestire gruppi di informazioni contenuti in tipi di variabili differenti.

14.1.3

Strutture che contengono altre strutture

Come si gi detto in precedenza, una struttura del C pu contenere qualsiasi tipo di dato ammesso dal linguaggio, quindi anche altre strutture. Quindi possibile definire una struttura come segue (supponendo ovviamente che sia gi stato definito il tipo di struttura coord): struct rettangolo{ struct coord supsin; struct coord infdes; }; Questa istruzione definisce una struttura di tipo rettangolo che contiene a sua colta due strutture di tipo coord. Queste due strutture di tipo coord sono chiamate supsin e infdes. Listruzione precedente definisce soltanto la struttura di tipo rettangolo. Per dichiarare una struttura, necessario specificare unistruzione del tipo: struct rettangolo riq; Si possono combinare tra loro la definizione e la dichiarazione, come si gi visto precedentemente per il tipo coord: struct rettangolo{ struct coord supsin; struct coord infdes; } riq; Per accedere alle locazioni reali dei dati (gli elementi di tipo int), sufficiente applicare due volte loperatore punto (.). Quindi, lespressione: riq.supsin.x si definisce al componente x del membro supsin della struttura di tipo rettangolo di nome riq. Per definire un rettangolo di coordinate (0,10), (100,200), si pu scrivere: riq.supsin.x riq.supsin.y riq.infdes.x riq.infdes.y = = = = 0; 10; 100; 200;

Vediamo ora un esempio di utilizzo di strutture che contengono altre strutture:

Pagina 3 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 14

Riceve in input le coordinate angolari di un rettangolo e ne calcola larea. Si suppone che la coordinata y dellangolo superiore sinistro sia maggiore della coordinata y dellangolo inferiore destro, che la coordinata x dellangolo inferiore destro sia maggiore della coordinata x dellangolo superiore sinistro e che tutte le coordinate siano positive. #include <stdio.h> struct coord{ int x; int y; }; struct rettangolo{ struct coord supsin; struct coord infdes; }; struct rettangolo riq; main() { int lung, larg; long area ; printf(\nInserisci la coordinata x superiore sinistra:); scanf(%d,&riq.supsin.x); printf(\nInserisci la coordinata y superiore sinistra:); scanf(%d,&riq.supsin.y); printf(\nInserisci la coordinata x inferiore destra:); scanf(%d,&riq.infdes.x); printf(\nInserisci la coordinata y inferiore destra:); scanf(%d,&riq.infdes.y); /*calcola la lunghezza e la larghezza*/ larg = riq.infdes.x riq.supsin.x; lung = riq.infdes.y riq.supsin.y; /*Calcola e visualizza larea*/ area = lung*larg; printf(\nLarea : %d, area); return 0; } Ecco input e output del programma: Inserisci la Inserisci la Inserisci la Inserisci la Larea 81 coordinata coordinata coordinata coordinata x y x y superiore superiore inferiore inferiore sinistra: 1 sinistra: 1 destra: 10 destra: 10

Pagina 4 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 14

14.1.4

Strutture che contengono altre strutture

E possibile definire una struttura che abbia come componenti uno o pi array. Larray pu essere di qualsiasi tipo di dato ammesso in C. Ad esempio la definizione: struct dati{ int x[4]; char y[10]; }; definisce una struttura di tipo dati che contiene un array x di quattro elementi interi e un array y di 10 caratteri. Successivamente possibile dichiarare una struttura record di tipo dati nel modo seguente: struct dati record; Per accedere a ciascun elemento dellarray contenuto nella struttura si utilizza una combinazione delloperatore punto con gli indici dellarray: record.x[2] = 100; record.y[1] = x;

14.1.5

Array di strutture

E possibile definire un array di strutture, ad esempio in un programma per la gestione di un elenco di numeri telefonici, possibile definire una struttura che contenga il nome e il numero dellutente: struct utente{ char nome[20]; char cognome[30]; char telefono[25]; }; Un elenco telefonico costituito per da molte voci, perci un'unica istanza di questa struttura non di molta utilit. Quello che serve un array di strutture di tipo utente. Dopo aver definito la struttura, si dichiara un array come segue: struct utente elenco[1000]; Questa istruzione dichiara un array elenco di 1000 elementi. Ciascun elemento rappresenta una struttura di tipo utente ed identificato da un indice.

14.1.6

Puntatori a strutture

E possibile dichiarare puntatori a strutture, per prima cosa occorre definire una struttura: struct parte{ int numero; char nome[10]; }; A questo punto, si dichiara un puntatore al tipo parte. struct parte *p_parte; Si rammenti che lopeartore di rinvio (*) allinterno della dichiarazione indica che p_parte un puntatore al tipo parte, non unistanza della struttura di tipo parte. Si ricordi che la dichiarazione non la definizione che predispone lo spazio in memoria per la memorizzazione delloggetto di dati. Poich un puntatore necessita di un indirizzo di memoria a cui puntare, necessario dichiarare unistanza di tipo parte alla quale possa essere indirizzato il puntatore. Di seguito riportata la dichiarazione: struct parte gz;

Pagina 5 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


A questo punto possibile inizializzare il puntatore: p_parte = &gz;

Corso di Programmazione A.A. 2011-12

Dispensa 14

Questa istruzione assegna lindirizzo di gz a p_parte. Ora che si dispone di un puntatore alla struttura gz, si pu utilizzare loperatore di rinvio (*). Applicando questo concetto allesempio corrente, poich p_parte rappresenta un puntatore alla struttura gz, *p_parte si riferisce a gz. Si pu applicare loperatore (.) per accedere ai singoli componenti di gz. Per assegnare il valore 100 a gz.numero, si pu scrivere: (*p_parte).numero = 100;

*p_parte deve essere racchiuso tra parentesi perch loperatore (.) ha la precedenza rispetto loperatore (*).
Un altro modo per accedere agli elementi di una struttura tramite un puntatore a struttura consiste nellutilizzare loperatore puntatore di membro indiretto, rappresentato dai caratteri -> (un tratino seguito dal simbolo maffiore). Quindi lesempio di prima lo si poteva scrivere: p_parte->numero = 100; Altri esempi, se p_str un puntatore alla struttura str, le espressioni seguenti si equivalgono: str.elem (*p_str).elem _str->elem

Pagina 6 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Esercizio 1 - Studenti/voti: materie insufficienti

Corso di Programmazione A.A. 2011-12

Dispensa 14

Utilizzando un array di strutture, si vogliono memorizzare i seguenti dati relativi agli studenti: Nome, Cognome, Matricola, Anno di corso, Elenco dei voti dove lelenco dei voti a sua volta memorizzato allinterno di un vettore. Si utilizza, poi, un ulteriore vettore di strutture per memorizzare le informazioni relative alle materie: codice materia, descrizione e docente. Lindice in cui sono memorizzati i voti allinterno dellarray di voti corrisponde alla posizione in cui sono memorizzate le materie nel rispettivo vettore delle materie. Sia data la matricola oppure il cognome di uno studente. Calcolare la sua media e visualizzare le materie in cui risulta insufficiente, il docente della materia e il voto corrispondente.

Pagina 7 di 7

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 15
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15

15.1

Le funzioni

Una funzione una sezione con nome indipendente di codice C che segue un compito specifico e restituisce opzionalmente un valore al programma chiamante. Analizziamo un esempio con di codice con una funzione:

1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:

/*Esempio di una semplice funzione*/ #include <stdio.h> long cubo(long x); long input, risp; main() { printf(Immettere un valore intero: ); scanf(%d, &input); /*Nota: %ld lindicatore di convers. per un intero lungo*/ risp = cubo(input); printf(\nIl cubo di %ld %ld.\n,input, risp); return 0; } /*funzione cubo() Calcola il valore al cubo della variabile*/ long cubo(long x) { long x_cubo; x_cubo = x * x * x; return x_cubo; }

Ecco loutput del programma: Esempio 1: Immettere un valore intero: 100 Il cubo di 100 1000000 Esempio 2: Immettere un valore intero: 9 Il cubo di 9 729 La riga 4 contiene il prototipo della funzione, il modello che apparir nella parte seguente del programma. Un prototipo contiene il nome della funzione, un elenco di variabili che verranno passate ed eventualmente il tipo della variabile restituita. Osservando la riga 4 si pu vedere che la funzione si chiama cubo, che richiede una variabile di tipo long e che restituisce un valore di tipo long. Le variabili passate ad una funzione sono dette argomenti e sono racchiuse tra le parentesi tonde che seguono il nome della funzione stessa. In questo esempio lunico argomento della funzione long x. La parola chiave che precede il nome della funzione indica il tipo di variabile restituito. In questo caso viene restituito una variabile di tipo long. La riga 13 richiama la funzione cubo e le passa il valore introdotto da tastiera come argomento. Il valore restituito dalla funzione viene assegnato alla variabile risp.

Pagina 2 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15

La definizione della funzione in questo caso contenuta nelle riche dalla 20 alla 26. Come il prototipo anche la definizione della funzione composta da pi parti. La funzione comincia con unintestazione alla riga 20, contenente il nome della funzione, il tipo e la descrizione degli eventuali argomenti. Si noti che listruzione della funzione identica al prototipo (a parte il punto e virgola). Il corpo della funzione, nelle righe dalla 21 alla 26. racchiuso allinterno di parentesi graffe e contiene istruzioni. Le variabili locali sono quelle dichiarate allinterno del corpo della funzione. Infine, la funzione termina con unistruzione return alla riga 25 che indica la conclusione della routine. Listruzione return serve anche per passare il valore restituito al programma chiamante. I programmi in C eseguono le istruzione contenute allinterno delle funzioni solo al momento in cui avviene la chiamata; in questo istante, il programma ha la possibilit di inviare delle informazioni alla funzione sotto forma di argomenti, per specificare i dati richiesti dalla procedura per eseguire il proprio compito. In seguito vengono eseguite le istruzioni interne fino alla conclusione della procedura, momento in cui il flusso di esecuzione torna al programma principale nellistruzione successiva alla chiamata. Le istruzioni possono passare al programma chiamante le informazioni elaborate attraverso una gestione particolare del valore restituito. Una funzione pu essere chiamata tutte le volte che serve; inoltre le funzioni cono richiamabili in qualsiasi ordine. Il prototipo di una funzione fornisce al compilatore unanteprima della funzione che verr definita in una parte seguente del codice. Esempi di prototipi: double quadrato(double numero); void stampa_relazione(int numero_relazione); int get_scelta_menu(void); Esempi di definizione: double quadrato(double numero) { return numero * numero; } /*intestazione*/ /*parentesi aperta*/ /*corpo della funzione*/ /*parentesi chiusa*/

void stampa_relazione(int numero_relazione) { if (numero_relazione == 1) printf(Stampo relazione 1); else printf(Non stampo relazione 1); } Attraverso limpiego delle funzioni allinterno dei propri programmi in C, possibile fare uso della programmazione strutturata, dove qualsiasi compito di un programma viene eseguito da una sezione indipendente.

15.1.1

Come si scrive una funzione

Il primo passo dello sviluppo di una funzione la definizione del suo obiettivo.

Pagina 3 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15

Intestazione: la prima riga di qualsiasi funzione lintestazione, la quale si compone di tre parti ciascuna asservita ad uno scopo preciso.
Nome della funzione Elenco dei parametri passati alla funzione nomefunzione (param1,...)

Tipo del valore restituito dalla funzione tipo

Tipo del valore restituito dalla funzione: corrisponde al tipo di dato che la funzione restituisce al programma chiamante. Nome della funzione: ad una funzione pu essere assegnato un nome qualsiasi, a patto che segua le regole del C riguardanti i nomi di variabile. Il nome della funzione deve essere unico. Lelenco dei parametri: molte funzioni utilizzano degli argomenti passati nella chiamata. Una funzione deve sapere che tipo di argomenti aspettarsi, cio il tipo di dato di ciascuno. E possibile passare a una funzione C qualsiasi tipo di dati. Per ciascun argomento passato alla funzione, lelenco dei parametri deve contenere un oggetto distinto.

Corpo della funzione: contenuto allinterno di una coppia di parentesi graffe e segue immediatamente lintestazione. Allinterno del corpo delle funzioni possibile dichiarare delle variabili locali, cos chiamate in quanto sono disponibili solo alla funzione di appartenenza e sono distinte dalle altre variabili con lo stesso nome dichiarate altrove. Per restituire un valore da una funzione si deve utilizzare la parola chiave return seguita da unespressione del C. Una funzione pu contenere pi di una istruzione return, solo la prima ad essere eseguita quella che conta.

#include <stdio.h> int maggiore(int a, int b); main() { int x, y, z; puts(Inserire due valori interi diversi); scanf(%d%d,&x, %y) ; z = maggiore(x,y); printf(\nIl valore maggiore %d.,z); return 0; } int maggiore(int a, int b) { if (a > b) return a;

Pagina 4 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


else return b; }

Corso di Programmazione A.A. 2011-12

Dispensa 15

I prototipi delle funzioni: i prototipi di una funzione sono identici alle intestazioni, ma sono seguiti da un punto e virgola. I prototipi forniscono al compilatore informazioni sul tipo di valore restituito, sul nome e sui parametri.

15.1.2

Passaggio degli argomenti

Per passare degli argomenti a una funzione necessario elencarli allinterno delle parentesi che seguono il nome della funzione stessa. Il numero, il tipo e lordine degli argomenti devono coincidere con quelli del prototipo e dellintestazione. Ad esempio, se una funzione viene definita con due parametri di tipo int, occorre passarle esattamente due argomenti int, ne uno di pi ne uno di meno, e del tipo corretto. Se si cerca di passare a una funzione un numero o un tipo sbagliato di argomenti, il compilatore se ne accorge sulla base delle informazioni del prototipo. Chiamata della funzione

funzione1 (a, b, c) ;

Intestazione della funzione

void

funzione1 (int x, int y, int z)

15.1.3

Chiamata alle funzioni

Esistono due modi di chiamare una funzione. Qualsiasi funzione pu essere richiamata specificandone il nome e lelenco di argomenti come un'unica istruzione. Ad esempio: areaRettangolo(5, 10); Se la funzione restituisce un valore, questo viene ignorato. Il secondo metodo pu essere utilizzato con le funzioni che restituiscono un valore. Dato che queste funzioni equivalgono a un valore (quello restituito), sono a tutti gli effetti delle espressioni del C e possono essere utilizzate ovunque possa essere impiegata unespressione. Nellesempio seguente, areaRettangolo() utilizzato come parametro di una funzione: printf(Larea del rettangolo di base %d e altezza %d : %d, b, h, areaRettangolo(b,h)); Viene chiamata per prima la funzione areaRettangolo() con i parametri b e h, quindi viene richiamata la funzione printf() utilizzando i valori di b, h w areaRettangolo().

Pagina 5 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15

15.1.4

Chiamata alle funzioni

Il termine ricorsione si riferisce alle situazioni in cui una funzione richiama se stessa in maniera sia diretta che indiretta. La ricorsione indiretta avviene quando una funzione ne richiama unaltra che al suo interno richiama la funzione primaria. Il programma presentato nellesempio sotto, presenta un esempio di chiamata ricorsiva. In particolare viene calcolato il fattoriale di un numero. Il fattoriale di un numero x, che ha per simbolo x! si calcola nel modo seguente: x! = x *(x - 1) * (x - 2) * (x - 3) * . . . * (2) * 1 E possibile calcolare x! anche nella maniera seguente: x! = x * (x - 1)! Avanzando di un altro passo, (x - 1)! Pu essere calcolato utilizzando la stessa procedura: (x - 1)! = (x - 1) * (x - 2)! #include <stdio.h> unsigned int fattoriale(unsigned int a); main() { unsigned int f, x; puts(inserire un valore intero compreso tra 1 e 8:); scanf(%d,&x); if (x > 8 || x < 1) printf(Solo I valori compresi tra 1 e 8 sono ammessi!!); else { f = fattoriale(x); printf(%u fattoriale valr %u,x,f); } return 0; } unsigned int fattoriale(unsigned int a) { if (a == 1) return 1; else { a *= fattoriale(a - 1); return a; } }

15.1.5

Ambito delle variabili

Lambito di una variabile si riferisce allestensione entro cui parti differenti di n programma hanno accesso a una determinata variabile; in altre parole, da dove la variabile risulta visibile. Quando si parla di variabili del C, i termini accessibilit e visibilit vengono utilizzati in modo intercambiabile. In riferimento allambito, con il

Pagina 6 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15

termine variabile si intende qualsiasi tipo di dato previsto dal linguaggio C; variabili scalari, array, strutture, puntatori e cos via, oltre che costanti simboliche definite tramite la parola chiave const. Inoltre, lambito influisce sul tempo di vita di una variabile: quanto tempo la variabile persiste nella memoria, oppure, quando viene allocato o deallocato dello spazio in memoria per la variabile stessa. Esempio di ambito: /*Versione 1 del listato*/ #include <stdio.h> intx = 999; void stampa_valore(void); main() { printf(%d\n,x); stampa_valore(); return 0; } void stampa_valore(void) { printf(%d\n,x); }

Ecco loutput del programma: 999 999 /*Versione 2 del listato*/ #include <stdio.h> void stampa_valore(void); main() { intx = 999; printf(%d\n,x); stampa_valore(); return 0; } void stampa_valore(void) { printf(%d\n,x); }

Pagina 7 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Se si prova a compilare il listato 2, viene generato un messaggio di errore del tipo: Error: undefined identifer x.

Corso di Programmazione A.A. 2011-12

Dispensa 15

Lunica differenza tra I due listati consiste nella posizione in cui la variabile x viene definite. Spostando la definizione di x in punti differenti del codice, se ne modifica lambito. Nel listato 1, x rappresenta una variabile esterna e il suo ambito si estende allintero programma. Nel listato 2, x rappresenta una variabile locale e come tale il suo ambito limitato allinterno della funfione main().

15.1.5.1

Variabili esterne

Una variabile esterna definita esternamente da qualsiasi funzione compresa la funzione main(). Le variabili esterne vengono anche definite come variabili globali. Se una variabile esterna no viene inizializzata al momento della definizione, il compilatore le inizializza automaticamente al valore 0. Lambito di una variabile esterna rappresentata dallintero programma. Ci significa che una variabile esterna visibile dalla funzione main() e da qualsiasi altra funzione allinterno del programma.

15.1.5.2

Variabili locali

Una variabile locale una variabile definita allinterno di una funzione. Lambito di una variabile locale risulta limitato alla funzione in cui definita. Le variabili locali non vengono automaticamente inizializzata ad alcun valore. Se al momento della definizione non vengono inizializzate, contengono un valore indefinito.

15.1.5.3

Variabili statiche e variabili automatiche

Le cariabili locali sono di default automatiche. Ci significa che vengono ricreate da zero ogni volta che viene richiamata la funzione in cui sono dichiarate e distrutte ogni volta che lesecuzione del programma abbandona quella funzione. Ai fini pratici, ci significa che una variabile automatica non mantiene il proprio calore nellintervallo di tempo che intercorre tra due chiamate successive alla funzione in cui definita. E possibile fare in modo che una variabile locale mantenga il proprio valore tra ciascuna chiamata alla funzione in cui definita, e per fare ci necessario definirla come statica utilizzando la parola chiave static. Ad esempio: void funzione(int x) { static int a; /*codice della funzione*/ } Esempio tra variabili locali statiche e automatiche: #include <stdio.h> void funzione1(void); main() { int conta; for (conta = 0; conta < 10; conta++) { printf(Alla iterazione %d: , conta); funzione1(); } return 0;

Pagina 8 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


} void funzione1() { static int x = 0; int y = 0; printf(x = %d, t = &d\n, x++, y++) ; }

Corso di Programmazione A.A. 2011-12

Dispensa 15

Ecco loutput del programma : Alla iterazione 0: x = 0, y = 0 Alla iterazione 1: x = 1, y = 0 Alla iterazione 2: x = 2, y = 0 Alla iterazione 3: x = 3, y = 0 Alla iterazione 4: x = 4, y = 0 Alla iterazione 5: x = 5, y = 0 Alla iterazione 6: x = 6, y = 0 Alla iterazione 7: x = 7, y = 0 Alla iterazione 8: x = 8, y = 0 Alla iterazione 9: x = 9, y = 0

15.1.5.4

Variabili di registro

La parola chiave register viene utilizzata per chiedere al compilatore di memorizzare una variabile locale automatica in un registro del processore, anzich in memoria. La parola chiave register una richiesta e non un ordine. La parola chiave _register pu essere utilizzata solo con variabile numeriche semplici e non possibile definire un puntatore alle variabili di registro.

15.1.6

Passaggio di variabili a funzioni

Il modo normale di passare un argomento a una funzione detto passaggio per valore. Con ci si intende che la funzione riceve una copia dellargomento.Quando una variabile passata per valore, la funzione pu accedere al valore della variabile, ma non alla variabile vera e propria. Di conseguenza, la funzione non pu modificare il valore della variabile originale. Il passaggio per valore ammesso per i tipi di dati base (char, int, long, float e double) e per le strutture. Esiste tuttavia un secondo modo di passare argomenti a una funzione: passare un puntatore alla variabile argomento, anzich il suo valore. Questo detto passaggio per riferimento. Le modifiche alle variabili passate per riferimento vengono applicate alla variabile di origine. #include <stdio.h> void per_valore(int a, int b, int c); void per_riferimento(int *a, int *b, int *c); main()

Pagina 9 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ int x = 2, y = 4, z = 6;

Corso di Programmazione A.A. 2011-12

Dispensa 15

printf( \nPrima della chiamata di per_valore(), x= %d, y = %d, z = %d.,x,y,z); per_valore(x,y,z); printf( \nDopo la chiamata di per_valore(), x= %d, y = %d, z = %d.,x,y,z); per_riferimento(&x, &y, &z); printf( \nDopo la chiamata di per_riferimento(), x= %d, y = %d, z = %d.,x,y,z); return 0; } void per_valore(int a, int b, int c) { a = 0; b = 0; c = 0; } void per_riferimento(int *a, int *b, int *c) { *a = 0; *b = 0; *c = 0; }

Pagina 10 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Esercizio 1. Passaggio per valore/per indirizzo

Corso di Programmazione A.A. 2011-12

Dispensa 15

Scrivere un programma che esegue lo swap di due variabili intere x e y. Prevedere due funzioni di swap, la prima (swap_val) che utilizzi un passaggio per valore e la seconda (swap_rif) che utilizzi, invece, un passaggio per indirizzo. Visualizzare il valore delle variabili x e y in seguito alla chiamata di ciascuna funzione e commentare il risultato

Esercizio 2. Ricerca di un pattern in una stringa Scrivere un programma che cerca le occorrenze di un pattern allinterno di un insieme di stringhe. Il programma prevede che lutente possa inserire pi stringhe; si considera la terminazione di una stringa con landata a capo (si legge cio una linea per volta). Linput termina quando viene inserita una stringa vuota (nessun carattere+invio). Si implementino e utilizzino due funzioni: int getline(char s[], int lim): legge una linea di input nel vettore s, fino a un massimo di lim caratteri, e ritorna la lunghezza della stringa int ricerca_pattern(char s[]) cerca il pattern dentro la stringa s e ritorna -1 se non ha trovato il pattern, un numero >=0 altrimenti. Per ogni input, il programma stampa il numero di occorrenze trovate fino a quel momento e, nel caso in cui sia stato trovato il pattern, anche la stringa.

Pagina 11 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Soluzione esercizio 1 #include<stdio.h> void swap_val(int,int); void swap_rif(int*,int*);

Corso di Programmazione A.A. 2011-12

Dispensa 15

main() { int x,y; printf("Inserisci x e y:\n"); scanf("%d %d",&x,&y); swap_val(x,y); printf("Dopo lo swap con passaggio per valore: x vale %d, y vale %d\n",x,y); swap_rif(&x,&y); printf("Dopo lo swap con passaggio per indirizzo: x vale %d, y vale %d\n",x,y); } void swap_val(int x, int y) // swap con passaggio per valore { int tmp; tmp = x; x = y; y = tmp; } // swap con passaggio per indirizzo void swap_rif(int *px, int *py) { int tmp; tmp = *px; *px = *py; *py = tmp; }

Soluzione esercizio 2 #include <stdio.h> #define LIM 100 int getline(char[], int); int ricerca_pattern(char[]); char pattern[] = "ando"; int trovato=0; // inizializzazione ridondante main() { // cerca le occorrenze di un pattern dentro una stringa char line[LIM]; printf("Inserisci il testo in cui ricercare il pattern ); printf(una linea per volta).\nStringa vuota per terminare.\n"); while (getline(line,LIM) > 0)

Pagina 12 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ if (ricerca_pattern(line) >= 0) printf("%s\n", line); printf("\n Finora ho trovato); printf(%d occorrenze di %s\n",trovato,pattern); } } int getline(char s[], int lim) { /* legge una linea di input nel vettore s, fino a un massimo di lim caratteri; ritorna la lunghezza della stringa */ int c, i = 0; while(lim > 1 && (c=getchar()) != '\n') { s[i] = c; ++i; --lim; } s[i] = '\0'; return i; } int ricerca_pattern(char s[]) { /* cerca la stringa pattern dentro s */ int i, j, k, ret=-1; for (i = 0; s[i] != '\0'; ++i) { for (j = i, k = 0; pattern[k] != '\0'; ++j, ++k) if (s[j] != pattern[k]) break; if (pattern[k] == '\0') { ++trovato; ret=i; } } return ret; }

Corso di Programmazione A.A. 2011-12

Dispensa 15

ESEMPI DI SCOPE

#include <stdio.h> int counter = 0; void f(void); void f2(void); /* variabile globale */

Pagina 13 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


int main() { ++counter; printf("counter is %2d before the call to f\n", counter); f(); printf("counter is %2d after the call to f\n", counter); f2(); return 0; } void f(void) { int counter = 10; } void f2(void) { printf("counter is %2d within f2\n", counter); }

Corso di Programmazione A.A. 2011-12

Dispensa 15

/* variabile locale

*/

printf("counter is %2d within f\n", counter);

Occorre sempre fare attenzione ai nomi e alla visibilit delle variabili: la variabile globale counter, definita allinizio del programma, visibile e modificabile in tutto il programma, eccetto per la funzione f, dove il nome counter usato per identificare una variabile locale. Nota bene: quello visto sopra non un buon esempio di programmazione, in quanto genera confusione per il lettore. #include <stdio.h> void f1(int); int f2(void); void f1(int i) { extern int pippo; pippo = pippo + i; } int f2() { static int pluto; // pluto inizializzato solo alla prima chiamata a f2 pluto = pluto + 1; return pluto; } int pippo; int main() { int j; int i = 2; printf("%d\n",pippo); f1(i); printf("%d\n",pippo);

Pagina 14 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


j = f2(); printf("%d\n",j); j = f2(); printf("%d\n",j); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 15

Lo scope di una variabile locale (automatica) la funzione dove stata definita. Lo scope di una variabile globale (esterna) va dal punto in cui essa definita al termine del file sorgente in cui si trova. Se necessario riferire una variabile esterna prima che essa sia stata definita, oppure se essa definita in un file sorgente diverso da quello in cui viene utilizzata, allora necessaria una dichiarazione di extern. La dichiarazione static, applicata ad una variabile esterna, ne limita lo scope al file sorgente nel quale essa si trova. La dichiarazione static, applicata ad una variabile non esterna, consente alla variabile di mantenere il proprio valore anche fra due chiamate successive. Le variabili esterne e static vengono inizializzate a zero, mentre le variabili automatiche hanno valori iniziali indefiniti.

Pagina 15 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 1
Step 1 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } main() variabili globali numero: 7 1 memoria ordine alloc. deal. Allavvio del programma la prima istruzione ad essere eseguita la dichiarazione della variabile numero con assegnamento del valore 7 in memoria viene allocato lo spazio necessario per mantenere la variabile numero (variabile locale della funzione main())

Pagina 2 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

La seconda istruzione prevede una chiamata ad una funzione stampa() Quando viene chiamata una funzione, lesecuzione del programma si blocca nel punto della chiamata, il controllo viene ceduto alla funzione chiamata In memoria vengono allocate tutte le variabili locali alla funzione incluse quelle indicate nei parametri

1 2

stampa() main() variabili globali

i: 7 numero: 7

2 1

3 4

I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile i (della funzione stampa()) avr una copia del valore della variabile numero (punto 3) Si procede poi con lesecuzione delle varie istruzioni presenti allinterno del corpo della funzione stampa() (punto 4) e quindi con la stampa a video del valore della variabile i, cio del valore 7.

Pagina 3 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando una funzione termina tutte le variabili locali vengono rimosse dalla memoria, quindi nella funzione stampa() presa in esame la variabile i viene rimossa dalla memoria. Il controllo viene ceduto al chiamante, quindi alla funzione main() ripartendo dallistruzione immediatamente dopo la chiamata (punto 5)

1 2 5

stampa() main() variabili globali

i: 7 numero: 7

2 1

3 4

Pagina 4 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede con il punto 6 e quindi con la chiamata della funzione incrementa() La funzione main() si blocca e il controllo viene ceduto alla funzione chiamata. In memoria vengono allocate tutte le variabili locali (punto 8) alla funzione incluse quelle indicate nei parametri I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile k (della funzione incrementa()) avr una copia del valore della variabile numero (punto 7)

1 2 5 6

incrementa() incrementa() stampa() main() variabili globali

val: 5 k: 7 i: 7 numero: 7

5 4 2 1 3

3 4 7 8

Pagina 5 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

vengono eseguite tutte le istruzioni allinterno della funzione, in particolare nel nostro esempio vengono incrementate le variabili val e k (val diventa 6 e k diventa 8). Vedi punti 9 e 10 Modificando la variabile k non viene modificata la variabile numero in quanto k una copia della variabile numero

1 2 5 6

incrementa() incrementa() stampa() main() variabili globali

val: 6 k: 8 i: 7 numero: 7

5 4 2 1 3

3 4 7 8 9 10

Pagina 6 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 6 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando una funzione termina tutte le variabili locali vengono rimosse dalla memoria, quindi nella funzione incrementa() presa in esame le variabili k e val vengono rimosse dalla memoria. Il controllo viene ceduto al chiamante, quindi alla funzione main() ripartendo dallistruzione immediatamente dopo la chiamata (punto 11)

1 2 5 6 11

incrementa() incrementa() stampa() main() variabili globali

val: 6 k: 8 i: 7 numero: 7

5 4 2 1

6 6 3

3 4 7 8 9 10

Pagina 7 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 7 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { printf(%d,i); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando il programma termina tutte le variabili allocate allinterno del main() vengono rimosse dalla memoria

1 2 5 6 11 12 13 3 4 7 8 9 10

incrementa() incrementa() stampa() main() variabili globali

val: 6 k: 8 i: 7 numero: 7

5 4 2 1

6 6 3 7

Pagina 8 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 2
Step 1 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } main() variabili globali numero: 7 1 memoria ordine alloc. deal. Allavvio del programma la prima istruzione ad essere eseguita la dichiarazione della variabile numero con assegnamento del valore 7 in memoria viene allocato lo spazio necessario per mantenere la variabile numero (variabile locale della funzione main())

Pagina 9 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

La seconda istruzione prevede una chiamata ad una funzione stampa() Quando viene chiamata una funzione, lesecuzione del programma si blocca nel punto della chiamata, il controllo viene ceduto alla funzione chiamata In memoria vengono allocate tutte le variabili locali alla funzione incluse quelle indicate nei parametri I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile i (della funzione stampa()) avr una copia del valore della variabile numero (punto 3)

1 2

3 4

stampa() stampa() main() variabili globali

val: 10 i: 7 numero: 7

3 2 1

Pagina 10 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } incrementa() incrementa() stampa() stampa() main() variabili globali val: 5 k: 10 val: 10 i: 7 numero: 7 5 4 3 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede con il punto 5 e quindi con la stampa del valore della variabile i. La seconda istruzione prevede una chiamata ad una funzione incrementa() lesecuzione della funzione stamoa() si blocca nel punto della chiamata, il controllo viene ceduto alla funzione chiamata In memoria vengono allocate tutte le variabili locali alla funzione incluse quelle indicate nei parametri I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile k (della funzione incrementa()) avr una copia del valore della variabile val (punto 7). La variabile val della funzione incrementa() non ha nulla a che fare con lomonima variabile allinterno della funzione stampa()

1 2

3 4 5 6

7 8

Pagina 11 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } incrementa() incrementa() stampa() stampa() main() variabili globali val: 6 k: 11 val: 10 i: 7 numero: 7 5 4 3 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo le istruzioni presenti allinterno del corpo della funzione incrementa() quindi con i punti 9 e 10

1 2

3 4 5 6

7 8 9 10

Pagina 12 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } incrementa() incrementa() stampa() stampa() main() variabili globali val: 6 k: 11 val: 10 i: 7 numero: 7 5 4 3 2 1 6 6 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando una funzione termina tutte le variabili locali vengono rimosse dalla memoria, quindi nella funzione incrementa() presa in esame le variabili k e val vengono rimosse dalla memoria. Il controllo viene ceduto al chiamante, quindi alla funzione stampa() ripartendo dallistruzione immediatamente dopo la chiamata (punto 11) quindi avremo la stampa del valore 10 (associato alla variabile val)

1 2

3 4 5 6 11 7 8 9 10

Pagina 13 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 6 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione stampa() termina le proprie istruzioni tutte le variabili locali vengono rimosse dalla memoria, quindi le variabili i e val vengono rimosse dalla memoria. Il controllo viene ceduto al chiamante, quindi alla funzione main() ripartendo dallistruzione immediatamente dopo la chiamata (punto 12) quindi avremo la stampa della stringa fine stampa.

1 2 12 13

incrementa() incrementa() incrementa() incrementa() stampa() stampa() main() variabili globali

val: 5 k: 7 val: 6 k: 11 val: 10 i: 7 numero: 7

9 8 5 4 3 2 1 6 6 7 7

3 4 5 6 11 14 15 7 8 9 10

Si procede eseguendo il punto 13 con la chiamata alla funzione incrementa() In memoria vengono allocate tutte le variabili locali alla funzione incluse quelle indicate nei parametri I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile k (della funzione incrementa()) avr una copia del valore della variabile numero (punto 14)

Pagina 14 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 7 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo le istruzioni presenti allinterno del corpo della funzione incrementa() quindi con i punti 9 e 10

1 2 12 13

incrementa() incrementa() incrementa() incrementa() stampa() stampa() main() variabili globali

val: 6 k: 8 val: 6 k: 11 val: 10 i: 7 numero: 7

9 8 5 4 3 2 1 6 6 7 7

3 4 5 6 11 14 15 16 17 7 8 9 10

Pagina 15 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 8 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando una funzione termina tutte le variabili locali vengono rimosse dalla memoria, quindi nella funzione incrementa() presa in esame le variabili k e val vengono rimosse dalla memoria. Il controllo viene ceduto al chiamante, quindi alla funzione main() ripartendo dallistruzione immediatamente dopo la chiamata (punto 18) quindi avremo la stampa del valore 7 (associato alla variabile numero)

1 2 12 13 18

incrementa() incrementa() incrementa() incrementa() stampa() stampa() main() variabili globali

val: 6 k: 8 val: 6 k: 11 val: 10 i: 7 numero: 7

9 8 5 4 3 2 1

10 10 6 6 7 7

3 4 5 6 11 14 15 16 17 7 8 9 10

Pagina 16 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 9 Ordine #include <stdio.h> void stampa(int i); void incrementa(int k); int main() { int numero = 7; stampa(numero); printf(fine stampa); incrementa(numero); printf(%d,numero); return 0; } void stampa(int i) { int val = 10; printf(%d,i); incrementa(val); printf(%d,val); } void incrementa(int k) { int val = 5; val++; k++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando il programma termina tutte le variabili allocate allinterno del main() vengono rimosse dalla memoria

1 2 12 13 18 19 20 3 4 5 6 11 14 15 16 17 7 8 9 10

incrementa() incrementa() incrementa() incrementa() stampa() stampa() main() variabili globali

val: 6 k: 8 val: 6 k: 11 val: 10 i: 7 numero: 7

9 8 5 4 3 2 1

10 10 6 6 7 7 11

restituzione di un parametro

Pagina 17 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 3
Step 1 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma vengono dichiarate le variabili numero e valore assegnando rispettivamente i valori 7 e 5 Si procede con la stampa a video delle variabili dichiarate precedenti. Quindi a video verranno stampati i valori 7 e 5

1 2 3 4

main() main() variabili globali

valore: 5 numero: 7

2 1

7 1000 1001 1002

5 1003 1004 1005 1006 1007 1008 1009 1010 1011

numero

valore

Pagina 18 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo il punto 5 con la chiamata alla funzione modifica() In memoria vengono allocate tutte le variabili locali alla funzione incluse quelle indicate nei parametri I parametri vengono passati alla funzione copiando, in base alla posizione, il valore nella nuova variabile locale alla funzione chiamata, quindi nel nostro esempio la variabile i (della funzione modifica()) avr una copia del valore della variabile numero (punto 6) e la variabile k avr lindirizzo di memoria della variabile valore (quindi 1001)

1 2 3 4 5

modifica() modifica() main() main() variabili globali

k: 1003 i: 7 valore: 5 numero: 7

3 3 2 1

frammento di memoria:

7 1000 1001 1002

5 1003 1004 1005 i

7 1006 1007 1008 k *k

1003 1009 1010 1011

numero

valore

Pagina 19 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

1 2 3 4 5

Vengono eseguite le istruzioni presenti allinterno della funzione: alla variabile i viene associato il valore 10, mentre, attraverso loperatore di rinvio (*) si accede alla cella di memoria con indirizzo presente allinterno della variabile k (1001) inserendo il valore 16. Modificando il *k viene modificata anche la variabile valore

modifica() modifica() main() main() variabili globali

k: 1003 i: 10 valore: 16 numero: 7

3 3 2 1

6 7 8

frammento di memoria:

7 1000 1001 1002

16 1003 1004 1005 i

10 1006 1007 1008 k *k

1003 1009 1010 1011

numero

valore

Pagina 20 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione termina tutte le variabili locali vengono eliminate dalla memoria Il controllo viene restituito alla funzione chiamante riprendendo dallistruzione immediatamente successiva alla chiamata della funzione (punto 9)

1 2 3 4 5 9

modifica() modifica() main() main() variabili globali

k: 1001 i: 10 valore: 16 numero: 7

3 3 2 1

4 4

6 7 8

frammento di memoria:

7 1000 1001 1002

16 1003 1004 1005 i

10 1006 1007 1008 k

1003 1009 1010 1011

numero

valore

Pagina 21 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Con le funzioni printf() nei punti 9 e 10 vengono stampati i valori delle variabili numero e valore, quindi rispettivamente 7 e 16

1 2 3 4 5 9 10

modifica() modifica() main() main() variabili globali

k: 1001 i: 10 valore: 16 numero: 7

3 3 2 1

4 4

6 7 8

frammento di memoria:

7 1000 1001 1002

16 1003 1004 1005 1006 1007 1008 1009 1010 1011

numero

valore

Pagina 22 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 6 Ordine #include <stdio.h> void modifica(int i, int *k); int main() { int numero = 7; int valore = 5; printf(%d,numero); printf(%d,valore); modifica(numero,&valore); printf(%d,numero); printf(%d,valore); return 0; } void modifica(int i, int *k) { i = 10; *k = 16; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione main() termina tutte le variabili locali vengono rimosse dalla memoria.

1 2 3 4 5 9 10 11 6 7 8

modifica() modifica() main() main() variabili globali

k: 1001 i: 10 valore: 16 numero: 7

3 3 2 1

4 4 5 5

frammento di memoria:

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

Pagina 23 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 4
Step 1 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma tutte le variabili globali vengono allocate in memoria. Le variabili globali sono visibili (modificabili) in tutto il programma

variabili globali

i: 0

0 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

Pagina 24 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo il punto 2 con la chiamata alla funzione elabora() In memoria vengono allocate tutte le variabili locali alla funzione

3 4

elabora() elabora() variabili globali

cont: 0 k: 0 i: 0

2 2 1

frammento di memoria:

0 1000 1001 1002 1003 1004

0 1005 1006 1007

0 1008 1009 1010 1011

cont

Pagina 25 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Vengono eseguite le istruzioni presenti nel corpo della funzione chiamata (punti 5, 6 e 7)

3 4 5 6 7

elabora() elabora() variabili globali

cont: 1 k: 1 i: 1

2 2 1

frammento di memoria:

1 1000 1001 1002 1003 1004

1 1005 1006 1007

1 1008 1009 1010 1011

cont

Pagina 26 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

2 9

La funzione elabora() termina e restituisce il controllo alla funzione main() ripartendo dallistruzione immediatamente successiva alla chiamata (punto 9). Tutte le variabili locali vengono rimosse dalla memoria eccetto le variabili statiche (le variabili statiche sono variabili visibili esclusivamente nella funzione in cui vengono dichiarate)

3 4 5 6 7 8
frammento di memoria:

elabora() elabora() variabili globali

cont: 1 k: 1 i: 1

2 2 1 3

1 1000 1001 1002 1003 1004

1 1005 1006 1007 1008 1009 1010 1011

cont

Pagina 27 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } elabora() elabora() elabora() variabili globali k: 0 cont: 1 k: 1 i: 1 4 2 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo il punto 9 con la chiamata alla funzione elabora() In memoria vengono allocate tutte le variabili locali alla funzione

2 9

10 11

3 4 5 6 7 8

frammento di memoria:

1 1000 1001 1002 1003 1004

1 1005 1006

0 1007 1008 1009 1010 1011

cont

Pagina 28 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 6 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } elabora() elabora() elabora() variabili globali k: 1 cont: 2 k: 1 i: 2 4 2 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Vengono eseguite le istruzioni presenti nel corpo della funzione chiamata (punti 12, 13 e 14)

2 9

10 11 12 13 14

3 4 5 6 7 8

frammento di memoria:

2 1000 1001 1002 1003 1004

2 1005 1006

1 1007 1008 1009 1010 1011

cont

Pagina 29 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 7 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } elabora() elabora() elabora() variabili globali k: 1 cont: 2 k: 1 i: 2 4 2 2 1 3 5 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

2 9 16

La funzione elabora() termina e restituisce il controllo alla funzione main() ripartendo dallistruzione immediatamente successiva alla chiamata (punto 16). Tutte le variabili locali vengono rimosse dalla memoria eccetto le variabili statiche

10 11 12 13 14 15

3 4 5 6 7 8

frammento di memoria:

2 1000 1001 1002 1003 1004

2 1005 1006 1007 1008 1009 1010 1011

cont

Pagina 30 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 8 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede eseguendo il punto 9 con la chiamata alla funzione elabora() In memoria vengono allocate tutte le variabili locali alla funzione

2 9 16

elabora() elabora() elabora() elabora() variabili globali

k: 0 k: 1 cont: 2 k: 1 i: 2

6 4 2 2 1 3 5

17 18

10 11 12 13 14 15

3 4 5 6 7 8

frammento di memoria:

2 1000 1001 1002 1003 1004

2 1005 1006 1007 1008 1009 1010

0 1011

cont

Pagina 31 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 9 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Vengono eseguite le istruzioni presenti nel corpo della funzione chiamata (punti 19, 20 e 21)

2 9 16

elabora() elabora() elabora() elabora() variabili globali

k: 1 k: 1 cont: 3 k: 1 i: 3

6 4 2 2 1 3 5

17 18 19 20 21

10 11 12 13 14 15

3 4 5 6 7 8

frammento di memoria:

3 1000 1001 1002 1003 1004

3 1005 1006 1007 1008 1009 1010

1 1011

cont

Pagina 32 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 10 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

2 9 16 23

La funzione elabora() termina e restituisce il controllo alla funzione main() ripartendo dallistruzione immediatamente successiva alla chiamata (punto 23). Tutte le variabili locali vengono rimosse dalla memoria eccetto le variabili statiche Allinterno della funzione main() possibile stampare il valore della variabile i in quanto variabile globale, non pu essere fatta alcuna operazione sulla variabile cont in quanto variabile locale alla funzione elabora()

elabora() elabora() elabora() elabora() variabili globali

k: 1 k: 1 cont: 3 k: 1 i: 3

6 4 2 2 1

7 5

17 18 19 20 21 22

10 11 12 13 14 15

3 4 5 6 7 8

frammento di memoria:

3 1000 1001 1002 1003 1004

3 1005 1006 1007 1008 1009 1010 1011

cont

Pagina 33 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 11 Ordine #include <stdio.h> 1 void elabora(); int i = 0; int main() { elabora(); elabora(); elabora(); return 0; } void elabora() { static int cont = 0; int k = 0; cont++; k++; i++; } memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Il programma termina e tutte le variabili allocate allinterno del main(), quelle locali e quelle statiche vengono rimosse dalla memoria

2 9 16 23 24

elabora() elabora() elabora() elabora() variabili globali

k: 1 k: 1 cont: 3 k: 1 i: 3

6 4 2 2 1

7 5 8 3 9

17 18 19 20 21 22

10 11 12 13 14 15

3 4 5 6 7 8

frammento di memoria:

3 1000 1001 1002 1003 1004

3 1005 1006 1007 1008 1009 1010 1011

cont

Pagina 34 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 5
Step 1 Ordine #include <stdio.h> void elabora(int vett[5]); int main() { int elenco[5]={2,4,6,8,10}; elabora(elenco); return 0; } void elabora(int vett[5]) { int i; for (i = 0; i < 5; i++) { vett[i] = 1; } }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma tutte le variabili locali alla funzione main() vengono allocate in memoria La variabile elenco tiene lindirizzo di memoria della prima cella allocata per mantenere in memoria larray

main() variabili globali

elenco: 1001

2 1000 1001

4 1002

6 1003

8 1004

10 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 35 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void elabora(int vett[5]); int main() { int elenco[5]={2,4,6,8,10}; elabora(elenco); return 0; } 3 4 void elabora(int vett[5]) { int i; for (i = 0; i < 5; i++) { vett[i] = 1; } } elabora() elabora() main() variabili globali i: 0 vett: 1001 elenco: 1001 3 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

1 2

Alla chiamata della funzione elabora (punto 2) il controllo viene ceduto alla funzione chiamata, tutte le variabili locali e presenti nellintestazione della funzione vengono allocate in memoria (punto 3 e 4) Allinterno della variabile vett della funzione elabora() viene copiato il valore presente allinterno della variabile elenco passata come parametro alla funzione (quindi viene copiato lindirizzo di memoria della prima cella allocata per larray elenco) La variabile vett quindi un puntatore!

frammento di memoria:

2 1000 1001

4 1002

6 1003

8 1004

10 1005 1006 1007 1008

1001 1009 1010 1011

elenco

vett

Pagina 36 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void elabora(int vett[5]); int main() { int elenco[5]={2,4,6,8,10}; elabora(elenco); return 0; } 3 4 5 6 7 8 void elabora(int vett[5]) { int i; for (i = 0; i < 5; i++) { vett[i] = 1; } } elabora() elabora() main() variabili globali i: 0, 1, 2, 3, 4 vett: 1001 elenco: 1001 3 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede con lesecuzione delle istruzioni presenti allinterno della funzione elabora()

1 2

frammento di memoria:

1 1000 1001

1 1002

1 1003

1 1004

1 1005 1006 1007 1008

1001 1009 1010 1011

elenco

vett

Pagina 37 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void elabora(int vett[5]); int main() { int elenco[5]={2,4,6,8,10}; elabora(elenco); return 0; } 3 4 5 6 7 8 9
frammento di memoria:

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ordine memoria alloc. deal.

1 2 10

Terminata la funzione elabora il controllo passa alla funzione chiamante main() ripartendo dallistruzione immediatamente successiva alla chiamata (punto 10). Tutte le variabili locali alla funzione elabora() vengono rimosse dalla memoria. Da notare che le modifiche fatte a partire da vett vanno a modificare il vettore elenco

void elabora(int vett[5]) { int i; for (i = 0; i < 5; i++) { vett[i] = 1; } }

elabora() elabora() main() variabili globali

i: 0, 1, 2, 3, 4 vett: 1001 elenco: 1001

3 2 1

4 4

1 1000 1001

1 1002

1 1003

1 1004

1 1005 1006 1007 1008

1001 1009 1010 1011

elenco

vett

Pagina 38 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> void elabora(int vett[5]); int main() { int elenco[5]={2,4,6,8,10}; elabora(elenco); return 0; } void elabora(int vett[5]) { int i; for (i = 0; i < 5; i++) { vett[i] = 1; } } elabora() elabora() main() variabili globali i: 0, 1, 2, 3, 4 vett: 1001 elenco: 1001 3 2 1 4 4 5 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Il programma termina e tutte le variabili locali al main() vengono rimosse dalla memoria.

1 2 10 11 3 4 5 6 7 8 9
frammento di memoria:

1 1000 1001

1 1002

1 1003

1 1004

1 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 39 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 6
Step 1 Ordine #include <stdio.h> int* elabora(); int main() { int *elenco; elenco = elabora(); return 0; } int* elabora() { int *tmp; tmp=(int*)malloc(sizeof(int)*5); return tmp; }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma tutte le variabili locali alla funzione main() vengono allocate in memoria La variabile elenco un puntatore, quindi una variabile in grado di tenere in memoria lindirizzo di unaltra variabile (di tipo intero)

main() variabili globali

elenco:

1000 elenco

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

Pagina 40 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> int* elabora(); int main() { int *elenco; elenco = elabora(); return 0; } 3 4 int* elabora() { int *tmp; tmp=(int*)malloc(sizeof(int)*5); return tmp; }
frammento di memoria:

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ordine memoria alloc. deal.

1 2

Alla chiamata della funzione elabora (punto 2) il controllo viene ceduto alla funzione chiamata, tutte le variabili locali e presenti nellintestazione della funzione vengono allocate in memoria (punto 4) La variabile tmp un puntatore, quindi una variabile in grado di tenere in memoria lindirizzo di unaltra variabile (di tipo intero)

elabora() main() variabili globali

tmp: elenco:

2 1

1000 elenco

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

tmp

Pagina 41 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> int* elabora(); int main() { int *elenco; elenco = elabora(); return 0; } 3 4 5 }
frammento di memoria:

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ordine memoria alloc. deal.

Si procede con leseguire le istruzioni presenti allinterno della funzione elabora(). La funzione malloc() alloca in memoria un numero di byte pari al parametro passato come argomento. Malloc() restituisce lindirizzo di memoria della prima cella di memoria allocata. Quindi la variabile tmp dopo lesecuzione della funzione malloc() avr il valore 1007 (considerando il frammento di memoria preso in esempio)

1 2

int* elabora() { int *tmp; tmp=(int*)malloc(sizeof(int)*5); return tmp;

elabora() main() variabili globali

tmp: 1007 elenco:

2 1

1007 1000 elenco 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

tmp

Pagina 42 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> int* elabora(); int main() { int *elenco; elenco = elabora(); return 0; } 3 4 5 6 7 int* elabora() { int *tmp; tmp=(int*)malloc(sizeof(int)*5); return tmp; } elabora() main() variabili globali tmp: 1007 elenco: 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione elabora() termina le proprie istruzioni tutte le variabili locali alla funzione vengono eliminate dalla memoria. Attraverso listruzione return la funzione elabora() restituisce al chiamante il valore presente nella variabile tmp quindi lindirizzo di memoria 1007 La variabile tmp viene quindi rimossa dalla memoria, ma non viene deallocato lo spazio riservato dalla funzione malloc()

1 2

frammento di memoria:

1007 1000 elenco 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

tmp

Pagina 43 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 5 Ordine #include <stdio.h> int* elabora(); int main() { int *elenco; elenco = elabora(); return 0; } 3 4 5 6 7 int* elabora() { int *tmp; tmp=(int*)malloc(sizeof(int)*5); return tmp; } elabora() main() variabili globali tmp: 1007 elenco: 1007 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

1 2

Quando la funzione elabora() termina le proprie operazioni allinterno della variabile elenco troveremo lindirizzo di memoria allocato attraverso la funzione malloc() richiamata allinterno della funzione elabora() Quindi attraverso la variabile elenco possibile accedere allarea di memoria allocata attraverso la funzione elabora()

frammento di memoria:

1007 1000 elenco 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

Pagina 44 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 7
Step 1 Ordine #include <stdio.h> void elabora(int* v); int main() { int *elenco; elabora(elenco); return 0; } void elabora(int* v) { v = (int*)malloc(sizeof(int)*5); }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma tutte le variabili locali alla funzione main() vengono allocate in memoria La variabile elenco un puntatore, quindi una variabile in grado di tenere in memoria lindirizzo di unaltra variabile (di tipo intero)

main() variabili globali

elenco:

1000 elenco

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

Pagina 45 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void elabora(int* v); int main() { int *elenco; elabora(elenco); return 0; } 3 void elabora(int* v) { v = (int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: elenco: 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

1 2

Alla chiamata della funzione elabora (punto 2) il controllo viene ceduto alla funzione chiamata, tutte le variabili locali e presenti nellintestazione della funzione vengono allocate in memoria (punto 3)

frammento di memoria:

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

elenco

Pagina 46 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void elabora(int* v); int main() { int *elenco; elabora(elenco); return 0; } 3 4 void elabora(int* v) { v = (int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: 1007 elenco: 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede con leseguire le istruzioni presenti allinterno della funzione elabora(). La funzione malloc() alloca in memoria un numero di byte pari al parametro passato come argomento. Malloc() restituisce lindirizzo di memoria della prima cella di memoria allocata. Quindi la variabile v dopo lesecuzione della funzione malloc() avr il valore 1007 (considerando il frammento di memoria preso in esempio)

1 2

frammento di memoria:

1007 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 47 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void elabora(int* v); int main() { int *elenco; elabora(elenco); return 0; } 3 4 5 void elabora(int* v) { v = (int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: 1007 elenco: 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione elabora() termina le proprie istruzioni tutte le variabili locali alla funzione vengono eliminate dalla memoria. La variabile v viene quindi rimossa dalla memoria, ma non viene deallocato lo spazio riservato dalla funzione malloc() Dopo la chiamata della funzione elabora() la variabile elenco NON contiene valori!

1 2

frammento di memoria:

1007 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 48 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15b

ESEMPIO 7-BIS
Step 1 Ordine #include <stdio.h> void elabora(int** v); int main() { int *elenco; elabora(&elenco); return 0; } void elabora(int** v) { *v=(int*)malloc(sizeof(int)*5); }
frammento di memoria:

ordine memoria alloc. deal.

Allavvio del programma tutte le variabili locali alla funzione main() vengono allocate in memoria La variabile elenco un puntatore, quindi una variabile in grado di tenere in memoria lindirizzo di unaltra variabile (di tipo intero)

main() variabili globali

elenco:

1000 elenco

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

Pagina 49 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 2 Ordine #include <stdio.h> void elabora(int** v); int main() { int *elenco; elabora(&elenco); return 0; } 3 void elabora(int** v) { *v=(int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: 1001 elenco: 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

1 2

Alla chiamata della funzione elabora (punto 2) il controllo viene ceduto alla funzione chiamata, tutte le variabili locali e presenti nellintestazione della funzione vengono allocate in memoria (punto 3) Allinterno della variabile v viene memorizzato lindirizzo di memoria del puntatore elenco

frammento di memoria:

1001 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 50 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 3 Ordine #include <stdio.h> void elabora(int** v); int main() { int *elenco; elabora(&elenco); return 0; } 3 4 void elabora(int** v) { *v=(int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: 1001 elenco: 1007 2 1 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Si procede con leseguire le istruzioni presenti allinterno della funzione elabora(). La funzione malloc() alloca in memoria un numero di byte pari al parametro passato come argomento. Malloc() restituisce lindirizzo di memoria della prima cella di memoria allocata. Attraverso *v accediamo alla variabile elenco, il valore restituito dalla funzione malloc() verr quindi scritto dentro la variabile elenco

1 2

frammento di memoria:

1007 1000 1001 1002

1001 1003 1004 1005 1006 1007 1008 1009 1010 1011

elenco

*v

Pagina 51 di 52

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Step 4 Ordine #include <stdio.h> void elabora(int** v); int main() { int *elenco; elabora(&elenco); return 0; } 3 4 5 void elabora(int** v) { *v=(int*)malloc(sizeof(int)*5); } elabora() main() variabili globali v: 1001 elenco: 1007 2 1 3 memoria ordine alloc. deal.

Corso di Programmazione A.A. 2011-12

Dispensa 15b

Quando la funzione elabora() termina le proprie istruzioni tutte le variabili locali alla funzione vengono eliminate dalla memoria. La variabile v viene quindi rimossa dalla memoria, ma non viene deallocato lo spazio riservato dalla funzione malloc() Dopo la chiamata della funzione elabora() la variabile elenco contiene lindirizzo di memoria della prima cella allocata dalla funzione malloc()

1 2

frammento di memoria:

1007 1000 1001 1002

1001 1003 1004 1005 1006 1007 1008 1009 1010 1011

elenco

Pagina 52 di 52

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 15c
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15c

Funzioni ricorsive
Le funzioni ricorsive hanno fatto la loro comparsa sulla scena molto tempo fa, praticamente con il primo linguaggio che era dotato di funzioni. Una funzione ricorsiva in sostanza una funzione che, per svolgere il proprio lavoro, richiama s stessa. Ad ogni richiamo la "profondit" dell'elaborazione aumenta, finch ad un certo punto, lo scopo viene raggiunto e la funzione ritorna. Una funzione ricorsiva "salva" il suo stato nel momento in cui richiama s stessa, solitamente nello stack. Ogni volta che la ricorsione viene invocata, tutte le variabili presenti vengono inserite nello stack ed una nuova serie di variabili viene creata (sempre dallo stack). Questo significa un elevato consumo dello stack del sistema, se stiamo lavorando con uno stack limitato (come e' il caso con certi sistemi o con certe architetture di programma), rischiamo di superare i limiti dello stack e di avere un bel crash del programma. Aggiungiamo poi che, solitamente, il numero di chiamate ricorsive della funzione non e' ipotizzabile a priori ed abbiamo un possibile problema. Un altro possibile problema e' se la nostra funzione utilizza altre risorse oltre alla memoria del sistema, e tali risorse sono in quantita' limitata (connessioni a database per esempio). In questo caso, se il numero di richiami e' superiore ad un certo livello, possiamo avere un fallimento di chiamata. Non sempre le funzioni ricorsive sono una risposta efficiente. Consideriamo che ogni chiamata ricorsiva consuma memoria ed utilizza un salto ad una funzione. Ridisegnando la funzione come non-ricorsiva, si risparmia un salto e (forse) un po' di quella preziosa memoria.

Esempi
Stampa di un elenco di numeri Il seguente programma stampa lelenco dei numeri allinterno di un intervallo (da 1 a 3) in modo ricorsivo: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. #include <stdio.h> void stampa(int mun); int main() { int x = 1; stampa(x); return 0; } void stampa (int num) { if (num > 3) return; printf(%d\n,num); stampa(num+1); }

(chiamata A) Dopo la riga 5 la memoria si configura nel seguente modo: nel main x: 1

Pagina 2 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


dopo la chiamata alla funzione stampa() alla riga 8 nel main, la memoria la seguente: stampa() nel main num: 1 x: 1 A

Corso di Programmazione A.A. 2011-12

Dispensa 15c

(chiamata B) Dato che num non maggiore di 3 si procede con la stampa a video del numero 1 e alla riga 17 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 2), la memoria diventa: stampa() stampa() nel main num: 2 num: 1 x: 1 B A

(chiamata C) Dato che num non maggiore di 3 si procede con la stampa a video del numero 2 e alla riga 17 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 3), la memoria diventa: stampa() stampa() stampa() nel main num: 3 num: 2 num: 1 x: 1 C B A

(chiamata D) Dato che num non maggiore di 3 si procede con la stampa a video del numero 3 e alla riga 17 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 4), la memoria diventa: stampa() stampa() stampa() stampa() nel main num: 4 num: 3 num: 2 num: 1 x: 1 D C B A

dato che la variabile num maggiore di 3 viene eseguita listruzione return alla riga 15, quindi la chiamata-D termina e il controllo passa alla funzione ricorsiva chiamante (chiamata-C), in particolare il controllo passa allistruzione successiva alla chiamata ricorsiva della funzione: riga 18, quindi la funzione alla chiamata 3 termina le proprie operazioni e cede il controllo al chiamante (chiamata-B)... si procede in questo modo fino alla prima chiamata. Consideriamo una variante del programma visto precedentemente, inserendo la stampa dopo la chiamata ricorsiva: 1. #include <stdio.h> 2. 3. void stampa(int mun); 4. 5. int main()

Pagina 3 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. { int x = 1; stampa(x); return 0; } void stampa (int num) { if (num > 3) return; stampa(num+1); printf(%d\n,num); }

Corso di Programmazione A.A. 2011-12

Dispensa 15c

Dopo la riga 5 la memoria si configura nel seguente modo: nel main x: 1

(chiamata A) dopo la chiamata alla funzione stampa() alla riga 8 nel main, la memoria la seguente: stampa() nel main num: 1 x: 1 A

(chiamata B) Dato che num non maggiore di 3 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 2), la memoria diventa: stampa() stampa() nel main num: 2 num: 1 x: 1 B A

(chiamata C) Dato che num non maggiore di 3 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 3), la memoria diventa: stampa() stampa() stampa() nel main num: 3 num: 2 num: 1 x: 1 C B A

(chiamata D) Dato che num non maggiore di 3 viene eseguita la chiamata ricorsiva passando il valore num+1 (cio il valore 4), la memoria diventa: stampa() stampa() num: 4 num: 3 D C

Pagina 4 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


stampa() stampa() nel main num: 2 num: 1 x: 1 B A

Corso di Programmazione A.A. 2011-12

Dispensa 15c

dato che la variabile num maggiore di 3 viene eseguita listruzione return alla riga 15, quindi la chiamata-D termina e il controllo passa alla funzione ricorsiva chiamante (chiamata-C), in particolare il controllo passa allistruzione successiva alla chiamata ricorsiva della funzione: riga 17 quindi alla stampa della variabile num allinterno della chiamata-C quindi la stampa del numero 3. Questo perch allinterno della chiamata-C il valore della variabile num pari a 3. Dopo aver stampato il numero la funzione alla chiamata-C termina e il controllo passa al chiamante e cio chiamata-B, in particolare il controllo passa allistruzione successiva alla chiamata ricorsiva della funzione: riga 17 quindi alla stampa della variabile num allinterno della chiamata-B quindi la stampa del numero 2... si procede in questo modo fino alla prima chiamata. Nella prima versione dellesempio loutput sar: 1, 2, 3. Nel secondo esempio invece avremo: 3, 2, 1 Fattoriale Il seguente programma calcola il fattoriale di un numero in modo ricorsivo: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. #include <stdio.h> int fattoriale(int mun); int main() { int x, fat; scanf(%d,&x); fat = fattoriale(x); printf(fattoriale: %d, fat); return 0; } int fattoriale(int num) { if (n == 0) return 1; ris = num * fattoriale(num - 1); rerturn ris; }

Supponiamo che venga inserito il valore 3 come input. Dopo la lettura della variabile x la memoria si configura cos: nel main x: 3

(chiamata A) Dopo la chiamata della funzione fattoriale() passando il valore num-1 quindi 3 (riga 19), la memoria la seguente: fattoriale() num: 3 A

Pagina 5 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


nel main x: 3

Corso di Programmazione A.A. 2011-12

Dispensa 15c

(chiamata B) Dopo la prima chiamata ricorsiva della funzione fattoriale()passando il valore num-1 quindi 2 (riga 19), la memoria la seguente: fattoriale() fattoriale() nel main num: 2 num: 3 x: 3 B A

(chiamata C) Dopo la prima chiamata ricorsiva della funzione fattoriale() passando il valore num-1 quindi 1 (riga 19), la memoria la seguente: fattoriale() fattoriale() fattoriale() nel main num: 1 num: 2 num: 3 x: 3 C B A

(chiamata D) Dopo la prima chiamata ricorsiva della funzione fattoriale() passando il valore num-1 quindi 0 (riga 19), la memoria la seguente: fattoriale() fattoriale() fattoriale() fattoriale() nel main num: 0 num: 1 num: 2 num: 3 x: 3 D C B A

alla chiamata-D il valore della variabile num uguale a 0 quindi al controllo nella riga 16 viene eseguita listruzione return passando il valore 1 al chiamante, quindi alla chiamata-C. Alla chiamata-C troviamo nella riga 19 il valore restituito dalla chiamata-D e cio 1 e il valore di num allinterno di questa chiamata e cio 1. Dopo aver calcolato il prodotto il risultato viene restituito alla chiamata-B. Alla chiamata-B troviamo nella riga 19 il valore restituito dalla chiamata-C e cio 1 e il valore di num allinterno di questa chiamata e cio 2. Dopo aver calcolato il prodotto il risultato viene restituito alla chiamata-A. Alla chiamata-A troviamo nella riga 19 il valore restituito dalla chiamata-B e cio 2 e il valore di num allinterno di questa chiamata e cio 3. Dopo aver calcolato il prodotto il risultato viene restituito alla main e stampato a video (viene visualizzato il valore 6). Torri di Hanoi La Torre di Hanoi un rompicapo matematico composto da tre paletti e un certo numero di dischi di grandezza decrescente, che possono essere infilati in uno qualsiasi dei paletti.

Pagina 6 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 15c

Il gioco inizia con tutti i dischi incolonnati su un paletto in ordine decrescente, in modo da formare un cono. Lo scopo del gioco portare tutti dischi sull'ultimo paletto, potendo spostare solo un disco alla volta e potendo mettere un disco solo su un altro disco pi grande, mai su uno pi piccolo.

La propriet matematica base che il numero minimo di mosse necessarie per completare il gioco 2n - 1, dove n il numero di dischi. Ad esempio avendo 3 dischi, il numero di mosse minime 7. Di conseguenza, secondo la leggenda, i monaci di Hanoi dovrebbero effettuare almeno 18.446.744.073.709.551.615 mosse prima che il mondo finisca, essendo n = 64. La soluzione generale data dall'algoritmo seguente: Si etichettano i paletti con le lettere A, B e C Dato n il numero dei dischi Si numerano i dischi da 1 (il pi piccolo, in alto) a n (il pi grande, in basso)

Per spostare i dischi dal paletto A al paletto B: 1. Sposta i primi n-1 dischi da A a C. Questo lascia il disco n da solo sul paletto A 2. Sposta il disco n da A a B 3. Sposta n-1 dischi da C a B Questo un algoritmo ricorsivo, di complessit esponenziale, quindi per risolvere il gioco con un numero n di dischi, bisogna applicare l'algoritmo prima a n-1 dischi. Dato che la procedura ha un numero finito di passi, in un qualche punto dell'algoritmo n sar uguale a 1. Quando n uguale a 1, la soluzione banale: basta spostare il disco da A a B.

Pagina 7 di 7

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI

A.A. 2011-12

PROGRAMMAZIONE

Dispensa 16
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

I File su disco
Flussi e file su disco
Tutte le operazioni di input e output, comprese quelle dei file su disco, avvengono in C mediante i flussi. Limpiego dei flussi predefiniti, collegati alla tastiera e allo schermo operano sostanzialmente allo stesso modo dei flussi su file. Questo il vantaggio dellinput/output mediante flussi: le tecniche sono le stesse per tutti i flussi. La caratteristica che distingue i flussi dai file su disco la necessit di creare esplicitamente il flusso da associare a un file.

Tipi di file su disco


Nel C ci sono due tipi di flussi: di testo e binari. A un file si pu associare ciascuno di due tipi; importante cogliere le differenze, per poter scegliere di volta in volta il tipo appropriato. I flussi di testo sono associati ai file in in odo testo. Tali file sono formati da un insieme di righe. Ogni riga contiene da zero o pi caratteri speciali, che ne segnalano la fine. Una riga pu essere al massimo lunga 255 caratteri. Va sottolineato che una riga in questo senso non una stringa: non terminata dal carattere NULL (\0). Quando si lavora su flussi in modo testo, si compie una traduzione tra il carattere di fine riga del C (\n) e i caratteri utilizzati dal sistema operativo per segnalare la fine di una riga del file su disco. Nei sistemi DOS, ad esempio, si utilizza la combinazione CR-LF (ritorno a capo, avanzamento riga). Nel copiare i dati verso un file su disco, ogni \n tradotto in una coppia CR-LF; viceversa quando i dati vengono letti da un file su disco. Nei sistemi UNIX non occorre alcuna traduzione, perch il carattere di fine riga lo stesso. I flussi binari sono associati a file in modo binario. In questo caso, i dati sono copiati cos come sono e non vengono considerati logicamente divisi in righe. I caratteri NULL e di fine riga sono trattati esattamente come ogni altro carattere. Alcune funzioni di input/output su file si applicano solo in uno dei due modi, mentre altre sono universali.

I nomi dei file


Ogni file su disco ha un nome che si pu utilizzare nei programmi, i nomi sono memorizzati come stringhe di testo. Le regole che stabiliscono quali nomi sono accettabili variano a seconda del sistema operativo. E importante quindi conoscere le regole del sistema operativo su cui si opera. Il nome di un file in C pu contenere anche dati relativi al percorso (path), che indica lunit e la cartella in cui si trova il file. Quando non si specifica il percorso, si assume che il file si trova nella posizione che il sistema operativo considera corrente in quel momento. Si consiglia di specificare in ogni caso il percorso completo. Nel PC, per separatre i nomi delle directory in un percorso, si utilizza il carattere \ (detto barra inversa). Ad esempio in DOS e Windows, il nome: a:\dati\elenco.txt denota un file di nome elenco.txt, nella cartella \dati, nellunit a. Non dimentichiamo che il carattere \ ha un significato speciale nelle stringhe del C. Per inserirlo letteralmente in una stringa, gli si deve anteporre un altro \. Perci, in un programma in C, il nome dellesempio precedente va indicato cos: char *filename = a:\\dati\\elenco.txt; Quando per si digita il nome da tastiera, si deve inserire un solo \. Non tutti i sistemi operativi si conformano a questo stile. In UNIX, ad esempio, il separatore di cartelle la barra obliqua /.

Pagina 2 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Apertura del file


La creazione di un flusso collegato ad un file su disco detta apertura del file. Dopo essere stato aperto, un file disponibile per la lettura, la scrittura o entrambi. Al termine delle operazioni, si deve chiudere il file. Per aprire un file, si utlizza la funzione di libreria fopen(), il cui prototipo, posto in stdio.h, il seguente: FILE *fopen(const char *nome, const char *modo); Secondo il prototipo, fopen() restituisce un puntatore a FILE, una struttura dichiarate in stdio.h. I membri della struttura sono sfruttati dal programma nelle operazioni di accesso al file, ma il programmatore pu disinteressarsene. Per ogni file che si intende aprire, si deve dichiarare un puntatore a FILE. La chiamata fopen() provoca la creazione di un esemplare di FILE e restituisce un puntatore a quellesemplare. Tutte le operazioni seguenti sul file si servono di quel puntatore. La funzione fopen() segnala il proprio fallimento restituendo il valore NULL. Ci pu essere dovuto, ad esempio, a un errore hardware o al tentativo di aprire un file di un dischetto no formattato. Largomento nome il nome del file da aprire. Come si gi detto, nome pu (e dovrebbe) contenere la specifica del percorso. Largomento nome pu consistere di una stringa letterale o di un puntatore a una variabile di tipo stringa. Largomento modo specifica in che modo il file va aperto, cio se il file binario o di testo e se deve essere aperto in lettura, scrittura o entrambi. I valori riconosciuti sono elencati nella tabella sotto. Il modo di default testo. Per aprire un file in modo binario, si deve aggiungere una b allargomento modo. Perci, ad esempio, se modo valre a, il file aperto per laggiunta, in modo testo; se vale ab, il file aperto per laggiunta in modo binario.

Modo r w

Significato Apre il file in lettura. Se il file non esiste, fopen() restituisce NULL Apre il file in scrittura. Se il file non esiste un file con il nome indicato, ne viene creato uno. Se esiste, il suo contenuto viene cancellato senza preavviso. Apre il file in modo di aggiunta. Se non esiste un file con il nome indicato ne viene creato uno. Se esiste, i dati nuovi vengono aggiunti in coda al file Apre il file in lettura e scrittura. Se non esiste un file con il nome indicato ne viene creato uno. Se esiste, i dati nuovi vengono inseriti allinizio, cancellando quelli precedenti Apre il file in modo di aggiunta. Se non esiste un file con il nome indicato ne viene creato uno. Se esiste, i dati nuovi vengono inseriti allinizio, cancellando quelli precedenti Apre il file in modo di aggiunta. Se non esiste un file con il nome indicato ne viene creato uno. Se esiste, i dati nuovi vengono aggiunti in coda al file

r+

w+

a+

Si ricorda che fopen() restituisce NULL in caso di errore. Fra le condizioni che possono provocare tale evento, sono comprese le seguenti.

Pagina 3 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Nome non valido Tentativo di aprire un file su un disco non pronto Tentativo di aprire un file in una cartella o unit inesistente Tentativo di aprire un file inesistente in sola lettura

Corso di Programmazione A.A. 2011-12

Dispensa 16

E buona norma verificare se c stato un errore a ogni chiamata di fopen(). Anche se non possibile stabilire quale sia stato lerrore, si pu almeno avvisare lutente e ripetere il tentativo o terminare il programma, secondo i casi. Spesso i compilatori del C offrono estensioni non ANSI che permettono di stabilire il tipo di errore, si consulti la documentazione del compilatore. Vediamo un esempio di apertura di file su disco in vari modi mediante fopen(). #include <stdlib.h> #include <stdio.h> main() { FILE *fp; Char chm filename[40], mode[4]; while(1) { /*legge il nome del file e il modo*/ printf(\n Inserire il nome del file: ); gets(filename); printf(\n Inserire il modo (max 3 caratteri): ); gets(mode); /*Apertura del file*/ if ((fp = fopen(filename,mode)) != NULL) { printf(\n Il file %s aperto in modo %s ,filename,mode); fclose(fp); puts(Premere x per uscire, un altro tasto per proseguire); if ((ch = getc(stdin)) == x) break; } } return 0; }

Scrittura e lettura dei dati su file


Un programma pu utilizzare un file su disco scrivendovi o leggendovi dei fati, anche in combinazione. Ci sono tre modi di scrivere dati in un file: Per mezzo delloutput formattato, si possono salvare in un file in un formato specifico. Ci si applica a file in modo testo. Limpiego principale di questa forma la creazione del file che contengono testo o valori numerici, destinati a essere trattati da altri programmi come fogli di calcolo o basi di dati.

Pagina 4 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Per mezzo delloutput a caratteri, si salvano in un file caratteri singoli o righe. Si pu applicare questa tecnica ai file in modo binario, ma si consiglia di limitarle ai file in modo testo. Limpiego principale di questa forma il salvataggio di testi (dati non numerici) in file destinati a essere letti in programmi come un elaboratore di testi. Per mezzo delloutput diretto, si salva in un file su disco il contenuto de unarea di memoria. Questa forma si applica solo a file binari ed la via migliore per salvare dati destinati a un uso successivo in un programma C.

Nella lettura dei fati da un file, si hanno le possibilit analoghe: input formattato, input a caratteri e input diretto. La scelta dipende quasi esclusivamente dal tipo del file su cui si opera. In genere, i dati vengono letti nello stesso modo in cui sono stati salvati, ma ci non indispensabile. Peraltro, la lettura in un modo diverso dalla precedente scrittura richiede la conoscenza approfondita del C e dei formati dei file.

Input e output formattato


Input e output formattato si riferiscono a dati, testuali o numerici, in un formato specifico. Essi sono strettamente legati allinput formattato da tastiera e alloutput formattato su schermo, mediante le funzioni scanf() e printf(). Funzione fprintf() Loutput formattato su file si compie per mezzo delfa funzione di libreria fprintf(), il cui prototipo, posto nel file di intestazione stdio.h, qui riportato: int fprintf(FILE *fp, char *fmt, ); Il primo argomento un puntatore a FILE. Per scrivere dati su un file, si passa alla funzione il puntatore restituito da fopen() allapertura del file. Il secondo argomento la stringa di formato, segue le stesse regole della funzione printf(). Lultimo argomento indicato dai punti di sospensione, essi rappresentano un numero variabile di argomenti. In altre parole, oltre al puntatore al file e alla stringa di formato, fprintf() accetta zero, uno o pi argomenti supplementari, proprio come printf(). Tali argomenti sono i nomi delle variabili di cui valori vanno copiati nel flusso specificato. Riassumento, fprintf() opera come printf(), tranne che loutput inviato al flusso specificato come primo argomento. Se tale argomento stdout, lefetto di fprintf() identico a quello di printf(). Vediamo un esempio: #include <stdlib.h> #include <stdio.h> main() { FILE *fp; float data; int count; Char chm filename[40]; puts(inserire cinque numeri decimali.);

Pagina 5 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


for (count = 0; count < 5; count++) scanf(%f,&data[count]); fflush(stdin); puts(inserire il nome del file:); gets(filename); if ((fp = fopen(filename,w)) != NULL) { fprintf(stderr,Errore nellapertura del file %s, filename); exit(1); } /*scrive i dati su file e su stdout*/ for (count = 0; count < 5; count++) { fprintf(fp, \ndata[%d] = %f, count, data[count]); fprintf(stdout, \ndata[%d] = %f, count, data[count]); } fclose(fp); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 16

Per linput formattato da file, si utilizza la funzione di libreria fscanf(), simile a scanf(), tranne che linput di fscanf() proviene da un flusso specificato, anzich stdin. Funzione fscanf() Ecco il prototipo di fscanf(): int fscanf(FILE *fp, conts char *fmt, ); Largomento fp un puntatore a FILE, usualmente restituito da una chiamata di fopen(); fmt un puntatore a una stringa di formato, che indica come la funzione deve leggere il fato di ingresso. I componenti della stringa di formato sono gli stessi di scanf(). Infine, i punti di sospensione denotano gli eventuali argomenti supplementari, cio gli indirizzi delle variabili alle quali assegnare i valori letti. Per mettere alla prova fscanf(), occorre un file di testo contenente numeri e parole in un formato leggibile dalla funzione, Con un editor, creare un file di nome input.txt, e inserirvi cinque numeri in virgola mobile, separati da spazi o a capo. Il contenuto del file potrebbe essere ad esempio: 123.45 100.03 87.001

0.00456 1.005

Pagina 6 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Vediamo lesempio:

#include <stdlib.h> #include <stdio.h> main() { FILE *fp; float f1,f2,f3,f4,f5; if ((fp = fopen(input.txt,r)) != NULL) { fprintf(stderr,Errore nellapertura del file); exit(1); } fscanf(fp, %f %f %f %f %f , &f1, &f2, &f3, &f4, &f5); printf(I valori sono %f, %f, %f, %f, %f, f1, f2, f3, f4, f5); fclose(fp); return 0; }

Input e output a caratteri


Nellambito dei file su disco, il termine I/O a caratteri si riferisce sia a caratteri singoli sia a righe di caratteri. Come gi detto, una riga una sequenza di caratteri chiusa dal carattere di nuova riga. LI/O a caratteri si applica ai file in modo testo. Le funzioni per linput a caratteri: getc() e fgetc() per caratteri singoli; fgets() per righe intere. Funzione getc() Le funzioni getc() e fgetc() sono identiche e intercambiabili. Esse leggono un carattere dal flusso specificato. IL prototipo di getc() contenuto nel file stdio.h il seguente: int getc(FILE *fp); Largomento fp un puntatore, tipicamente quello restituito da fopen() allapertura di un file. La funzione restituisce il carattere letto o in caso di errore la costante EOF. La funzione getc() gi stata utilizzata per leggere un carattere battuto da tastiera. Funzione fgets() La funzione fgets() per leggere una riga di caratteri da un file, il suo prototipo :

Pagina 7 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

char *fgets(char *str, int n, FoE *fp); Il primo argomento un puntatore al buffer in cui collocare linput; n il numero massimo di caratteri che si possono leggere; fp un puntatore al file da cu leggere, solitamente restituito da fopen(). Alla chiamata, fgets() legge caratteri dal file puntato da fp in memoria, a partire dallindirizzo indicato da str. La lettura prosegue fino a quando si incontra il carattere di nuova riga o qando sono stati letti n-1 caratteri, secondo quale condizione si verifica per prima. Se loperazione non va a buon fine, fgets() restituisce str. Sono possibili due tipi di errore, segnalati da un valore di ritorno NULL: Se avviene un errore di lettura o si incontra EOF prima di aver letto almeno un carattere, la funzione restituisce NULL; il buffer a cui punta str rimane invariato Se avviene un errore di lettura o si incontra EOF dopo aver letto almeno un carattere, la funzione restituisce NULL; il contenuto del buffer a cui punta str indefinito.

Va notato che fgets() non legge necessariamente una riga intera (cio tutto ci che precede il prossimo carattere di nuova riga).Se si raggiunge il numeri di n-1 caratteri letti prima di aver incontrato il carattere di nuova riga, la funzione si arresta. La successiva operazione di lettura pertir dalla posizione in cui si arrestata precedentemente. Si presentato due funzioni di output a caratteri: putc() e fputs(). Funzione putc() La funzione di libreria putc() scrive un carattere nel flusso specificato. IL su prototipo, contenuto di stdio.h, il seguente: int putc(int ch, FILE *fp); Largomento ch il carattere da scrivere. Come nelle altre funzioni che trattano caratteri, esso dichiarato di tipo int, ma in realt si utilizza solo il byta basso. Largomento fp un puntatore al file su cui scrive, solitamente restituito da fopen(). La funzione putc() restituisce il carattere scritto in caso di riuscita e OEF in caso di errore. La costante simbolica di EOF, definita in stdio.h, ha il valore 1. Funzione fputs() Per scrivere una riga di caratteri in un flusso, si utilizza la funzione di libreria fputs(), che opera esattamente come puts(). La sola differenza che in fputs() si pu specificare il flusso di destinazione. Inoltre fputs() non aggiunge il carattere di nuova riga al termine della stringa, il programmatore se lo desidera, deve inserirlo esplicitamente. Il prototipo della funzione, in stdio.h, il seguente: int fputs(char *str, FILE *fp); Largomento str un puntatore alla stringa da scrivere; fp un puntatore a FILE. La stringa a cui punta str viene scritta nel file, senza il carattere terminale \0. La funzione fputs() restituisce un valore negativo in caso di succeso e EOF in caso di errore.

Pagina 8 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Input e output diretto


Linput/output diretto su file si utilizza in genere per salvare dati destinati a essere letti in seguito dallo stesso o da un altro programma in C. Esso si applica solo a file in modo binario. Loutput diretto trasferisce blocchi di dati dalla memoria al disco. Ad esempio, con una sola chiamata di una funzione di output diretto, si pu scrivere su disco un intero array; viceversa, una sola chiamata di una funzione di input diretto pu leggere larrary dal disco alla memoria. Le funzioni di input/output diretto fread() e ferite(). Funzione fwrite() La funzione fwrite() scrive un blocco di dati dalla memoria a un file in modo binario. Il suo prototipo in stdlib.h, il seguente: int fwrite(void *buf, int size, int count, FILE *fp); Largomento buf un puntatore allarea di memoria contente i dati da scrivere nel file. Il suo tipo void: pu puntare a qualsiasi oggetto. Largomento size specifica la dimensione, in byte, dei singoli elementi-dato; count specifica quanti elementi devono essere scritti. Ad esempio per salvare un array di cento interi, size deve valere 2 (perch un intero occupa 2 byte) e count deve valere 100 (perch larray contiene 100 elementi). Per stabilire il valore di size si pu utilizzare la funzione sizeof(). Largomento fp un puntatore al file su cui scrivere, solitamente restituito da fopen(). La funzione ferite() restituisce il numero di elementi effettivamente scritti; un valore inferiore a count indice di errore. Funzione fread() La funzione fread() legge un blocco di dati da un file in modo binario verso la memoria. Il suo prototipo in stdlib.h, il seguente: int fread(void *buf, int size, int count, FILE *fp); Gli argomenti di fread(), sono analoghi a quelli di ferite(). Largomento di buf un puntatore allarea di memoria destinata a ricevere i dati letti dal file. Vediamo un esempio di utilizzo delle funzioni ferite() e fread(). #include <stdlib.h> #include <stdio.h> #define SIZE 20 main() { FILE *fp; int count, array1[SIZE], array2[SIZE]; /*inizializza array1[]*/ for (count = 0; count < SIZE; count++)

Pagina 9 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


array1[count] = 2*count; /*apre un file in modo binario*/ if ((fp = fopen(direct.txt,wb)) != NULL) { fprintf(stderr,Errore nellapertura del file); exit(1); } /*salva array1[] sul file*/ if (fwrite(array1, sizeof(int), SIZE, fp) != SIZE) { fprintf(stderr,Errore nella scrittura su file); exit(1); } fclose(fp); /*riapre allo stesso file per la lettuar in modo binario*/ if ((fp = fopen(direct.txt,rb)) != NULL) { fprintf(stderr,Errore nellapertura del file); exit(1); } /*legge i dati in array2[]*/ if (fread(array2, sizeof(int), SIZE, fp) != SIZE) { fprintf(stderr,Errore di lettura del file); exit(1); } fclose(fp); for(count = 0; count < SIZE; count++) printf(%d\t%d\n,array1[count],array2[count]); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 16

File e buffer: chiusura e svuotamento


Al termine delle operazioni su file, questo va chiuso con la funzione fclose(), gi utilizzata in programmi di questo capitolo. Il suo prototipo il seguente: int fclose(FILE *fp); Largomento un puntatore al file associato al flusso; fclose() restituisce 0 in caso di successo e 1 in caso di errore. Quando si chiude un file, il suo buffer viene spazzato, cio scritto sul file. La funzione fcloseall() chiude tutti i flussi aperti, salvo quelli standard (stdin, stdout, stderr). Il suo prototipo il seguente: int fcloseall(void);

Pagina 10 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

La funzione spezza il buffer di ogni flusso e restituisce il numeri dei flussi chiusi. Quando il programma termina (per avere raggiunto la fine di main() o per aver eseguito la funzione exit()), tutti i flussi aperti vengono automaticamente spazzati e chiusi. Tuttavia buona norma chiudere esplicitamente i flussi, quando non sono pi necessari. Il motivo legato ai buffer dei flussi. Quando si crea un flusso associato a un file su disco, un buffer viene automaticamente creato e associato al flusso in questione. Un buffer unarea di memoria che ospita temporaneamente i dati destinati a essere letti da un file o scritti nel file. I buffer sono necessari perch le unit a disco sono dispositivi a blocchi, cio operano leggendo e scrivendo i dati in blocchi di dimensione predefinita. La dimensione ideale dei blocchi varia secondo il dispositivo. Il programmatore non deve preoccuparsi del valore esatto. Il buffer associato a un flusso e lhardware del disco. Quando il programma scrive dati nel flusso, i dati vengono salvati nel buffer, fino a quando il buffer pieno; a questo punto, lintero contenuto del buffer viene copiato nel disco come blocco. Un processo simmetrico avviene in lettura. La creazione e gestione del buffer sono a cura del sistema operativo. In pratica, tutto ci significa che, durante lesecuzione del programma, certi dati logicamente gi scritti si trovano in realt nel buffer, e non ancora sul disco. Se il programma si blocca, se manca la corrente o se si verificano altri problemi, i dati nel buffer potrebbero perdersi. Con le funzioni di libreria fflush() e flushall() possibile spazzare il buffer di un flusso senza chiuderlo. Si utilizza fflush() per trasferire su disco il contenuti di un buffer, continuando a operare sul file associato. Si utilizza flushall() par spazzare i buffer di tutti i flussi aperti. I prototipi delle due funzioni sono i seguenti: int fflush(FILE *fp); int flushall(void); Largomento fp un puntatore a FILE.

Accessi sequenziale e casuale


A ogni file aperto associato un indicatore di posizione (che chiameremo segnaposto), che determina il punto in cui si compiono le operazioni di lettura e scrittura. La posizione espressa in byte dallinizio del file. Quando si apre un file nuovo, il segnaposto allinizio, cio alla posizione 0 (il file essendo nuovo di lunghezza 0). Quando si apre un file preesistente, il segnaposto si trova alla fine del file se questo stato aperto in modo di apprendimento e allinizio se stato aperto in un altro modo. Tutte le funzioni di input/output descritte in precedenza sfruttano il segnaposto, anche se di nascosto. Le operazioni di lettura e scrittura avvengono nella posizione segnata dal segnaposto e lo aggiornano. Se occorre controllare il segnaposto, si possono utilizzare apposite funzioni di librerie. Si pu cos realizzare il cosiddetto accesso casuale a un file, cio laccesso diretto a qualsiasi posizione del file, per leggere e scrivere dati, senza dover scorrere i precedenti. Le funzioni ftell() e rewind() Per controllare il segnaposto allinizio del file, si utilizza la funzione di libreria rewind(), il cui prototipo, in stdio.h, il seguente: void rewind(FILE *fp);

Pagina 11 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Largomento fp un puntatore al file associato al flusso. Dopo la chiamata di rewind(), il segnaposto si trova allinizio del file (byte 0). Per determinare la posizione del segnaposto, si utilizza la funzione ftell(), il cui prototipo, in stdio.h, il seguente: long ftell(FILE *fp); Largomento fp un puntatore al file associato al flusso. La funzione restituisce un valore di tipo long, che da la posizione corrente nel file, espressa in byte dallinizio del file (il primo byte si trova alla posizione 0). In caso di errore la funzione restituisce 1. Le funzioni fseek() La funzione fseek() consente un controllo pi versatile del segnaposto di un flusso. Con fseek(), si pu posse il segnaposto in qualsiasi posizione di un file. Il prototipo della funzione, in stdio.h il seguente: int fseek(FILE *fp, long offset, int origin); Largomento fp il puntatore al file associato al flusso. La distanza di cui spostare il segnaposto, espressa in byte, indicata da offset. Largomento origin specifica il punto di partenza dello spostamento. I suoi valori possibili sono tre, le corrispondenti costanti simboliche sono descritte nella seguente tabella:

Costante SEEK_SET SEEK_CUR SEEK_END

Valore 0 1 2

Descrizione Sposta il segnaposto di offset byte dallinizio del file Sposta il segnaposto di offset byte dalla posizione corrente Sposta il segnaposto di offset byte dalla fine del file

La funzione fseek() restituisce 0 se lo spostamento stato compiuto e un valore diverso da zero se c stato u errore. Vediamo un esempio:

#include <stdlib.h> #include <stdio.h> #define MAX 50 main() { FILE *fp; int count, array [MAX]; long offset; /*inizializza array*/ for (count = 0; count < MAX; count++)

Pagina 12 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


array[count] = 10*count; /*apre un file*/ if ((fp = fopen(random.dat,wb)) != NULL) { fprintf(stderr,Errore nellapertura del file); exit(1); } /*ascrive larray nel file*/ if (fwrite(array, sizeof(int), MAX, fp) != MAX) { fprintf(stderr,Errore nella scrittura su file); exit(1); } fclose(fp); /*riapre allo stesso file per la lettua in modo binario*/ if ((fp = fopen(random.dat,rb)) != NULL) { fprintf(stderr,Errore nellapertura del file); exit(1); } /*Chiede allutente quale elemento leggere. Viene letto lelemento e lo visualizza; termina se lutente inserisce -1*/ while(1) { printf(\n Inserire elem da leggere, 0-%d, -1 per uscire: ,MAX-1); scanf(%ld,offset); if (offset < 0) break; /*Pone il segnaposto alla posizione richiesta*/ if (fseek(fp, (offset * sizeof(int), SEEK_SET)) != 0) { fprintf(stderr,Errore nelluso di fseek()); exit(1); } /*Legge un numero intero*/ fread(&data, sizeof(int), 1, fp); printf(\n Lelemento in %ld ha un valore: %d),offset,data); } fclose(fp); return 0; }

Corso di Programmazione A.A. 2011-12

Dispensa 16

Pagina 13 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 16

Rivelazione della fine di un file


Esistono due modi per rilevare la fune di un file. Nella lettura carattere per carattere di un file in modo testo, la fine del file segnalata da un carattere speciale, individuato dalla costante speciale EOF, definita in stdio.h e pari a 1. Il valore .1 non corrisponde a nessun carattere vero. Quando una funzione di input a caratteri legge EOF da un file in modo testo si pu essere certi che stata raggiunta la fune del file. In un flusso binario, non si pu applicare lo stesso criterio, perch un byte potrebbe avere il valore 1 ed essere un dato significativo. Si deve invece utilizzare la funzione di libreria feof(), valida sia per i file in modo binario sia per i file in modo testo. Il prototipo della funzione : int feof(FILE *fp); Largomento fp un puntatore al file, solitamente restituito da fopen(). La funzione feof() restituisce un valore diverso da zero se stata raggiunta la fine del file e 0 in caso contrario. Quando una chiamata di feof() rileva la fine del file, ogni operazione di lettura sul file proibitafino a una chiamata di rewind() o fseek(), oppure fino alla chiusura e riapertura del file.

Pagina 14 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Esercizio:

Corso di Programmazione A.A. 2011-12

Dispensa 16

Scrivere un programma che apre un file di testo e conta le occorrenze di ciascun carattere del file. Si conteggiano tutti i caratteri presenti sulla tastiera, distinguendo lettere maiuscole e minuscole, numeri, gli spazi e i segni di punteggiatura. Il risultato deve essere stampato sullo schermo. Soluzione esercizio:

#include <stdlib.h> #include <stdio.h> int file_exists(char *filename); main() { FILE *fp; char ch, source[81]; int index; long count[127]; printf(\n Inserire il nome del file: ); gets(source); if (file_exists(source)) { fprintf(stderr,\n Il file %s non esiste.,cource); exit(1); } if ((fp = fopen(source,rb)) != NULL) { fprintf(stderr,\n Errore nellapertura del file %s,source); exit(1); } for (index = 0; index z 127; index++) count[index] = 0; while(1) { ch = fgetc(fp); /*Termina se trova la fine del file*/ if (feof(fp)) break; /*Conteggio I caratteri fra 32 e 126*/ if (ch > 31 && ch < 127) count[ch]++; } /*visualizza i risultati*/ printf(\nCarattere\tConteggio);

Pagina 15 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


for (index = 32; index < 127; index++) printf(%c\t%d\n,index,count[index]); /*chiude il file e termina*/ fclose(fp); return 0; } int file_exists(char *filename) { /*La funzione restituisce TRUE se il file esiste, FALSE altrimenti*/ FILE *fp; if (((fp = fopen(filename,r)) != NULL) return 0; else { fclose(fp); return 1; } }

Corso di Programmazione A.A. 2011-12

Dispensa 16

Pagina 16 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1
17.1.1

Liste
Inserimento in testa

Considerando le seguenti strutture dati e variabili: struct cella{ char valore; struct cella *next; }; struct cella *testa = NULL; Dove cella la struttura che definisce come fatto ogni elemento della lista, testa il puntatore al primo elemento della lista (testa mantiene lindirizzo di memoria della prima cella in lista).

A testa

NULL

Attraverso una chiamata alla funzione malloc() allochiamo in memoria lo spazio necessario per mantenere la nuova cella da inserire allinterno della lista:

A testa

NULL

1
D nuovo NULL

nuovo = (struct cella*)malloc(sizeof(struct cella));

nuovo un puntatore ad un elemento di tipo struct cella allo stesso modo di testa, la funzione malloc() restituisce lindirizzo di memoria della prima cella allocata, tale indirizzo viene memorizzato allinterno della variabile (puntatore) nuovo.
Pagina 2 di 2

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

La prima operazione da compiere collegare la nuova cella alla prima della lista, quindi fare in modo che lelemento next della nuova cella prenda lo stesso valore di testa:

A testa

NULL

2
nuovo->next = testa;

D nuovo

NULL

La seconda operazione consiste nellassociare la testa al nuovo elemento creato:

A testa

NULL

3
testa = nuovo;

D nuovo
In modo da ottenere linserimento del nuovo elemento in testa:

4
testa

NULL

Pagina 3 di 3

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Se non si seguono esattamente nellordine indicato i passi indicati in precedenza e ad esempio come primo passo si associa la testa della lista al nuovo elemento:

A testa

NULL

ERRORE! se fatto come primo passo si perde il riferimento al resto della lista!

D nuovo
Si perde il riferimento al resto della lista quindi non sar possibile collegare il nuovo elemento con il resto della lista! Riassumendo il codice necessario per linserimento di un elemento in lista: nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->next = testa; testa = nuovo; Le istruzioni descritte funzionano anche considerando una lista vuota:

NULL testa

1
D nuovo NULL

nuovo = (struct cella*)malloc(sizeof(struct cella));

Pagina 4 di 4

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


NULL testa

Corso di Programmazione A.A. 2011-12

Dispensa 17

2
D nuovo NULL
nuovo->next = testa;

NULL testa

3
testa = nuovo;

D nuovo

4
testa

NULL

Pagina 5 di 5

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.2

Stampa degli elementi il lista

Considerando la struttura dati e le variabili definite nel punto precedente, la stampa consiste nel accedere ad ogni elemento della lista partendo dal primo elemento in testa.

1
testa

NULL

con testa->valore si propriet della prima cella

accede

alla

Per poter leggere i valori nella seconda cella bisogna accedere a tale cella:

NULL
testa = testa->next permette di passare alla cella successiva

testa

dopo aver eseguito testa = testa->next si ottiene:

NULL
ora con testa->valore possibile accede alla propriet della seconda cella

testa
si procede eseguendo nuovamente testa = testa->next per accedere alla terza cella:
Pagina 6 di 6

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


A B C NULL

Corso di Programmazione A.A. 2011-12

Dispensa 17

testa = testa->next permette di passare alla cella successiva

testa

NULL
ora con testa->valore possibile accede alla propriet della terza cella

testa
Ripetendo le operazioni viste nei punti precedenti si accede alla fine della lista:

NULL

testa

NULL

testa

Pagina 7 di 7

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Quindi la logica quella di accedere a tutti gli elementi della lista fino a quando non si arriva alla fine della lista e cio al valore NULL. Quindi riassumendo il codice potrebbe essere:

while (testa != NULL) { printf(%d, testa->valore); tesa = testa->next; } Procedendo in questo modo per dopo aver stampato la lista si perde il riferimento al primo elemento della lista, e quindi alla lista intera! Quindi prima di scorrere la lista bisognerebbe tener traccia del primo elemento attraverso un altro puntatore:

temp

1
testa

NULL

temp = testa;

temp testa

NULL

testa = temp->next;

temp

NULL

testa
Pagina 8 di 8

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

temp

NULL
testa = temp->next;

testa temp

NULL

testa

temp

NULL
testa = temp->next;

testa

temp

NULL

testa
Pagina 9 di 9

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

In questo modo arrivati alla fine della lista il riferimento al primo elemento non andato perso. Con listruzione temp = testa si riesce a ripristinare lo stato iniziale.

temp = testa; while (testa != NULL) { printf(%d, testa->valore); testa = testa->next; } testa = temp; E preferibile muoversi allinterno della lista (scorrere la lista) con un puntatore diverso dalla testa e non con il riferimento alla testa (cos si evita di dimenticarsi di tener traccia del primo elemento!!)

temp = testa; while (temp != NULL) { printf(%d, temp->valore); temp = temp ->next; } In questo modo dopo aver eseguito la stampa il riferimento alla testa non stato modificato!

Pagina 10 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.3

Ricerca di un elemento in lista

La ricerca simile alla stampa: bisogna scorrere la lista fino ad individuare lelemento cercato:

temp = testa; trovato = 0; while (temp != NULL) { if (temp->valore = valoreDaCercare) { trovato = 1; break; } temp = temp ->next; } if (trovato) printf(Il valore cercato presente in lista!); else printf(valore NON trovato); Allinterno dellanalisi della lista (dentro al ciclo while) attraverso un controllo con un istruzione if possibile valutare se la cella corrente quella cercata, la variabile trovato ci permette di sapere al di fuori del ciclo se lelemento cercato presente o meno allinterno della lista. Se entriamo dentro al corpo dellistruzione if la variabile trovato viene impostata al valore 1 altrimenti rimane settata al valore 0.

Pagina 11 di 11

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.4

Numero di occorrenze di un elemento in lista

Questa operazione simile a quella del punto precedente (in questo caso bisogna per contare quante volte un elemento ripetuto allinterno della lista):

temp = testa; conta = 0; while (temp != NULL) { if (temp->valore = valoreDaCercare) conta++; temp = temp ->next; } printf(Lelemento cercato presente %d volte!,conta);

17.1.5

Numero di elementi in lista

temp = testa; conta = 0; while (temp != NULL) { conta++; temp = temp ->next; } printf(La lista ha %d elementi!,conta);

Pagina 12 di 12

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.6

Inserimento di un nuovo elemento in coda alla lista

Per poter inserire un nuovo elemento in coda alla lista prima bisogna procedere a scorrere la lista fino ad arrivare allultimo elemento:

testa

NULL
temp = testa; while (temp->next != NULL) temp = temp ->next;

temp

Come negli esempi precedenti la lista viene scorsa attraverso un puntatore dappoggio (cursore) in modo da non perdere il riferimento al primo elemento

testa

NULL

temp NULL

D
attraverso la funzione malloc() viene creato il nuovo elemento: nuovo = (struct cella*)malloc(sizeof(struct cella));

nuovo

Pagina 13 di 13

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

testa

NULL

temp NULL

D
Il puntatore allelemento successivo dellultima cella viene fatto puntare alla nuova cella: temp->next = nuovo;

nuovo

Bisogna fare attenzione in quanto se la lista vuota non bisogna scorrere la lista ma semplicemente far puntare il puntatore alla testa al nuovo elemento. Il codice per questa funzionalit :

nuovo = (struct cella*)malloc(sizeof(struct cella)); if (testa == NULL) testa = nuovo; else { temp = testa; while (temp->next != NULL) temp = temp ->next; temp->next = nuovo; }

Pagina 14 di 14

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.7

Cancellazione dellelemento in testa alla lista

Prima di cancellare lelemento in testa, il riferimento alla testa della lista deve passare al secondo elemento per evitare di perdere lintera lista:

testa

NULL

temp

Viene utilizzato un puntatore temp per tener traccia del primo elemento prima di far puntare a testa allelemento successivo: temp = testa; testa = testa->next;

testa

NULL

temp
a questo punto possibile eliminare il primo elemento attraverso la funzione free():

3
temp

NULL

testa

free(temp);

Pagina 15 di 15

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.8

Cancellazione di un elemento in lista

Per cancellare un elemento dalla lista bisogna prima individuare lelemento allinterno della lista, quindi bisogna avviare una procedura di ricerca vista in precedenza per arrivare allelemento da eliminare.

testa

NULL

temp
Dopo aver individuato lelemento da eliminare (ad esempio la cella C considerando lesempio precedente), bisogna rimuoverlo dalla lista. Per eliminare correttamente lelemento senza alterare la catena che collega un elemento ad un altro, bisogna far in modo che lelemento che precede la cella da eliminare venga collegato allelemento che segue la cella da eliminare, quindi:

testa

NULL

temp
Per per poter fare questo necessario avere un riferimento allelemento precedente della cella da eliminare. Quindi oltre al cursore temp che ci consente di scorrere la lista necessario un ulteriore puntatore (ad esempio prec) che di volta in volta ci permetta di tener traccia dellelemento precedente di quello considerato. Supponiamo di dover eliminare la cella C, quindi dovremo scorrere la lista fino ad arrivare a tale cella.

Pagina 16 di 16

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


prec NULL testa NULL

Corso di Programmazione A.A. 2011-12

Dispensa 17

temp

Come prima operazione il cursore temp punta alla stessa cella della testa, ovviamente il precedente del primo elemento non esiste quindi il riferimento prec sar a NULL temp = testa; ptec = NULL;

prec

NULL

testa

NULL

temp

Prima di accedere allelemento successivo bisogna tener traccia dellelemento che si sta per lasciare: prec = temp;

Pagina 17 di 17

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

prec

testa

NULL

temp

Si procede passando allelemento successivo: temp = temp->next;

I punti 2 e 3 si ripetono fino ad arrivare allelemento da eliminare C:

prec

4-A

testa

NULL

temp

4-B

Pagina 18 di 18

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


La situazione prima di eliminare lelemento :

Corso di Programmazione A.A. 2011-12

Dispensa 17

prec

5
testa A B C
D

NULL

temp
Prima di eliminare lelemento C bisogna collegare la cella precedente (B) a quella successiva (D):

prec

6
testa A B C
D

NULL

temp
in modo da ottenere:

prec->next = temp->next;

prec

testa

NULL
Con la funzione free() eliminiamo la cella puntata da temp free(temp);

temp
Pagina 19 di 19

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi riassumendo il codice per eliminare un elemento dalla lista:

Corso di Programmazione A.A. 2011-12

Dispensa 17

prec = NULL; temp = testa; while (temp != NULL) { if (temp->valore == elementoDaEliminare) { prec->next = temp->next; free(temp); break; } prec = temp; temp = temp->next; } Bisogna fare attenzione in quanto se lelemento da eliminare il primo della lista, il codice riportato precedente genera la perdita del riferimento alla testa della lista:

prec testa

NULL NULL

temp
ERRORE! in questo modo si perde il riferimento alla testa della lista!

Pagina 20 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi il codice corretto diventa:

Corso di Programmazione A.A. 2011-12

Dispensa 17

prec = NULL; temp = testa; while (temp != NULL) { if (temp->valore == elementoDaEliminare) { if (prec == NULL) testa = testa->next; else prec->next = temp->next; free(temp); break; } prec = temp; temp = temp->next; } prima di eliminare un elemento bisogna controllare se questo lelemento in testa alla lista, in tal caso bisogna muovere il riferimento alla testa.

Pagina 21 di 21

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.9

Cancellare tutte le occorrenze di un elemento in lista

Questa funzionalit simile alla precedete solo che dopo aver eliminato la prima occorrenza dellelemento non bisogna uscire dal ciclo ma procedere alla ricerca di altre occorrenze:

prec = NULL; temp = testa; while (temp != NULL) { if (temp->valore == elementoDaEliminare) { if (prec == NULL) testa = testa->next; else prec->next = temp->next; elimina = temp; temp = temp->next; free(elimina); } else { prec = temp; temp = temp->next; } } Dato che bisogna procedere con lanalisi della lista non possibile eliminare a partire da temp in quanto verr utilizzato alliterazione successiva per considerare il nuovo elemento.

Pagina 22 di 22

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.10

Swap di due elementi in lista

Loperazione di swap consiste nello scambiare di posto due elementi allinterno della lista. Per eseguire questa operazione bisogna considerare diverse cose: individuare il primo elemento da spostare individuare il secondo elemento da spostare considerare cosa fare se uno dei due elementi il primo della lista considerare cosa fare se i due elementi sono consecutivi allinterno della lista (uno attaccato allaltro)

Ovviamente in base a quanto detto prima le operazioni da implementare possono essere differenti. Per individuare gli elementi da spostare bisogna implementare una ricerca allinterno della lista per ottenere i riferimenti necessari rispettivamente al primo e al secondo elemento (si ripete 2 volte la ricerca vista nei punti precedenti). Come per la cancellazione di un elemento servir un puntatore allelemento precedente per poter sistemare a modo tutti i riferimenti. Considerando che nessuno dei due elementi sia in testa alla lista e che gli elementi non siano consecutivi:

precPrimo testa

precSecondo NULL

primo

secondo

i puntatori primo e secondo si riferiscono rispettivamente alla prima e alla seconda cella da invertire, mentre precPrimo e precSecondo sono puntatori agli elementi che precedono primo e secondo.

Pagina 23 di 23

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Dopo loperazione di swap, la lista dovrebbe diventare (usando qualche puntatore dappoggio):

Corso di Programmazione A.A. 2011-12

Dispensa 17

4 1

precPrimo testa

precSecondo NULL

primo

secondo

3 riorganizzando un po:

precPrimo testa

precSecondo NULL

secondo

primo

Vediamo ora il dettaglio dei passi per arrivare a quanto visto sopra.

Pagina 24 di 24

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

precPrimo testa

precSecondo NULL

primo

secondo
precPrimo->next = secondo;

2
precPrimo testa precSecondo NULL

primo

secondo

precSecondo->next = primo;

Pagina 25 di 25

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

3
precPrimo testa temp precSecondo NULL

primo

secondo

Per evitare di perdere il riferimento allelemento successivo di primo, prima di spostare il riferimento next di primo salviamo tale valore in una variabile dappoggio temp temp = primo->next; primo->next = secondo->next;

precPrimo testa

temp

precSecondo NULL

primo

secondo

Pagina 26 di 26

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

precPrimo testa

temp

precSecondo NULL

primo

secondo

secondo->next = temp;

precPrimo testa

precSecondo NULL

primo

secondo

Pagina 27 di 27

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Come descritto in precedenza troveremo delle differenze nel caso in cui uno dei due elementi da spostare in testa alla lista, in quanto parte delle operazioni riguarderanno il riferimento alla testa della lista:

precPrimo

NULL
1

precSecondo testa NULL

primo

secondo

avremo quindi:

precSecondo testa NULL

primo

secondo

Pagina 28 di 28

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Un altro caso particolare lo troveremo quando gli elementi da invertire sono uno di seguito allaltro:

Corso di Programmazione A.A. 2011-12

Dispensa 17

precPrimo testa

precSecondo

NULL

primo

secondo

avremo quindi:

precPrimo testa

precSecondo

NULL

primo

secondo

Pagina 29 di 29

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Riassumendo il codice necessario per lo swap di due elementi in lista, considerando che primo sia il puntatore al primo elemento, secondo sia il puntatore al secondo elemento, precPrimo sia il puntatore allelemento precedente del primo e sia impostato a NULL nel caso tale elemento sia in testa, precSecondo sia il puntatore allelemento che precede secondo ed eventualmente sia uguale a primo nel caso in cui i due elementi siano consecutivi:

if (precPrimo == NULL) //il primo elemento in testa testa = secondo; else precPrimo->next = secondo; if (precSecondo->next == primo) //se due elementi sono consecutivi temp = primo; else { temp = primo->next; precSecondo->next = primo; } primo->next = secondo->next; secondo->next = temp;

Pagina 30 di 30

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

17.1.11

Inserimento di un nuovo elemento attraverso una funzione

Proviamo a scrivere una funzione che ci consenta di inserire un nuovo elemento allinterno della lista, considerando tutte le variabili locali (anticipiamo che questo primo esempio sbagliato! spiegheremo poi il perch). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> #include <stdlib.h> struct cella{ char valore; struct cella *next; }; void inserisci(struct cella *t, char val); int main() { struct cella *testa = NULL; //...altre operazioni inserisci(testa,D); //...altre operazioni return 0; } void inserisci(struct cella *t, char val) { struct cella *nuovo; nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->valore = v; nuovo->next = t; t = nuovo; }

Pagina 31 di 31

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Prima della chiamata allinterno del main() della funzione inserisci, la lista sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

Cerchiamo di capire dove si trova lerrore analizzando, tra le altre cose, quello che accade in memoria mentre le varie istruzioni vengono eseguite.

A testa
in memoria quindi potremo avere la seguente situaione:

NULL

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014 1015

testa

Quando la funzione inserisci viene chiamata (riga 17) allinterno del main(), il controllo viene ceduto dal chiamante (main) alla funzione chiamata (inserisci), tutte le variabili della funzione inserisci() vengono allocate in memoria (incluse quelle presenti allinterno dellintestazione).

Pagina 32 di 32

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi la situazione in memoria sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014 1015

testa

val

nuovo

I parametri passati alla funzione verranno copiati nelle variabili presenti nellintestazione nellordine in cui sono stati inseriti, quindi t prender il valore presente allinterno di testa e val il carattere D (riga 24 e 26):

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1003 1011 1012 1013 1014 1015

testa
Pagina 33 di 33

val

nuovo

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Attraverso la funzione malloc() verr allocata la nuova cella in memoria, lindirizzo del primo byte allocato verr restituito dalla funzione malloc() e inserito (riga 28) allinterno della variabile nuovo:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1003 1011 1012 1015 1013 1014 1015

testa

val

nuovo

Il valore presente allinterno della variabile val viene copiato allinterno dellelemento valore della nuova cella creata, referenziata dal puntatore nuovo (riga 29):

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1003 1011 1012 1015 1013 1014 D 1015

testa
Pagina 34 di 34

val

nuovo

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi la situazione a livello logico sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

testa A t NULL B C NULL

D nuovo

Pagina 35 di 35

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Come descritto nel paragrafo 16.1.1 la prima operazione da implementare per inserire lelemento in lista consister nel far puntare il puntatore next di della nuova cella a quello che punta la testa (nuovo->next = t):

testa A t NULL B C NULL

D nuovo
Quindi in memoria avremo:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1003 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

Pagina 36 di 36

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Come descritto nel paragrafo 16.1.1 la seconda operazione da fare consiste nello spostare il riferimento della testa, nel nostro caso t dato che siamo allinterno della funzione (t = nuovo):

testa A t B C NULL

D nuovo
Quindi in memoria avremo:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1015 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

Pagina 37 di 37

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Riassumendo in memoria avremo la seguente situazione:

Corso di Programmazione A.A. 2011-12

Dispensa 17

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1015 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

testa A t B C NULL

D nuovo

Pagina 38 di 38

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Quando la funzione termina tutte le variabili locali vengono eliminate dalla memoria, il controllo viene restituito al chiamante (funzione main()) in memoria avremo la seguente situazione:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014

D next: 1003 1015

testa

A livello logico:

testa A B C NULL

Quindi tutte le modifiche fatte alla testa della lista allinterno della funzione non vengono viste allesterno!
Pagina 39 di 39

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Modifichiamo il codice descritto allinizio paragrafo (in rosso le modifiche). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> #include <stdlib.h> struct cella{ char valore; struct cella *next; }; void inserisci(struct cella **t, char val); int main() { struct cella *testa = NULL; //...altre operazioni inserisci(&testa,D); //...altre operazioni return 0; } void inserisci(struct cella **t, char val) { struct cella *nuovo; nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->valore = v; nuovo->next = *t; *t = nuovo; }

Corso di Programmazione A.A. 2011-12

Dispensa 17

La funzione inserisci() riceve ora come primo parametro un puntatore di puntatore cio una variabile che al suo interno mantiene lindirizzo di memoria di unaltra variabile che a sua volta contiene un indirizzo di memoria.

Pagina 40 di 40

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Prima della chiamata allinterno del main() della funzione inserisci, la lista sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

A testa

NULL

in memoria quindi potremo avere la seguente situaione:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014 1015

testa

Quando la funzione inserisci viene chiamata (riga 17) allinterno del main(), il controllo viene ceduto dal chiamante (main) alla funzione chiamata (inserisci), tutte le variabili della funzione inserisci() vengono allocate in memoria (incluse quelle presenti allinterno dellintestazione).

Pagina 41 di 41

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi la situazione in memoria sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014 1015

testa

val

nuovo

I parametri passati alla funzione verranno copiati nelle variabili presenti nellintestazione nellordine in cui sono stati inseriti, quindi t prender lindirizzo di memoria della prima variabile e quindi di testa testa e val il carattere D (riga 24 e 26):

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1013 1014 1015

testa
Pagina 42 di 42

val

nuovo

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Attraverso la funzione malloc() verr allocata la nuova cella in memoria, lindirizzo del primo byte allocato verr restituito dalla funzione malloc() e inserito (riga 28) allinterno della variabile nuovo:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014 1015

testa

val

nuovo

Il valore presente allinterno della variabile val viene copiato allinterno dellelemento valore della nuova cella creata, referenziata dal puntatore nuovo (riga 29):

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014 D 1015

testa
Pagina 43 di 43

val

nuovo

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi la situazione a livello logico sar:

Corso di Programmazione A.A. 2011-12

Dispensa 17

testa A B C NULL

*t D nuovo NULL

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014 D 1015

testa

*t

val

nuovo

con t si ottiene il valore 1001 o meglio lindirizzo di memoria che contiene il valore della variabile (puntatore) testa, mentre con *t si accede al contenuto dellarea di memoria referenziata dallindirizzo 1001.

Pagina 44 di 44

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Come descritto nel paragrafo 16.1.1 la prima operazione da implementare per inserire lelemento in lista consister nel far puntare il puntatore next di della nuova cella a quello che punta la testa (nuovo->next = *t):

testa A *t D nuovo NULL B C NULL

Quindi in memoria avremo:

A 1003 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

Pagina 45 di 45

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Come descritto nel paragrafo 16.1.1 la seconda operazione da fare consiste nello spostare il riferimento della testa, nel nostro caso t dato che siamo allinterno della funzione (*t = nuovo):

testa A *t D nuovo
NB. modificando *t modifichiamo anche testa! Quindi in memoria avremo:

NULL

A 1015 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

Pagina 46 di 46

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Riassumendo in memoria avremo la seguente situazione:

Corso di Programmazione A.A. 2011-12

Dispensa 17

A 1015 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 D 1010 1001 1011 1012 1015 1013 1014

D next: 1003 1015

testa

val

nuovo

testa A B C NULL

t D nuovo

Pagina 47 di 47

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 17

Quando la funzione termina tutte le variabili locali vengono eliminate dalla memoria, il controllo viene restituito al chiamante (funzione main()) in memoria avremo la seguente situazione:

A 1015 1001 1002 next: 1008 1003 1004

C next: NULL 1005 1006 1007

B next: 1005 1008 1008 1010 1011 1012 1013 1014

D next: 1003 1015

testa

A livello logico:

testa

NULL

Quindi tutte le modifiche fatte alla testa della lista in questo caso rimarranno!
Pagina 48 di 48

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Una versione ulteriore del programma corretto potrebbe essere (in rosso le modifiche rispetto alla prima versione errata): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> #include <stdlib.h> struct cella{ char valore; struct cella *next; }; struct cella* inserisci(struct cella *t, char val); int main() { struct cella *testa = NULL; //...altre operazioni testa = inserisci(testa,D); //...altre operazioni return 0; } struct cella* inserisci(struct cella *t, char val) { struct cella *nuovo; nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->valore = v; nuovo->next = t; t = nuovo; return t; }

Corso di Programmazione A.A. 2011-12

Dispensa 17

In questo caso la funzione inserisci restituisce un valore, e cio la testa della lista modificata (riga 32). Quando la funzione termina il valore restituito viene inserito allinterno della variabile testa (riga 17) in modo da mantenere aggiornate le modifiche fatte. Questa versione equivalente alla precedente.
Pagina 49 di 49

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1

Liste con doppi puntatori

In una lista con doppi puntatori ogni cella ha un riferimento allelemento successivo e uno allelemento precedente: struct cella{ char valore; struct cella *next; struct cella *prev; };

//puntatore allelemento successivo //puntatore allelemento precedente

NULL testa

NULL

18.1.1

Inserimento in testa

Considerando le seguenti strutture dati e variabili: struct cella{ char valore; struct cella *next; struct cella *prev; };

//puntatore allelemento successivo //puntatore allelemento precedente

struct cella *testa = NULL; Dove cella la struttura che definisce come fatto ogni elemento della lista, testa il puntatore al primo elemento della lista (testa mantiene lindirizzo di memoria della prima cella in lista).

Pagina 2 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

NULL testa

NULL

Attraverso una chiamata alla funzione malloc() allochiamo in memoria lo spazio necessario per mantenere la nuova cella da inserire allinterno della lista:

NULL

NULL

testa
nuovo = (struct cella*)malloc(sizeof(struct cella));

NULL D nuovo
nuovo un puntatore ad un elemento di tipo struct cella allo stesso modo di testa, la funzione malloc() restituisce lindirizzo di memoria della prima cella allocata, tale indirizzo viene memorizzato allinterno della variabile (puntatore) nuovo. La prima operazione da compiere collegare la nuova cella alla prima della lista, quindi fare in modo che lelemento next della nuova cella prenda lo stesso valore di testa:

NULL

NULL

NULL

testa

NULL D nuovo NULL

nuovo->next = testa;

Pagina 3 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


La seconda operazione consiste nellassociare il puntatore alla cella precedente prev dellelemento che in testa al nuovo elemento creato:

Corso di Programmazione A.A. 2011-12

Dispensa 18

NULL

NULL

testa

NULL D nuovo

testa->prev = nuovo;

Lultima operazione consiste nello spostare il riferimento della testa al nuovo elemento:

NULL

testa

NULL D nuovo
In modo da ottenere linserimento del nuovo elemento in testa:

testa = nuovo;

NULL testa

NULL

Pagina 4 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

ATTENZIONE! Se la lista vuota al primo inserimento NON sar possibile eseguire testa->prev = nuovo in quanto NON esiste una cella in testa, quindi prima di utilizzare il puntatore allelemento precedente bisogner controllare che sia presente almeno un elemento allinterno della lista! Nel caso di lista vuota lunica operazione da fare per linserimento risulter:

NULL testa

1
NULL D nuovo NULL

NULL testa

2
NULL D nuovo
Riassumendo il codice necessario per linserimento di un elemento in lista: nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->prev = NULL; nuovo->next = testa; if (testa != NULL) testa->prev = nuovo; testa = nuovo;
Pagina 5 di 20

NULL

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.2

Stampa degli elementi il lista con doppi puntatori

Considerando la struttura dati e le variabili definite nel punto precedente, la stampa consiste nel accedere ad ogni elemento della lista partendo dal primo elemento in testa. Le operazioni per stampare una lista con doppi puntatori sono le stesse viste con le liste semplici:

NULL testa

NULL

con testa->valore si propriet della prima cella

accede

alla

Per poter leggere i valori nella seconda cella bisogna accedere a tale cella:

NULL testa

NULL

testa = testa->next permette di passare alla cella successiva

dopo aver eseguito testa = testa->next si ottiene:

NULL

NULL

ora con testa->valore possibile accede alla propriet della seconda cella

testa
Pagina 6 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


si procede eseguendo nuovamente testa = testa->next per accedere alla terza cella:

Corso di Programmazione A.A. 2011-12

Dispensa 18

NULL

NULL

testa = testa->next permette di passare alla cella successiva

testa

NULL

NULL

ora con testa->valore possibile accede alla propriet della terza cella

testa
Ripetendo le operazioni viste nei punti precedenti si accede alla fine della lista:

NULL

NULL

testa

NULL

NULL

testa
Pagina 7 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

Quindi la logica quella di accedere a tutti gli elementi della lista fino a quando non si arriva alla fine della lista e cio al valore NULL. Quindi riassumendo il codice potrebbe essere:

while (testa != NULL) { printf(%d, testa->valore); tesa = testa->next; } Procedendo in questo modo per dopo aver stampato la lista si perde il riferimento al primo elemento della lista, e quindi alla lista intera! Quindi, come per le liste semplici prima di scorrere la lista bisognerebbe tener traccia del primo elemento attraverso un altro puntatore: temp = testa; while (testa != NULL) { printf(%d, testa->valore); testa = testa->next; } testa = temp; In questo modo arrivati alla fine della lista il riferimento al primo elemento non andato perso. Con listruzione temp = testa si riesce a ripristinare lo stato iniziale. E preferibile muoversi allinterno della lista (scorrere la lista) con un puntatore diverso dalla testa e non con il riferimento alla testa (cos si evita di dimenticarsi di tener traccia del primo elemento!!) temp = testa; while (temp != NULL) { printf(%d, temp->valore); temp = temp ->next; } In questo modo dopo aver eseguito la stampa il riferimento alla testa non stato modificato!
Pagina 8 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.3

Ricerca di un elemento in lista con doppi puntatori

La ricerca simile alla stampa: bisogna scorrere la lista fino ad individuare lelemento cercato:

temp = testa; trovato = 0; while (temp != NULL) { if (temp->valore = valoreDaCercare) { trovato = 1; break; } temp = temp ->next; } if (trovato) printf(Il valore cercato presente in lista!); else printf(valore NON trovato); Allinterno dellanalisi della lista (dentro al ciclo while) attraverso un controllo con un istruzione if possibile valutare se la cella corrente quella cercata, la variabile trovato ci permette di sapere al di fuori del ciclo se lelemento cercato presente o meno allinterno della lista. Se entriamo dentro al corpo dellistruzione if la variabile trovato viene impostata al valore 1 altrimenti rimane settata al valore 0. ANCHE QUESTA OPERAZIONE E UGUALE ALLE LISTE SEMPLICI!

Pagina 9 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.4

Numero di occorrenze di un elemento in lista con doppi puntatori

Questa operazione simile a quella del punto precedente (in questo caso bisogna per contare quante volte un elemento ripetuto allinterno della lista):

temp = testa; conta = 0; while (temp != NULL) { if (temp->valore = valoreDaCercare) conta++; temp = temp ->next; } printf(Lelemento cercato presente %d volte!,conta); ANCHE QUESTA OPERAZIONE E UGUALE ALLE LISTE SEMPLICI!

18.1.5

Numero di elementi in lista con doppi puntatori

temp = testa; conta = 0; while (temp != NULL) { conta++; temp = temp ->next; } printf(La lista ha %d elementi!,conta); ANCHE QUESTA OPERAZIONE E UGUALE ALLE LISTE SEMPLICI!

Pagina 10 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.6

Inserimento di un nuovo elemento in coda alla lista con doppi puntatori

Per poter inserire un nuovo elemento in coda alla lista prima bisogna procedere a scorrere la lista fino ad arrivare allultimo elemento (come con le liste semplici):

NULL testa

NULL
temp = testa; while (temp->next != NULL) temp = temp ->next;

temp

Come negli esempi precedenti la lista viene scorsa attraverso un puntatore dappoggio (cursore) in modo da non perdere il riferimento al primo elemento

NULL testa

NULL

temp

NULL
attraverso la funzione malloc() viene creato il nuovo elemento: nuovo = (struct cella*)malloc(sizeof(struct cella));
Pagina 11 di 20

D nuovo

NULL

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

NULL testa

NULL

temp

NULL nuovo
Il puntatore allelemento successivo dellultima cella viene fatto puntare alla nuova cella: temp->next = nuovo;

NULL

NULL testa

temp

NULL nuovo
Il puntatore allelemento precendente della nuova cella deve puntare allelemento riferito da temp: nuovo->prev = temp;
Pagina 12 di 20

NULL

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

Bisogna fare attenzione in quanto se la lista vuota non bisogna scorrere la lista ma semplicemente far puntare il puntatore alla testa al nuovo elemento. Il codice per questa funzionalit :

nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->next = NULL; nuovo->prev = NULL; if (testa == NULL) testa = nuovo; else { temp = testa; while (temp->next != NULL) temp = temp ->next; temp->next = nuovo; nuovo->prev = temp; }

Pagina 13 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.7

Cancellazione dellelemento in testa alla lista con doppi puntatori

Prima di cancellare lelemento in testa, il riferimento alla testa della lista deve passare al secondo elemento per evitare di perdere lintera lista:

temp

1
NULL testa A B C NULL
Viene utilizzato un puntatore temp per tener traccia del primo elemento prima di far puntare a testa allelemento successivo: temp = testa; testa = testa->next;

temp

NULL testa

NULL

temp

NULL

La seconda cella deve essere staccata dalla prima quindi il puntatore allelemento precedente prev dovr puntare a NULL testa->prev = NULL; questa operazione ovviamente dovr essere fatta NULL solo se la lista ha almeno 2 elementi e quindi se esiste la cella successiva a quella che deve essere eliminata if (testa != NULL) testa->prev = NULL;

NULL

Pagina 14 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


testa
a questo punto possibile eliminare il primo elemento attraverso la funzione free():

Corso di Programmazione A.A. 2011-12

Dispensa 18

temp

NULL

4
NULL A B C NULL
free(temp);

testa

Riassumendo le operazioni per eliminare lelemento in testa alla lista saranno:

if (testa != NULL) // se esiste almeno un elemento { temp = testa; testa = testa->next; if (testa != NULL) testa->prev = NULL; free(temp); }

Pagina 15 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.8

Cancellazione di un elemento in lista con doppi puntatori

Per cancellare un elemento dalla lista bisogna prima individuare lelemento allinterno della lista, quindi bisogna avviare una procedura di ricerca vista in precedenza per arrivare allelemento da eliminare.

NULL testa

NULL

temp
Dopo aver individuato lelemento da eliminare (ad esempio la cella C considerando lesempio precedente), bisogna rimuoverlo dalla lista. Per eliminare correttamente lelemento senza alterare la catena che collega un elemento ad un altro, bisogna far in modo che lelemento che precede la cella da eliminare venga collegato allelemento che segue la cella da eliminare, quindi:

NULL testa

B temp

NULL

Inoltre dovremo fare in modo che lelemento che segue la cella da eliminare deve essere collegato allelemento che la precede:

NULL testa

B temp

NULL

Pagina 16 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

Al contrario delle liste semplici, con le liste con doppi puntatori non avremo bisogno di un secondo puntatore prec per gestire le operazioni dette prima in quanto a partire dal riferimento tesmp (puntatore alla cella da eliminare) riusciremo ad accedere sia alla cella che precede sia alla cella che segue quella da eliminare Supponiamo di dover eliminare la cella C, quindi dovremo scorrere la lista fino ad arrivare a tale cella (come visto nei paragrafi precedenti per la ricerca d di un elemento allinterno della lista).

NULL testa

NULL

temp
Dobbiamo fare in modo che lelemento che precede la cella da eliminare venga collegato con lelemento che segue la cella da eliminare, quindi considerando il disegno riportato sopra, la cella B deve essere collegata con la cella D e quindi il riferimento next della cella B deve puntare alla cella D:

NULL testa

B temp

NULL

A partire da temp riusciamo ad accedere alla cella precedente con temp->prev (cella B) e a partire da tale riferimento modifichiamo il puntatore next facendolo puntare alla cella che segue quella da eliminare temp->prev->next = temp->next;

Pagina 17 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

NULL testa

B temp

NULL

A partire da temp riusciamo ad accedere alla cella successiva con temp->next (cella D) e a partire da tale riferimento modifichiamo il puntatore prev facendolo puntare alla cella che precede quella da eliminare temp->next->prev = temp->prev;

in modo da ottenere:

NULL testa

B temp

NULL

ATTENZIONE! Prima di eseguire: temp->prev->next = temp->next bisogna controllare che esista la cella precedente Stesso discorso per: temp->next->prev = temp->prev bisogna controllare che esista la cella che segue

Pagina 18 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi il codice diventa:

Corso di Programmazione A.A. 2011-12

Dispensa 18

temp = testa; while (temp != NULL) //scorrimento della lista { if (temp->valore == elementoDaEliminare) { if (temp->prev == NULL) //lelemento in testa alla lista! testa = testa->next; else temp->prev->next = temp->next; if (temp->next != NULL) temp->next->prev = temp->prev; free(temp); break; } temp = temp->next; } prima di eliminare un elemento bisogna controllare se questo lelemento in testa alla lista, in tal caso bisogna muovere il riferimento alla testa come visto nel paragrafo precedente.

Pagina 19 di 20

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 18

18.1.9

Cancellare tutte le occorrenze di un elemento in lista con doppi puntatori

Questa funzionalit simile alla precedete solo che dopo aver eliminato la prima occorrenza dellelemento non bisogna uscire dal ciclo ma procedere alla ricerca di altre occorrenze:

temp = testa; while (temp != NULL) //scorrimento della lista { if (temp->valore == elementoDaEliminare) { if (temp->prev == NULL) //lelemento in testa alla lista! testa = testa->next; else temp->prev->next = temp->next; if (temp->next != NULL) temp->next->prev = temp->prev; elimina = temp; temp = temp->next; free(elimina); continue; //per ripartire dal while } temp = temp->next; } Dato che bisogna procedere con lanalisi della lista non possibile eliminare a partire da temp in quanto verr utilizzato alliterazione successiva per considerare il nuovo elemento.

Pagina 20 di 20

CORSO DI and Split Unregistered TECNOLOGIE INFORMATICHE Simpo PDF MergeLAUREA IN SCIENZE EVersion - http://www.simpopdf.com CESENA

CORSO DI PROGRAMMAZIONE
A.A. 2011-12

Dispensa 19
Laboratorio

Dott. Mirko Ravaioli e-mail: mirko.ravaioli@unibo.it http://www.programmazione.info

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 19

19.1

Liste con priorit o lista ordinata

Le liste con priorit (o lista ordinata) sono gestite come le liste semplici viste nella dispensa 17, solo che gli elementi allinterno della lista vengono mantenuti ordinati rispetto un criterio gestito direttamente in fase di inserimento di un nuovo elemento. Immaginiamo per esempio lufficio accettazione di un pronto soccorso, i vari malati vengono messi in fila per la visita in ordine di arrivo e soprattutto in ordine di gravit, quindi un malato grave sicuramente verr visitato prima di uno meno grave anche se arrivato prima al pronto soccorso. La struttura dati necessaria per gestire la lista con priorit la stessa vista per le liste semplici: struct cella{ int ordine; struct cella *next; };

//puntatore allelemento successivo

struct cella *testa = NULL; Consideriamo nel nostro esempio un valore interno allinterno di ogni cella per gestire la priorit (o lordine). Lobiettivo quello di mantenere la lista ordinata in maniera crescente ad ogni inserimento. Il caso pi semplice si verifica quando la lista vuota, ovviamente il nuovo elemento da inserire indipendentemente dal valore della priorit verr inserito in testa:

1
testa NULL

2
testa NULL 7 nuovo
nuovo = (struct cella*)malloc(sizeof(struct cella));
Pagina 2 di 10

3
testa NULL NULL nuovo
testa = nuovo;

NULL

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Consideriamo una lista con vari elementi:

Corso di Programmazione A.A. 2011-12

Dispensa 19

3 testa

5 3

10

NULL

Se il nuovo elemento da inserire ha come ordine il numero 7

7 nuovo
Ovviamente dovrebbe essere inserito tra la cella con il valore 5 e la cella con il valore 8:

NULL

3 testa

5 3

10

NULL

Per inserire la nuova cella nella posizione giusta dovremo scorrere la lista fino ad arrivare alla prima cella con valore maggiore rispetto quella che deve essere inserita. Dato che la lista ordinata in maniera crescente, il primo elemento maggiore sar anche quello che dovr stare immediatamente dopo al nuovo elemento dopo linserimento.

nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->valore = valoreDaInserire; if (testa == NULL) //lista vuota testa = nuovo; else { prec = NULL; temp = testa; while (temp != NULL)
Pagina 3 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


{ if (temp->valore > nuovo->valore) //trovata la cella con valore pi grande { nuovo->next = temp; if (prec == NULL) // se lelemento da inserire in testa testa = nuovo; else prec->next = nuovo; break; } prec = temp; temp = temp->next; } }

Corso di Programmazione A.A. 2011-12

Dispensa 19

Quando si arriva allinterno del corpo dellistruzione if allinterno del while (quindi quando viene trovata la cella con valore pi alto rispetto a quella da inserire), la situazione sar la seguente:

prec

temp

3 testa

5 3

10

NULL

7 nuovo

NULL

Pagina 4 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 2


prec temp

Corso di Programmazione A.A. 2011-12

Dispensa 19

3 testa

5 3

10

NULL

7 nuovo

NULL

prec

temp

3 testa

5 3

10

NULL

7 nuovo

4
3 testa 5 3 8 10 NULL

7
Pagina 5 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Se lelemento deve essere inserito in testa:

Corso di Programmazione A.A. 2011-12

Dispensa 19

prec

NULL

temp

3 testa

5 3

10

NULL

2 nuovo

NULL

prec

NULL

temp

3 testa

5 3

10

NULL

2 nuovo

NULL

Pagina 6 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 3

Corso di Programmazione A.A. 2011-12

Dispensa 19

prec

NULL

temp

3 testa

5 3

10

NULL

2 nuovo

4
3 testa 5 3 8 10 NULL

2
Se lelemento deve essere inserito in coda, lalgoritmo descritto sopra non funziona in quanto allinterno del ciclo while non si trover mai una cella pi grande rispetto a quella da inserire:

temp 3 testa 5 3 8 10 NULL

NULL

prec 13 nuovo
Pagina 7 di 10

NULL

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com


Quindi dopo il ciclo bisogner fare un controllo per capire se lelemento non stato inserito e quindi ci troviamo in fondo alla lista:

Corso di Programmazione A.A. 2011-12

Dispensa 19

nuovo = (struct cella*)malloc(sizeof(struct cella)); nuovo->valore = valoreDaInserire; if (testa == NULL) //lista vuota testa = nuovo; else { prec = NULL; temp = testa; while (temp != NULL) { if (temp->valore > nuovo->valore) //trovata la cella con valore pi grande { nuovo->next = temp; if (prec == NULL) // se lelemento da inserire in testa testa = nuovo; else prec->next = nuovo; break; } prec = temp; temp = temp->next; } if (temp == NULL) //il nuovo elemento non stato inserito e siamo in fondo alla lista prec->next = nuovo; }

temp 3 testa 5 3 8 10 NULL

NULL

prec 13
Pagina 8 di 10

NULL

nuovo

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 19

Considerando lultimo algoritmo, nel caso in cui il valore della nuova cella da inserire sia gi presente allinterno della lista, la nuova cella viene inserita dopo quella con il valore uguale in quanto allinterno del ciclo si cerca la cella con un valore maggiore rispetto a quella da inserire:

1
3 testa

prec

temp

5 3

10

NULL

5 nuovo

NULL

prec

temp

3 testa

5 3

10 NULL

5 nuovo

3
3 testa 5 3 8 10 NULL

5
Pagina 9 di 10

Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com

Corso di Programmazione A.A. 2011-12

Dispensa 19

Se invece la nuova cella deve essere inserita prima di quella con il valore uguale, lunica modifica da fare nellalgoritmo nellespressione presente nellistruzione if nel corpo del ciclo while: sostituire il simbolo maggiore (>) con maggiore uguale (>=) Nel caso in cui la lista deve essere ordinata in modo decrescente basta inserire, sempre nel controllo dellistruzione if il minore (<) o minore uguale, il resto rimane invariato! Tutte le altre operazioni che possono essere eseguite sulla lista ordinata sono fatte allo stesso modo delle liste semplici come descritto nella dispensa 17.

Pagina 10 di 10

Potrebbero piacerti anche