Il linguaggio C Notate che ... 1 Il C è un linguaggio a blocchi int main (void) { blocco } 2 Il C è un linguaggio a blocchi (2) Non è possibile mischiare dichiarazioni e comandi ! int main (void) { Dichiarazione di variabili locali Comandi } 3 Variabili non inizializzate Le variabili non inizializzate non producono nessun errore!!!! int main (void) { /* ed esempio …. */ int min, max, out; min = max * out; … } Controllate sempre di aver inizializzato le variabili prima del loro uso! 4 E se non me lo esegue? Se avete cercato di eseguire a.out e non ci riuscite: $$ a.out 5 E se non me lo esegue?(2) Se avete cercato di eseguire un file eseguibile e non ci siete riusciti: $$ a.out bash:command not found Significa che la variabile di ambiente PATH non contiene la directory corrente quindi usate il path completo $$ ./a.out 6 E se non me lo esegue?(3) Se avete cercato di eseguire un file eseguibile e non ci siete riusciti: $$ a.out bash:command not found Per settare il PATH – bash $$ export PATH=$PATH:. – csh e tcsh $$ setenv PATH=${PATH}:. 7 Informazioni sulle funzioni di libreria Usare la sezione 3 dei manuali in linea, es: $$ man 3 printf 8 Informazioni sulle funzioni di libreria (2) NAME printf SYNOPSIS #include <stdio.h> int printf(const char* format,…); DESCRIPTION …… cosa fa EXAMPLES …… esempi di uso SEE ALSO … … altre funzioni simili 9 Le FAQ Alla pagina www.di.unipi.it/~susanna/LPS/FAQ.htm Trovate le domande e risposte più comuni 10 Il linguaggio C Tipi di dato, costanti, operatori (parte1) 11 I tipi di dato primitivi • Interi : short, int, long, – 2,4,8,16 byte (dipende dalla implementazione) – segnaposto • %d rappresentazione decimale • %x,%o rappresentazione esadecimale o ottale – costanti intere : 1, -2, 4565 – La funzione predefinita sizeof() fornisce la lunghezza in byte di un qualsiasi tipo o variabile C • es. sizeof(int) 12 I tipi di dato primitivi (2) • Caratteri : – char, 1 byte, cod. ASCII, segnaposto %c – costanti carattere : `a`, `b`, `c` – i caratteri sono visti da C come interi un po’ speciali : è possibile operare sui caratteri con le normali operazioni su interi – es: char c = `a`; c++; putchar(c); /* ???? */ 13 I tipi di dato primitivi (3) • Reali : – float, double, long double – 4,8,16 byte, – rappresentazione in virgola mobile – segnaposto : • stampa (es. printf()) %f, %lf • lettura (es. scanf()) %f (float) , %lf (double), %Lf (long double), 14 I tipi di dato primitivi (4) • Libreria matematica (libm) – fornisce tutte le più comuni operazioni matematiche per operare sui reali • es. sqrt() – per utilizzarla includere il file • #include <math.h> – compilare con gcc … … … -lm effettua il link esplicito della libreria 15 I tipi di dato primitivi (5) • Non esistono i tipi byte e bool !!! • Le stringhe sono rappresentate come array di caratteri. • signed/unsigned – modificatore per i tipi interi e caratteri – permette di scegliere fra la rappresentazione di soli interi positivi (es 0-255 su un byte) o quella in complemento a due (es -128,+127) • es. unsigned int, signed char 16 I tipi di dato primitivi (6) • limits.h – contiene macro che definiscono i massimi ed i minimi numeri rappresentabili per tutti i tipi – es. INT_MIN, INT_MAX – per utilizzarli includere il file limits.h 17 I tipi di dato primitivi (7) • Conversioni fra tipi diversi : – sono automatiche in C – posso scrivere espressioni con operandi di tipo diverso int a=0; float b, r=3.1; b = a + r; – di solito viene convertito tutto nel tipo più accurato, le regole p.115 nel testo KP – conversioni esplicite r =(float) a / b 18 Il linguaggio C Funzioni 19 Funzioni C • Le funzioni C sono l’unico modo per strutturare i programmi e non si possono annidare • Non esistono le procedure • Non esistono i metodi • Dichiarazione di funzioni : – descrive il prototipo della funzione ovvero: nome della funzione, il tipo del valore restituito ed il tipo di tutti gli argomenti utilizzati 20 Funzioni C (2) • Dichiarazione di funzioni : – es: prototipo della funzione di somma di due interi int somma (int, int); oppure int somma (int x, int y); x, y sono inutili, ma vengono convenzionalmente specificati per documentazione 21 Funzioni C (3) • Definizione di funzioni : – la definizione di una funzione è divisa fra : • una intestazione (head) che fornisce il prototipo della funzione a i nomi per i parametri formali, ed • un corpo (body) che specifica le azioni da compiere – es: int somma (int x, int y) { return (x + y); intestazione } 22 Funzioni C (4) • Definizione di funzioni : – la definizione di una funzione è divisa fra • una intestazione (head) che fornisce il prototipo della funzione a i nomi per i parametri formali, ed • un corpo (body) che specifica le azioni da compiere – es: int somma (int x, int y) { return (x + y); corpo } 23 Funzioni C (5) • La dichiarazione o la definizione di una funzione deve sempre precedere il suo uso! – Vedimo la tipica struttura di un programma C (unico file): 24 Funzioni C (5.1) /* direttive al preprocessore */ #include … #define … /* dichiarazioni di varibili globali*/ int k; /* dichiarazione di funzioni (prototipi)*/ int somma (int x, int y); int main (…) {… somma(4,5); … } /* definizione di funzioni */ int somma (int x, int y) {….} 25 Esempio di funzione #include <stdio.h> int max (int a, int b); int main (void){ printf(“Il massimo è %d \n”, max(10,2)); return 0; } int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; } 26 Funzioni C (6) • Tutti i parametri sono passati per valore – una copia viene messa sullo stack all’atto della chiamata – la copia viene rimossa qundo la funzione termina e non è più accessibile • Scope delle variabili: – Il corpo di una funzione contiene le variabili locali • sono allocate sullo stack, • sono accessibili solo a quella funzione • perdono il valore fra un’invocazione e l’altra 27 Esempio di funzione (.1) #include <stdio.h> int max (int a, int b); int main (void){ printf(“Il massimo è %d \n”, max(10,2)); return 0; } Parametri attuali int max (int a, int b) { copiati sullo stack int tmp; tmp = (a < b)? b : a return tmp; /* definizione */ } 28 Esempio di funzione (.2) #include <stdio.h> int max (int a, int b); int main (void){ printf(“Il massimo è %d \n”, max(10,2)); return 0; } Variabili locali int max (int a, int b) { sullo stack int tmp; tmp = (a < b)? b : a return tmp; /* definizione */ } 29 Esempio di funzione: il frame (.3) printf(“Il massimo è %d \n”, max(10,2)); XX: return 0; } stack stack int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; } 30 Esempio di funzione: il frame (.4) printf(“Il massimo è %d \n”, max(10,2)); XX: return 0; } int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; } stack stack 2 10 XX 31 Esempio di funzione: il frame (.5) printf(“Il massimo è %d \n”, max(10,2)); XX: return 0; } int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; &tmp } stack stack 2 10 XX 676186815 32 Esempio di funzione: il frame (.6) printf(“Il massimo è %d \n”, max(10,2)); XX: return 0; } int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; &tmp } stack stack 2 10 XX 10 33 Esempio di funzione: il frame (.7) printf(“Il massimo è %d \n”, max(10,2)); XX: return 0; } stack stack int max (int a, int b) { int tmp; tmp = (a < b)? b : a return tmp; } 34 Funzioni C (7) • Scope delle variabili (cont.) : – Le variabili globali sono variabili dichiarate al di fuori delle funzioni • sono accessibili all’interno di tutte le funzioni • sono allocate nell’area dati e vengono deallocate solo alla terminazione del programma – Le globali sono sconsigliate a meno di casi motivati! • Devono essere sempre adeguatamente documentate 35 Esempio di funzione #2 /* un esempio di var globale */ #include <stdio.h> int max (int a, int b); int k = 0; /* var globale */ int main (void){ printf(“Il massimo è %d \n”, max(10,2)); printf(“Il valore di k è %d \n”, k); return 0; } int max (int a, int b) { k = k + 1; /* side effect */ return (a < b)? b : a ; } 36 Esempio di funzione #2 (.1) • Risultato esecuzione > max Il massimo è 10 Il valore di k è 1 37 Funzioni C (8) • Scope delle variabili (cont.) : – Le variabili statiche locali (static) • sono accessibili all’interno della funzione che le dichiara • mantengono il valore fra una invocazione e l’altra 38 Esempio di funzione #3 /* un esempio di var statica */ #include <stdio.h> int max (int a, int b); int main (void){ printf(“Il massimo è %d \n”, max(10,2)); /*k non è più accessibile fuori da max*/ return 0; } int max (int a, int b) { static int k = 0; k++; printf(“Il valore di k è %d \n”, k); return (a < b)? b : a ; } 39 Esempio di funzione #3 (.1) • Risultato esecuzione (lo stesso di prima) > max Il valore di k è 1 Il massimo è 10 40 Funzioni C: commenti • La nostra convenzione per i commenti per dichiarazioni e definizioni – usa le stesse categorie viste a Metodologie Programmazione, con sintassi un po’ diversa /* @description -- somma due interi @param x -- significato del parametro @require -- (opz) condizioni sui parametri es. x positivo) @return x -- descriz valore restituito (if any) @modify -- decrizione effetti su variabili globali, array etc*/ 41 Funzioni C: commenti (esempio) /* @description -- somma due interi @param a -- intero @param b -- intero @return x -- la somma @modify -- incrementa k, globale*/ int max (int a, int b) { k = k + 1; return (a < b)? b : a ; } 42 Funzioni C: commenti (esempio) (.1) /* @description -- max di due interi @param a -- intero @param b -- intero @return x -- la somma @modify -- incrementa k, globale*/ int max (int a, int b) { extern int k; k = k + 1; return (a < b)? b : a ; } 43 Il linguaggio C Tipi di dato, costanti, operatori (parte2) 44 I valori booleani • Rappresentazione dei valori booleani in C : – non esiste il tipo bool! – sono rappresentati con int – 0 rappresenta FALSO, qualsiasi valore != 0 rappresenta VERO • es. 456 viene interpretato come VERO • es. if (4) printf(“VERO”); è un condizionale con condizione legale e sempre vera, che quindi stampa sempre VERO sullo schermo. 45 Gli operatori di confronto • ==, !=, (uguale, diverso) – definiti sui tipi base e sulle struct – se usati con gli array confrontano solo il puntatore al primo elemento – lo stesso vale con le stringhe • <, <=, >, >= – definiti sui tipi base (numerici) – per le stringhe si usano funzioni di libreria (es. strcmp(), strncmp()) • restituscono sempre un valore di tipo int 46 Gli operatori booleani • && (and), || (or), ! (not) – valore : 0 se sono falsi e 1 se sono veri – restituscono un valore di tipo int • operatori bit a bit : & (and), | (or), ^ (xor), << (lshift), >> (rshift), ~ (complemento) – es. 1 << 3 // maschera con 1 in terza posizione a = a & ~ (1 << 3 ) // azzera il quarto bit di a 47 Es operatori bit-a-bit • a = a & ~ (1 << 3 ) 0 0 0 0 7 6 5 4 0 0 0 1 3 2 1 0 48 Es operatori bit-a-bit (2) • a = a & ~ (1 << 3 ) 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 49 Es operatori bit-a-bit (3) • a = a & ~ (1 << 3 ) 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 1 0 1 1 1 50 Es operatori bit-a-bit (4) • a = a & ~ (1 << 3 ) 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 51 Es operatori bit-a-bit (5) • a = a & ~ (1 << 3 ) 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 52 Es operatori bit-a-bit (6) • a = a & ~ (1 << 3 ) 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 53 Es operatori bit-a-bit (7) • a = a & ~ (1 << 3 ) 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 0 1 0 1 Nuovo valore di a Abbiamo azzerato il bit 3 54 Selezionare l’n-esimo bit di un intero int bitn (int c, int n){ if (((1<<n) & c)!= 0) return 1; else n return 0; 0 1 0 1 1 1 0 1 } 1<<n 0 0 0 1 0 0 0 0 (1<<n) & c 0 0 0 1 0 0 0 0 55 Stampare i K bit meno significativi di un intero void bitstampa (int c){ int i; for (i=0; i<K; i++) if (bitn(c,K-1-i)!= 0) printf(”1”); else printf(”0”); } 56 Gli array • Aggregati di variabili dello stesso tipo : – dichiarazione (array statici): 0 1 int a[4]; 1 3 2 3 posizione 7 8 valore a[1] – gli indici partono da 0 – non viene effettuato nessun controllo sugli indici nell’accesso : se cerco di accedere a a[15]il compilatore non dà errore ed in fase di esecuzione il risultato dipende dal contenuto della memoria adiacente ad a 57 Le stringhe • Sono array di caratteri terminati da ´\0´ – dichiarazione : char parola[8]=“mango”; – permette di memorizzare una qualsiasi parola di al più 7 caratteri (l’ottavo serve per il terminatore) – è inizializzata con la parola “mango” – string.h fornisce tutte le più comuni funzioni per manipolare stringhe 58 Le strutture (record) • Aggregati di variabili di tipo diverso – dichiarazione di una nuova struttura: struct studente { char nome[40]; char cognome[40]; double voto; }; – dichiarazione di variabili di tipo struttura : struct studente x; struct studente classe[140]; 59 Le strutture (record) (2) • Ridenominazione tipi : typedef tipo nome; – es : typedef int Intero; Intero nome[40]; – es : typedef char stringa40[41]; stringa40 nome; 60 Le strutture (record) (3) • Ridenominazione tipi : typedef struct { char nome[40]; char cognome[40]; double voto; } studente ; – così si può eliminare struct dalle dichiarazioni studente x; studente classe[140]; 61 Le strutture (record) (4) • Ridenominazione tipi : typedef struct studente { char nome[40]; char cognome[40]; double voto; } studente ; Opzionale (serve per i tipi ricorsivi) – così si può eliminare struct dalle dichiarazioni studente x; studente classe[140]; 62 Le strutture (record) (5) • Accesso ai campi : – tramite l’operatore punto (.) – es : studente x; studente classe[140]; classe[i].nome = x.nome; 63 I tipi enumerati • Rappresentano insiemi finiti: – giorni: lunedi, martedi, … – mesi: gennaio, febbraio, … – bool: true, false. • Associano un identificatore (costante) ad ogni elemento dell’insieme • Sono rappresentati con degli interi – .. Ma il compilatore controlla che le funzioni siano chiamate con il tipo giusto (type checking) 64 I tipi enumerati (2) • Esempio: enum giorni {lun,mar,mer,gio,ven,sab,dom}; enum giorni x; x = lun; … /* dichiarazione */ – Si può evitare di ripetere enum con un typedef – I valori interi assegnati partono da 0 : • lun == 0,mar == 1,mer == 2,… 65 I tipi enumerati (3) • Esempio: typedef enum {true = 1, false = 0} bool; bool trovato; trovato = false; … /* dichiarazione */ – Si possono scegliere i valori interi assegnati ad ogni elemento 66 I tipi union • Nei tipi union più valori di tipi diversi condividono la stessa zona di memoria – noti anche come record con varianti – Esempio: union numero { int intero; long double reale; struct complex complesso; }; – solo uno dei tre viene memorizzato ogni volta 67 I tipi union (2) union numero { int intero; /* 4 byte */ long double reale; /* 8 byte */ struct complex complesso; /* 16 byte */ }; union numero n; • … in memoria ... &n 16 byte 68 I tipi union (3) union numero { int intero; /* 4 byte */ long double reale; /* 8 byte */ struct complex complesso; /* 16 byte */ }; union numero n; /* specifico come interpretare n */ n.intero = 5; 0 0 0 5 &n Parte significativa 69 I tipi union (4) • Uso tipico: – in una struct che contenga (oltre alla union) un campo ‘tipo del valore’ typedef enum {INT,REAL,COMPLEX} numtyp; struct numero { numtyp tipo; union {/* union anonima */ int intero; long double reale; struct complex complesso; } }; 70 I tipi union (5) • Uso tipico: (cont.) struct numero x ; … switch (x.tipo) { case INT : …. x.intero = ...; case REAL: …. X.reale = ...; case COMPLEX: …. X.complesso = ...; ….. 71