Introduzione al linguaggio C
Cenni storici Nel 1972, presso i Bell Laboratories, Dennis Ritchie progettava e realizzava la prima versione del linguaggio C, riprendendo e sviluppando i principi e i costrutti sintattici del linguaggio BCPL, di Martin Richards, e del linguaggio B, di Ken Thompson, l'autore del S.O. Unix. In seguito Ritchie e Thompson riscrissero in C il codice di Unix. Il C implementava vari tipi di dati (carattere, interi, numeri in virgola mobile, strutture) non previsti all’epoca dagli altri due linguaggi. Con il tempo C ha subito trasformazioni: la sua sintassi è stata affinata, anche per l’estensione object-oriented (C++).
Caratteristiche principali Elevato potere espressivo: Tipi di dato primitivi e tipi di dato definibili dall’utente Strutture di controllo (programmazione strutturata, funzioni e procedure) Caratteristiche di basso livello (gestione delle memoria, accesso alla rappresentazione) Sintassi definita formalmente
Elementi del testo di un programma C Nel testo di un programma C possono comparire: Parole chiave: sono parole riservate che esprimono istruzioni, tipi di dato, e altri elementi predefiniti nel linguaggio. Identificatori: nomi che rappresentano oggetti usati nel programma (ad esempio: variabili, costanti, tipi, funzioni ecc.). Costanti: numeri (interi o reali), caratteri e stringhe. Operatori: sono simboli che consentono la combinazione di dati in espressioni. Commenti
Parole chiave Sono parole riservate, cioè non possono essere utilizzate come identificatori, che esprimono istruzioni, tipi di dato, e altri elementi predefiniti nel linguaggio: auto break case const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef unsigned void volatile while
Identificatori Un identificatore è un nome che denota un oggetto usato nel programma (ad esempio: variabili, costanti, tipi, funzioni). Deve iniziare con una lettera (o con il carattere ‘_’), alla quale possono seguire lettere e cifre in numero qualunque. Distinzione tra maiuscole e minuscole, il linguaggio è case-sensitive. Esempio identificatori validi: identificatori non validi: num - cont1 - _num int - 3num - 0d Var1 - NUM - Cont Nota bene: prima di essere usato, ogni identificatore deve essere già stato definito in una parte di testo precedente.
Costanti Numeri interi Rappresentano numeri relativi (quindi con segno): 2 byte 4 byte base decimale 12 70000, 12L Si possono rappresentare anche in forma binaria, ottale e esadecimale. Numeri reali Varie notazioni: 24.0 2.4E1 240.0E-1 Suffissi: l, L, u, U ( interi-long, unsigned) f, F (reali - floating) Prefissi: 0 (ottale) 0x, 0X(esadecimale)
Costanti Caratteri: Insieme dei caratteri disponibili (è dipendente dalla realizzazione). In genere, ASCII esteso (256 caratteri). Si indicano tra singoli apici: ‘a’ ‘A’ Caratteri speciali: sono caratteri ai quali non è associato alcun simbolo grafico, ai quali è associato un significato predefinito: newline \n va a capo backspace \b cancella l’ultimo carattere form feed \f va alla pagina successiva carriage feed \r torna all'inizio della linea corrente tab \t tabulazione ecc. Stringhe In C, sequenze di caratteri racchiusa tra doppi apici " : "a" "abc" "" (stringa vuota)
Operatori Il linguaggio di programmazione C è una sequenza di espressioni, pertanto gli operatori vengono largamente utilizzati. Nel linguaggio C sono definite 4 classi di operatori (aritmetici, relazionali, logici e bit-a-bit), inoltre vi sono altri operatori dedicati a compiti specifici. Il primo operatore necessario è quello di assegnamento, la forma generale di questo operatore è: Nome_variabile = espressione Nome_variabile deve essere una variabile o un puntatore ( non possono esserci funzioni o costanti ). Mentre l'espressione può essere una semplice costante o complessa quanto si voglia.
Operatori + Aritmetici - * / % ++ -- Somma effettua l'operazione di somma fra 2 espressioni. - Sottrazione effettua l'operazione di differenza fra 2 espressioni. * Moltiplicazione effettua l'operazione di moltiplicazione fra 2 espressioni. / Divisione effettua l'operazione di divisione fra 2 espressioni. % Modulo calcola il resto della divisone fra la 1° ed la 2° espressione. Funziona con espressioni che tornino valori interi (char, int, long). Unario di cambio segno - espressione cambia il segno all'intera espressione. Unario + + espressione Equivale a espressione. ++ Incremento. Nella forma postfissa (es: variabile++) forma prefissa (es: ++variabile). In entrambi i casi le due forme sono equivalenti a: variabile = variabile + 1; Nella forma postfissa l'incremento viene effettuato dopo dell'utilizzo della variabile. Nella forma prefissa l'incremento viene effettuato prima dell'utilizzo della variabile. -- Decremento. Valgono le stesse considerazioni effettuate per l'incremento ++, tranne il fatto che le forme variabile-- e --variabile equivalgono a: variabile = variabile - 1;
Operatori Relazionali == Uguaglianza. Ritorna 1 (vero) se i valori sono uguali, ritorna 0 (falso) in caso contrario. != Disuguaglianza. Ritorna 1 (vero) se i valori sono differenti, ritorna 0 (falso) in caso contrario. < Minoranza. Ritorna 1 (vero) se il valore/espressione a sinistra dell’operatore è minore del valore/espressione di destra, ritorna 0 (falso) in caso contrario. <= Minoranza o uguaglianza. Ritorna 1 (vero) se il valore/espressione a sinistra dell’operatore è minore o uguale al valore/espressione di destra, ritorna 0 (falso) in caso contrario. > Maggioranza. Ritorna 1 (vero) se il valore/espressione a sinistra è maggiore del valore/espressione di destra, ritorna 0 (falso) in caso contrario. >= Maggioranza o uguaglianza. Ritorna 1 (vero) se il valore/espressione a sinistra dell’operatore è maggiore o uguale al valore/espressione di destra, ritorna 0 (falso) in caso contrario.
Operatori Logici ! Negazione. (unario) !espressione ritorna 0 se espressione ritorna un valore diverso da 0; ritorna 1 se espressione ritorna il valore uguale a 0. && And. Esegue l'and logico fra 2 espressioni. Valuta la prima espressione e se questa è falsa, ritorna 0 senza valutare la seconda; altrimenti valuta la seconda espressione. Se quest'ultima è falsa ritorna 0 altrimenti ritorna 1. || Or. Esegue l'or logico fra 2 espressioni. Valuta la prima espressione e se questa è vera, ritorna 1 senza valutare la seconda; altrimenti valuta la seconda espressione. Se quest'ultima è vera ritorna 1 altrimenti ritorna 0. Nel C il valore 0 viene considerato come valore logico FALSO, mentre un valore diverso da 0 è considerato come valore logico VERO. Pertanto gli operatori logici ! && || riconoscono in ingresso il valore 0 come condizione di falsità ed ogni altro valore come condizione di verità. Nel C gli operatori logici ritornano, invece, sempre uno dei due valori 0 o 1.
Operatori Bit a bit ~ & | ^ << >> complemento ad 1 (unario). ~espressione ritorna il complemento ad 1 del valore, ovvero ogni bit posto ad 1 viene cambiato a 0 e viceversa. & And bit a bit. Ritorna il valore dell'and effettuato bit a bit su 2 valori. Esempio: a=10, b=12, c = a&b; In binario avremmo (a) 00001010, (b) 00001100 il valore di ritorno sarà (c) 00001000. | Or (inclusivo) bit a bit. Ritorna il valore dell'or effettuato bit a bit su 2 valori. Esempio: a=10, b=12, c = a|b; In binario avremmo (a) 00001010, (b) 00001100 il valore di ritorno sarà (c) 00001110. ^ Or esclusivo (ex-or) bit a bit. Ritorna il valore dell'ex-or effettuato bit su 2 valori. Esempio: a=10, b=12, c = a^b; In binario avremmo (a) 00001010, (b) 00001100 il valore di ritorno sarà (c) 00000110. << Shift a sinistra. espr_1 << espr_2, ritorna il valore della traslazione a sinistra di espr_2 bit sui bit del valore di espr_1. I nuovi bit che entrano a destra sono posti a 0. Esempio: a=10; b=2, c = a<<b; In binario avremmo (a) 00001010, (b) 0000010 il valore di ritorno sarà c = 00101000. >> Shift a destra. espr_1 >> espr_2, ritorna il valore della traslazione a destra di espr_2 bit sui bit del valore di espr_1. I nuovi bit che entrano a sinistra possono dipendere dall'architettura dell'elaboratore e/o dalla implementazione del compilatore. Non e' garantito che siano sempre posti a 0 anche per valori negativi di espr_1; per evitare ciò, è bene assicurarsi che il valore ritornato da espr_1 sia di tipo unsigned. Esempio: a=10; b=2, c = a<<b; In binario avremmo (a) 00001010, (b) 0000010 il valore di ritorno sarà c = 00000010. N.B. - Per semplicità di esempi, sono stati considerati gli interi come costituiti da 8 bit, anziché' 16 o 32 bit come avviene in realtà sugli elaboratori.
Operatori Altri () (tipo) [] & * . -> sizeof ?: , Cambio priorità. L'espressione contenuta all'interno delle parantesi () viene eseguita con priorità più alta, rispetto all'espressione all'esterno. (tipo) Conversione tipo (casting). Forza l'espressione seguente al tipo specificato fra parentesi. [] Accesso ad un elemento di un array. L'espressione all'interno delle parentesi [] (che deve essere intero) viene impiegato per accedere all'array. Esempio: a[5] & Di indirizzo. Ritorna l'indirizzo della variabile di memoria a cui e' applicato. Esempio: &a * L'accesso da puntatore (dereferencing). Permette l'accesso ad un elemento di memoria il cui indirizzo e' contenuto in un puntatore. Esempio: *p . Di accesso ad un campo di una struttura (o di una union). Permette di accedere ad un campo facente parte di una struttura o di una union. Esempio: data.giorno -> Di accesso ad un campo di una struttura (o di una union) puntata. Permette di accedere ad un campo di una struttura o di una union puntata da un puntatore, scrivendo un codice più compatto e più leggibile, piuttosto che utilizzare gli elementi del linguaggio () . Esempio: p->giorno sizeof Di dimensione di una variabile o di un tipo. Ritorna la dimensione in byte occupata dalla variabile o necessaria per il tipo a cui l'operatore viene applicato. Esempio: sizeof(long) ?: Condizionale. espr_1 ? espr_2 : espr_3 Viene valutata espr_1. Se questa risulta vera, ritorna il valore di espr_2; altrimenti di espr_3. Equivale "quasi" ad: if (espr_1) espr_2; else espr_3; tranne per il fatto che il valore restituito dall'operatore condizionale è rappresentato da espr_2 o da espr_3. Esempio: int a, b, max; ... max = (a > b) ? a : b; max contiene il massimo fra a, b N.B. - l'uso delle parentesi è solo per migliorare la leggibilità. , Concatenazione. Concatena più espressioni. L'espressione concatenata assume il valore dell'ultima espressione da concatenare. Esempio: x=10, y=0; l'intera espressione assume il valore 0
Commenti Sono sequenze di caratteri ignorate dal compilatore che non interferiscono con il programma. Vanno racchiuse tra /* e */ Esempio: /*questo è un commento*/ I commenti si usano per introdurre o spiegare cosa fa quella istruzione o quella parte di codice di un programma. Sono utili per rendere il programma più leggibile e comprensibile.
Struttura base di un programma C Nel caso più semplice, un programma C consiste in: parte definizioni globali parte del programma principale (main) eventuali altre funzioni. La parte main di un programma è suddivisa in: Una parte di definizioni (variabili, tipi, costanti, etc.) in cui vengono descritti e definiti gli oggetti che vengono utilizzati dal main; Una parte istruzioni che descrive l’algoritmo risolutivo utilizzato, mediante istruzioni del linguaggio.
Struttura di un programma C – esempio 1 Librerie: contengono programmi di supporto con varie funzioni. Esempio la libreria stdio.h serve per le istruzioni di I/O. #include<stdio.h> /* Questo è il nostro primo programma C */ /* I commenti possono occupare... …più linee! */ main() { printf(“Salve, mondo\n ”); return 0; } Commento: testo esplicativo aggiunto solo per chiarezza; non ha alcuna funzione ed è ignorato dal compilatore, commenti su più linee Definisce una funzione main() che non riceve alcun valore come argomento La parentesi graffa aperta, indica l’inizio del programma La funzione printf() stampa a video ciò che viene inserito tra i doppi apici La funzione printf() non esegue automaticamente il ritorno a capo, ma bisogna inserire l’apposito comando \n, che indica newline, alla fine del testo da stampare o con un un’ulteriore printf(“\n”); Specifica il valore che deve essere ritornato; 0 attesta che il programma è terminato correttamente La parentesi graffa chiusa, indica la fine del programma
Struttura di un programma C – esempio 2 #include<stdio.h> int main() { int a, b, c; printf(“Inserire primo intero: \n”); scanf(“%d”, &a); printf(“Inserire secondo intero: \n”); scanf(“%d”, &b); c =a+b; printf(“Risultato: %d\n”, c); return 0; } Librerie: contengono programmi di supporto con varie funzioni. Esempio la libreria stdio.h serve per le istruzioni di I/O. Ogni funzione ritorna un valore: la parola chiave int indica che si tratta di un valore intero La parentesi graffa aperta, indica l’inizio del programma Dichiara che nella funzione main() verranno utilizzate tre variabili intere con nomi a, b e c; inizialmente il valore non è definito La funzione printf() stampa a video ciò che viene inserito tra i doppi apici Legge da tastiera (standard input), e specifica che i caratteri letti da tastiera devono essere interpretati come le cifre di un numero intero(%d), e memorizzato nella variabile a La funzione printf() stampa a video ciò che viene inserito tra i doppi apici Legge da tastiera (standard input), e specifica che i caratteri letti da tastiera devono essere interpretati come le cifre di un numero intero(%d), e memorizzato nella variabile b Esegue la somma di a + b e il risultato viene memorizzato in c La funzione printf() stampa oltre alla parola, il risultato presente in c, dove c deve essere interpretata come numero intero (%d) Specifica il valore che deve essere ritornato; 0 attesta che il programma è terminato correttamente La parentesi graffa chiusa, indica la fine del programma
Ricapitolando…… Introduzione alle caratteristiche, agli elementi e alla struttura base di un programma in linguaggio C. Gli esempi finali hanno permesso di introdurre alcuni dei concetti di base del linguaggio C: Ogni programma C contiene la funzione main(). I blocchi di codice sono delimitati da parentesi graffe. Le istruzioni sono terminate dal punto e virgola. Le variabili Devono essere dichiarate. Hanno un tipo in base ai dati che dovranno contenere.