Il linguaggio C Danilo Ardagna Politecnico di Milano 14-3-2014
Introduzione! Linguaggio di programmazione ad alto livello (HLL)! Sviluppato negli anni 70 (C standard ANSI)! Molto diffuso e adatto ad un ampio spettro di applicazioni:! Scientifiche! Gestionali! Industriali, es. acquisizione dei dati, controllo dei processi! Informatiche, es. sw di base (SO Unix), strumenti (CAD), pacchetti applicativi 2
Linguaggio ad alto livello! Elementi: alfabeto o vocabolario del linguaggio! Sintassi: insieme di regole con cui si compongono gli elementi per costruire frasi eseguibili (istruzioni)! Semantica: significato egli elementi, delle frasi e dell intero programma! Regole sintattiche: devono essere univoche sulla composizione delle frasi! Deve essere possibile determinare in modo automatico e con certezza se una frase è sintatticamente corretta! Correttezza sintattica: condizione necessaria per la corretta esecuzione del programma! Solo programmi corretti possono essere tradotti correttamente dal compilatore 3
Linguaggio ad alto livello! Errori di un programma! Sintattici: rilevati durante la compilazione, la descrizione dell algoritmo non rispetta le regole con cui è possibile comporre le frasi del linguaggio! Di esecuzione: rilevati run-time, il programma commette operazioni illecite (violazione di accesso in memoria, overflow, indici non validi, ecc.) 4
Alcune proprietà del C! È un linguaggio dichiarativo:! L intenzione di utilizzare una variabile o una funzione deve essere dichiarata a priori! È un linguaggio strutturato! Fornisce i costrutti base per la definizione di sequenza, selezione ed iterazione! È un linguaggio case sensitive:! Discrimina tra caratteri maiuscoli e minuscole! e.g., Main è diverso da main!! 5
Struttura di un programma C! Un programma scritto in C è un file di testo strutturato in modo opportuno! Il programma scritto in modo corretto può essere compilato: tradotto in linguaggio macchina! Le fasi di compilazione ed esecuzione saranno approfondite durante le ore di laboratorio! Imparare a programmare in C significa impararne la sintassi e la semantica per fare in modo che esso:! Sia compilabile (verifica sintattica)! Si comporti come atteso (verifica semantica) 6
Elementi del linguaggio C! Parole chiave (o riservate): proprie del linguaggio! Istruzioni oppure parole con un significato particolare (tipi)! Identificatori: costituiti da sequenze di caratteri alfanumerici! Nomi di variabili, costanti, tipi definiti dall utente, funzioni, procedure! Definiti dall utente, oppure di sistema (contenuti in librerie) 7
Elementi del linguaggio C! Operatori (unari o binari)! Assegnamento =! Aritmetici!+ - * /! Relazionali (confronto) > < <= ==!=! Logici! &&! Altri! Separatori (delimitatori)! di identificatori di variabili e costanti,! di istruzioni ;! di commento /*... */! di blocco di istruzioni {...}! in espressioni (...) 8
Elementi del linguaggio C! Direttive al preprocessore C! #include (parola chiave)! Valori costanti (cifre o caratteri) 9
Hello world! Il codice inizia qui #include <stdio.h> Parte dichiarativa L esecuzione inizia qui main() { printf( Hello World! ); Parte esecutiva } 10
Direttiva include! Le direttive:! sono istruzioni particolari rivolte al compilatore! iniziano con il carattere #! La direttiva include indica l intenzione di utilizzo di una libreria esterna! Esempio! #include <stdio.h> richiede la libreria di gestione degli input e degli output (e.g., printf() e scanf(), )! #include <math.h> richiede la libreria con le principali funzioni matematiche (e.g., abs(), sin(), )! Una volta inclusa una libreria è possibile utilizzare tutte le funzioni definite al loro interno 11
main! È una parola chiave e indica l inizio del programma vero e proprio! Le istruzioni che seguono saranno quelle effettivamente eseguite dall elaboratore! Le istruzioni che compongono il programma principale (il main appunto) sono racchiuse all interno di parentesi graffe { } 12
printf( Hello world! );! È una tipica istruzione! Come tutte le istruzioni deve terminare con il carattere ; (punto e virgola)! Permette la stampa a video dei caratteri racchiusi tra apici e inseriti all interno delle parentesi tonde 13
Risultato Hello World! 14
Esempio #include <stdio.h> main() { printf( 1 Overture ); printf( 2 1921 ); } 1 Overture2 1921 15
Esempio #include <stdio.h> main() { printf( 1 Overture\n ); printf( 2 1921\n ); } 1 Overture 2 1921 16
La macchina astratta del C BUS UC x a pippo MEM. Standard input Standard output 17
Standard I/O! Un programma C ha due periferiche standard di ingresso e uscita! Standard input (tastiera)! Standard output (video) che possono essere viste come sequenze di celle elementari analoghe alla memoria 18
Memoria! Divisa in celle elementari VARIABILI! Ogni cella può contenere un dato! I dati possono essere! Numeri! Caratteri! Stringhe (successioni finite di caratteri, immagazzinati in celle consecutive) 19
Memoria! Semplificazioni/idealizzazioni (astrazioni)! nessun limite al numero delle celle di memoria disponibili Rappresentazione in memoria di una variabile A indirizzo di A Valore di A 20
Variabili! Sono contenitori di informazioni (cioè di valori), caratterizzati da:! nome simbolico: rappresenta in modo univoco una locazione di memoria! tipo: rappresenta i valori ammissibili per la variabile e le operazioni che si possono effettuare su di essa Nome variabile identifica una cella di memoria Il valore della variabile indica il contenuto della cella di memoria } Cella di memoria Memoria centrale 21
Dichiarazione di variabili! Dichiarazione di variabile: indica che e come una variabile verrà utilizzata dal programma! Definisce l identificatore simbolico (nome)! Definisce il tipo, adatto ai valori da rappresentare! Alloca la quantità di memoria adeguata a contenere il tipo! Associa in modo univoco l indirizzo di memoria al nome! Consente di rilevare, durante la compilazione, errori sull uso improprio della variabile nel programma! Sintassi C per la dichiarazione di variabili tipo_variabile nome_var;! Lo spazio di memoria allocato per una variabile dipende dal tipo di dato 22
Perché dichiarare una variabile?! Una variabile rappresenta uno spazio di memoria centrale! Prima dell esecuzione del programma deve essere chiaro quanto spazio serve al programma! Quindi devono essere note tutte le variabili che un programma intende utilizzare! Tutti gli accessi alla variabile indicano un accesso alla memoria! Quindi deve essere chiaro quale spazio di memoria è associato alla variabile 23
Parte dichiarativa! La parte dichiarativa non è unica in tutto il codice! Esistono:! una parte dichiarativa globale: prima del main! una parte dichiarativa locale al main: all interno del main! Al momento, dichiarare una variabile all interno di quella globale o di quella locale non fa differenza! La differenza sarà evidente quando si introdurranno i sottoprogrammi #include <stdio.h> int i; main() { } int j; Parte dichiarativa locale al main printf( Hello World ); Parte dichiarativa globale 24
Parte dichiarativa globale! Contiene dichiarazione di funzioni importate da altri moduli (file), cioè definite e codificate in altri file (attraverso la direttiva #include)! Oggetti visibili (utilizzabili) da tutto il programma, cioè da main e da altre funzioni! Tipi di dati, variabili, costanti simboliche, prototipi di funzioni 25
Parte dichiarativa locale! Può contenere gli stessi elementi dichiarabili nella parte dichiarativa globale! È esclusa solo la possibilità di utilizzare la direttiva #include 26
Tipi di dato! Un tipo identifica la classe dei valori ammissibili, le operazioni lecite, e la modalità con cui agiscono le operazioni! Esistono due macro-classi di tipi:! Tipi built-in (predefiniti)! Tipi definiti dall utente! Sia quelli built-in che quelli definiti dall utente possono essere:! Semplici! Composti 27
Tipi semplici built-in! char:! Occupa 8 bit (1 byte)! Può contenere valori da 0 a 255, che rappresentano la codifica ASCII del carattere corrispondente! int! Occupa 16 bit (2 byte)! Rappresentano gli interi relativi. Valori in complemento a 2 da 32768 a + 32767 28
Tipi semplici built-in! float! Occupa 32 bit (4 byte)! Rappresentano i razionali in virgola mobile. Valori espressi tramite mantissa e esponente. Valori da 10-38 a 10+38! double! Occupa 64 bit (8 byte)! Rappresentano i razionali in virgola mobile. Valori espressi tramite mantissa e esponente. Valori da 10-308 a 10+308 char, int, float e double sono anche detti tipi aritmetici 29
Calcolo dello spazio di memoria dati richiesto #include <stdio.h> int i, j; main() { char c; float f; printf( Hello World );! Il programma richiede uno spazio di memoria per i dati pari a! 2 byte per i! 2 byte per j! 1 byte per c! 4 byte per f! Per un totale di 9 byte } 30
long, short e unsigned 1/2! Il tipo di dato int ha delle varianti che modificano i valori ammissibili! long raddoppia lo spazio di memoria! short dimezza lo spazio di memoria! unsigned elimina il bit di segno e ammette solo valori positivi! Il tipo di dato double ammette la sola variante long che aumenta lo spazio di memoria 31
long, short e unsigned 2/2! unsigned int:! Occupa 16 bit (2 byte)! Ammette valori compresi tra 0 e 65535 (2 16-1)! long int! Occupa 32 bit (4 byte)! Ammette valori compresi tra -2.147.483.648 e 2.147.483.647! short int! Occupa 8 bit (1 byte)! Ammette valori compresi tra -128 e +127 (2 8-1)! unsigned long int! Occupa 32 bit (4 byte)! Ammette valori compresi tra 0 e 4.294.967.295 (2 32-1)! long double! Occupa 80 bit (10 byte)! Ammette valori compresi tra 10-4932 e 10 +4932 32
Tipo di dato char 1/2! Dichiarando una variabile di tipo char si riserva una zona di memoria di 1 byte! In quella cella di memoria non è inserito il carattere ma la sua codifica ASCII (American Standard Code for Information Interchange)! Esempio char c; c = A ;! Nella zona di memoria è inserito il valore 01000001 2 (65 10 ) 33
Tabella ASCII 34
Tipi di Operatori! Aritmetici + (somma) - (sottrazione) * (prodotto) / (divisione) % (resto)! Di confronto > (maggiore) >= (maggiore o uguale) < (minore) <= (minore o uguale) == (uguale)!= (diverso)! Assegnamento = 35
Assegnamento! È un operatore e si indica con il simbolo = (uguale)! A sinistra dell operatore deve sempre esserci una variabile! A destra può esserci! una costante! una variabile! un espressione! Attenzione! Il tipo di dato della variabile a sinistra dell operatore deve essere identico (o almeno compatibile) con quanto indicato a destra 36
Compatibilità tra i tipi! Il compilatore controlla la compatibilità tra i tipi! In alcune situazioni risolve la non compatibilità tramite delle regole di conversione automatica tra i tipi! La conversione del tipo è indolore se il tipo di dato a destra è inferiore al tipo della variabile a sinistra dell operatore, secondo la seguente gerarchia: char < int < float! Altrimenti non è esclusa perdita di informazione 37
Compatibilità tra i tipi - Esempio! La conversione di un valore a float in un valore int comporta la perdita della parte frazionaria int i; float f; f = 4.3; /*OK!*/ i = 4.3; /*assegnamento incongruente con perdita di informazione*/ f = 10; /*assegnamento incongruente senza perdita di informazione*/ i = 10; /*OK!*/ 38
Costanti! Esistono due tipi di costanti! Esplicite: esprimono direttamente dei valori! 24 costante di tipo int! 24L costante di tipo long! 3.145 costante di tipo double! A costante di tipo char! Simboliche! Sono nomi simbolici che il programmatore adotta per indicare dei valori prefissati (maggiore leggibilità)! Hanno un tipo espresso implicitamente nel valore! Devono essere precedentemente dichiarate 39
Dichiarazione di costanti! Dichiarare una costante significa associare un simbolo ad un valore! A differenza delle variabili tale valore non cambierà mai durante tutto il programma! Esistono due modi per dichiarare una costante:! Attraverso la direttiva #define! Attraverso la parola chiave const! In ogni caso una costante NON occupa alcuna zona di memoria 40
Direttiva #define! Associa un nome ad un valore di qualunque tipo! Esempi! #define nmaxp 10! #define vmax 150.0! #define FALSE 0! #define TRUE 1! #define MIASTRINGA Hello world!! L istruzione non deve terminare con il ;! In questo caso l andare a capo indica la fine dell istruzione! Il nome della costante viene anche detta macro 41
Parola chiave const! Permette di definire costanti tipizzate! Sono costanti che accettano un valore ammissibile per il tipo di dato al quale è associato const float pi = 3.1415;! Anche in questo caso non viene riservata alcuna zona di memoria! Ulteriori assegnamenti di costanti all interno del codice sono vietati 42
Il compilatore e le costanti In fase di compilazione ogni riferimento ad una costante viene sostituito con il valore corrispondente Alla fine di questa sostituzione la costante è come se non fosse stata dichiarata Per questo motivo il compilatore non riserva alcuna zona di memoria ad una costante #define PI 3.14 const int raggio = 10; main() { float area; area = raggio * raggio * PI; } main() { float area; area = 10 * 10 * 3.14; } 43
Funzioni di Ingresso e Uscita! In C, l ingresso e l uscita avviene tramite chiamate (attivazioni) di funzioni (sottoprogrammi) disponibili nella Standard Library! Standard Library: collezione di funzioni disponibili nell ambiente C e utilizzabili dai programmi applicativi Funzioni! di input e output da terminale e da file! gestione di stringhe! matematiche! 44
Funzioni di Ingresso e Uscita La Standard Library è costituita da:! una collezione di header file (stdio.h, string.h, math.h)! un header file contiene la dichiarazione di tipi, costanti simboliche predefinite e prototipi di funzione! #include permette a qualsiasi programma di utilizzare questi identificatori che non sono stati dichiarati all interno del programma! e da un insieme di file; ciascun file costituisce il codice oggetto eseguito dalla funzione 45
Stampa su video printf(stringa di controllo, elementi da stampare);! printf () è l identificatore riservato alla funzione! stringa di controllo (racchiusa tra ), contiene:! caratteri alfanumerici da stampare a video! caratteri di conversione e/o di formato (preceduti dal simbolo %): utilizzati per interpretare i valori degli elementi da stampare. Es. d, f, c,! caratteri di controllo della stampa: caratteri ASCII a cui non corrisponde alcun simbolo stampabile, hanno effetti di formato di stampa, come linea nuova (\n), tabulazione (\t), ecc. 46
Stampa su video SEMANTICA e FUNZIONAMENTO: printf (stringa di controllo, elementi da stampare);! è la chiamata alla funzione, che fa partire l attivazione del sottoprogramma associato! stampa i caratteri alfanumerici tra doppi apici e al posto dei caratteri di conversioni stampa il valore dell identificatore corrispondente nella lista da stampare! I caratteri di controllo stampa posizionano il cursore nella posizione opportuna 47
I tipi di dati nelle operazioni di I/O printf( "%d è uguale a %d\n", num1, num2); stringa di controllo tipo dell elemento da stampare %d intero decimale %f floating point %c carattere %s stringa 48
Esempio #include <stdio.h> main() { int num1,num2; num1=1; num2=num1; printf( "%d è uguale a %d\n", num1, num2); } 1 è uguale a 1 49
Ingresso da Tastiera scanf(stringa controllo, lista di variabili)! scanf() è l identificatore riservato alla funzione! stringa di controllo (racchiusa tra ), contiene:! caratteri di conversione e/o di formato (preceduti dal simbolo %): utilizzati per interpretare i valori degli elementi letti da tastiera, che devono essere memorizzati nelle variabili. Es. d, f, c,! variabili a cui associare il valore letto: lista di identificatori di variabili. Le variabili devono essere indicate tramite il loro indirizzo (&nome_var). La lista è ordinata rispetto ai caratteri di conversione 50
Ingresso da Tastiera SEMANTICA e FUNZIONAMENTO scanf(stringa controllo, lista di variabili);! è la chiamata alla funzione che attiva l esecuzione del programma associato! ad ogni pressione di un tasto, la funzione visualizza su video il carattere alfanumerico premuto! la sequenza di tasti premuti deve terminare con la pressione del tasto Invio! la funzione assegna alle variabili il valore binario del carattere letto 51
I tipi di dati nelle operazioni di I/O Altri caratteri di controllo: \n (a capo) \t (tabulatore) \ (carattere di escape: permette di visualizzare effetivamente il carattere che lo segue) (es. \, \, \%, \\) scanf( "%d%s%d", &giorno, mese, &anno ); Nota: nella scanf le variabili vanno precedute da & (ampersand) (vedremo poi perché!) 52
Tipo di dato char 2/2! Per il compilatore quindi vale la relazione char == unsigned short int! Attenzione! c = 1 è diverso da c = 1! Nel primo caso a c è assegnato il valore 1! Nel secondo caso a c è assegnata la codifica del carattere 1 e quindi 49! Supponendo che c== 1 con c di tipo char! printf( %c, c); stampa il carattere 1! printf( %d, c); è valido e stampa il numero 49! Supponendo che i==90 con i di tipo int! printf( %c, i); è valido e stampa il carattere Z! printf( %d, i); stampa il numero 90 53
Tabella ASCII 54
Tipi aggregati! Utilizzare solo i tipi di dato semplici può essere limitante e rendere il programma poco flessibile! Ad esempio si supponga di voler memorizzare le temperature dell ultima settimana! Servono 7 variabili di tipo intero! Servono 7 operazioni di lettura int main() { int lun,mar,mer,gio,ven,sab,dom; } Leggi(lun);.. Leggi(dom); 55
Limiti dei tipi semplici! Dall esempio emerge una certa ripetitività nelle istruzioni! La soluzione è poco flessibile. Se volessi memorizzare le temperature dell ultimo mese:! devo dichiarare altre variabili! devo aggiungere altre Leggi! Serve un meccanismo per associare ad una etichetta non un solo valore ma diversi valori! Il linguaggio C mette a disposizione due alternative:! Array! Struct 56
Array! È definito come una sequenza FISSATA di elementi OMOGENEI:! Fissata significa che la lunghezza dell array deve essere nota a tempo di compilazione! Omogenei significa che tutti gli elementi dell array devono essere dello stesso tipo! Dichiarazione: tipo_dato nome_vettore[dimensione_vettore];! Per l esempio discusso la dichiarazione sarà: int temperature[7]; 57
Elementi di un array 1/2! Ogni elemento di un array è individuato tramite un indice che rappresenta la sua posizione nell array! Se DIM è la dimensione dell array (valore intero), allora gli elementi dell array sono indicizzati con un indice compreso tra 0 e DIM-1! Es.: se dichiaro un array in questo modo: int temperature[7];! Significa che ho a disposizione 7 elementi indicizzati attraverso un valore compreso tra 0 e 6 temperature[0]; /*primo elemento*/ temperature[1]; /*secondo elemento*/ temperature[6]; /*settimo elemento*/! Se utilizzo un indice con valore superiore a DIM-1 ho un errore a run-time 58
Elementi di un array 2/2! Un array può essere una composizione di:! Tipi semplici: interi, reali,! Tipi composti: array, struct! Ogni elemento è una variabile del tipo dichiarato, quindi posso applicare gli operatori ammissibili per quel tipo di dato temperature[1]; /*è un intero*/ x = temperature[1]*7; /*è una operazione lecita*/ x = temperature*7 /*è illecito! Non posso moltiplicare per un numero un intero array /* 59
Array e memoria! Lo spazio in memoria è allocato ad indirizzi fisici contigui! Spazio di memoria totale occupato è dato dallo spazio richiesto da ogni singolo elemento per la dimensione dell array:! DIM*sizeof(tipo_elem) 60
typedef e array! La dichiarazione: typedef tipo_elem tipo_array [DIM];! Definisce un nuovo tipo di nome tipo_array! Il tipo definito è un array di DIM elementi di tipo tipo_elem! Esempio: typedef int vett[dim]; vett vettore_di_ingresso; 61
Array di Array! L elemento di un array può essere a sua volta un array! La dichiarazione int matrice [DIM2][DIM1];! Definisce la variabile matrice come un array di DIM2 elementi, ciascun elemento è un array di DIM1 elementi! Alloca spazio in memoria (in locazioni fisiche contigue) adatto a contenere DIM1xDIM2 elementi! Accesso agli elementi di una matrice:! matrice [i][j] denota il valore j-mo dell i-mo array 62
Array e ciclo for! L accesso agli elementi di un array avviene attraverso indice! Se voglio scorrere un intero array è utile utilizzare il costrutto for int vettore[10]; for (int i=0;i<10; i++) Scrivi(vettore[i]); 63
Copia di un array! Dati due array a e b il seguente assegnamento a = b! Non produce la copia dei valori di b in a! La copia deve avvenire elemento per elemento for (i = 0; i<dim; i++) a[i] = b[i];! Questo è dovuto alla modalità con cui un array è memorizzato 64
Costruttore Struct! Consente di dichiarare variabili che tramite un unico identificatore raggruppano informazioni di tipo eterogeneo (record) Costruttore struct { }! Una struttura (record) è definita come un insieme prefissato di campi non omogenei! Ogni campo è dotato di nome e di tipo 65
Costruttore Struct Dichiarazione di una variabile di tipo record Sintassi: struct { tipo_campo1 nome_campo1; tipo_campo2 nome_campo2; tipo_campo3 nome_campo3; }nome_var; Semantica:! nome_var rappresenta il valore della variabile (come aggregato di valori dei suoi campi) 66
Accesso ai campi della struttura Tramite la notazione puntata nome_var.nome_campo1 struct { char cognome[30]; char nome[30]; int matricola; int votiesa[29]; } studente; studente.matricola studente.votiesa[3] studente.cognome[0] 67
Accesso ai campi della struttura Operatori utilizzabili per i singoli campi: operatori leciti per il tipo del campo Operatori globali: assegnamento studente2=studente1; Non è possibile confrontare studenti, operatore (==) non disponibile. E necessario effettuare il confronto campo per campo 68
Esempio: Una biblioteca typedef struct { int anno; int pagine; char autore[30]; char titolo[100]; } libro_t; libro_t mio_libro; mio_libro.anno = 1998; 69
Esempio: Una biblioteca libro_t mio_libro; printf( Anno:\n"); printf("%d\n", mio_libro.anno); printf( Numero di pagine: "); printf("%d\n", mio_libro.pagine); printf("autore:\n"); printf( %s\n, mio_libro.autore); printf("titolo:\n"); printf( %s\n, mio_libro.titolo); 70
Array di struct typedef struct { int anno; int pagine; char autore[30]; char titolo[100]; } libro_t; libro_t biblioteca[n]; 71
Esempio: ricerca libro piu vecchio in biblioteca int vecchio=0; for (i=1; i<n; i++) { if (biblioteca[i].anno < biblioteca[vecchio].anno) { vecchio = i; } } 72