Sei sulla pagina 1di 92

Introduzione alla programmazione in C

Variabili e memoria
Operatori ed espressioni

Fondamenti di Programmazione
CdL Ingegneria Informatica e dell'Informazione
Lezione 3
Le origini del C

Sviluppato a partire dal
linguaggio B ideato da
Thompson per creare
versioni iniziali di Unix

Implementato all’inizio degli anni ’70 da D.
Ritchie presso i laboratori Bell

E' diventato uno standard ANSI (American
National Standard Institute) nel 1989 (terminato
nel 1990)

Aggiornato nel 1999 (C99)
Caratteristiche del linguaggio C
È un linguaggio di programmazione general purpose,
imperativo a blocchi, procedurale

È considerato un linguaggio di alto livello ma non
troppo
– fornisce un insieme ristretto di costrutti di
controllo e di parole chiave, ma un insieme
ricco di operatori e strutture dati avanzate

Lo standard definisce librerie

specificano funzioni (.h) per l’accesso al sistema
operativo, l’allocazione di memoria, il trattamento
delle stringhe, funzioni matematiche...
Motivazioni
Perchè usare il C?
Offre al programmatore potenza, efficienza, portabilità
Visione a basso livello delle risorse
• Memoria e dispositivi
• Possibilità di incapsulare codice assembler
Capacità di manipolare i singoli bit
Uso efficiente delle risorse
• Ridotto uso di memoria
• Compilazione efficiente

Perchè non usare il C?


Scarso livello di astrazione
Motivazioni
Sistemi operativi
Perchè usare il C?
Offre al programmatore potenza, efficienza, portabilità
Visione a basso livello delle risorse
Compilatori
• Memoria e dispositivi
• Possibilità di incapsulare codice assembler
Capacità di manipolare i singoli bit
Uso efficiente delle risorse
Sistemi embedded
• Ridotto uso di memoria
• Compilazione efficiente

Perchè non usare il C?


Scarso livello di astrazione Sistemi real time
Sommario

Caratteristiche fondamentali
Il lessico
Istruzioni di base, strutture di controllo e blocchi
Le variabili e i tipi primitivi
Gli operatori e le espressioni
Problem solving
Il lessico

Alfabeto:
utilizzato per scrivere i programmi UNICODE
Parole chiave:
parole riservate che hanno un significato particolare
e non possono essere ridefinite
Parole chiave definite dallo standard ANSI

auto • double 
int 
struct

break • else 
long 
switch

case • enum 
registe 
typedef

char • extern r 
union

const • float

return 
unsigned

continue • for

short 
void

default • goto

signed 
volatile

do • if

sizeof 
while

static
Il lessico
Identificatori:
nomi usati per indicare variabili, funzioni etc.
Separatori:
caratteri che permettorno di separare o raggruppare
parti di codice: ( ) { } [ ] ; , .
Operatori:
denotano operazioni
Letterali:
sequenze di caratteri per rappresentari valori di tipi
primitivi : 1234 “questa è una stringa”
Il lessico

Commenti:
Programmare bene significa anche saper
scrivere codice ben commentato e leggibile

devono essere aperti e chiusi attraverso l’uso dei simboli


/* e */
oppure se di una sola riga preceduti da //

//commenti su una riga

/*Si possono inserire commenti su più


righe*/
Identificatori (nomi)

 Il C è un linguaggio case sensitive. Ad esempio il


carattere “A” è diverso dal carattere “a”, sono due
entità diverse e distinte.
 Un nome può iniziare con una lettera alfabetica e
continuare con lettere, cifre numeriche, simbolo di
sottolineatura
 NON inziare un nome con il simbolo di
sottolineatura (nomi di sistema)
 La lunghezza può essere un elemento critico;
generalmente la dimensione max è di 32 caratteri
Esempi
“BUONI” “CATTIVI”
Lettera_1 1lettera
cent double
CENT Joe's
number_of_student two*FOUR
precipitevolissimevolmenteprecipitevolissime

num_student
Caratteri vietati:
. , ; : + - = \/ * < > ! ? @ # % $ ^ & ' ~ parentesi e spazi
Sommario

Caratteristiche fondamentali
Il lessico
Istruzioni di base, strutture di controllo e
blocchi
Le variabili e i tipi primitivi
Gli operatori e le espressioni
Problem solving
Struttura di un programma

Dati (numeri interi o “reali”; caratteri alfanumerici; dati booleani):


 Variabili e costanti (variabilità nel tempo)
• count
 Tipi (elementari o strutturati)
• int
 classi di memoria (visibilità)
Istruzioni:
 istruzioni base
 strutture di controllo
 moduli
Istruzioni base

Assegnamento ed espressioni:
 comandi per assegnare un valore ad una
variabile direttamente o come valutazione di
sequenze di operatori e operandi

esempio:
– sum = 0
– interest = amount * 0.07;
Strutture di controllo

Costrutti condizionali: if...then...else


if(sum>0){
sum++;
}else{
sum=0;
}
Costrutti iterativi: for, while, do...while
while(sum){
sum++;}
Strutturazione a blocchi

Un blocco è un insieme di istruzioni logicamente
collegate delimitato da parentesi graffe { }

<istruzione>;
{<istruzione>; <istruzione>;}

Ogni istruzione per essere valida deve essere


terminata da un carattere “ ; ”
Norme di buona programmazione


scrivere istruzioni chiare, una per riga


evidenziare blocchi di istruzioni con le
parentesi graffe anche se consistente di un
solo comando


utilizzare l’indentazione dei diversi blocchi
del programma per una più facile lettura del
codice stesso.
Moduli


I programmi sono spesso abbastanza complessi da
dover essere scomposti in “pezzi” più maneggevoli

Un modulo consiste di istruzioni per svolgere un
certo compito raggruppate insieme in un’unità a cui è
dato un nome

il nome può essere usato come sostituto dell’intero insieme di
istruzioni

Vantaggi:

risparmio di scrittura, organizzazione, riutilizzo
Creare un programma
eseguibile


Editor
 Scrittura del programma sorgente
(nome_file.c)

Compilatore
 Produce codice oggetto scritto in linguaggio
assembler (nome_file.obj)

Linker
 Collega tra loro i moduli che costituiscono il
programma (files oggetto) e produce il
codice eseguibile (nome_file.exe)

Loader (fase di caricamento)
L'inizio del programma

main (non Main o MAIN) è una funzione speciale che
indica l’inizio dell’esecuzione del programma e deve
pertanto essere presente in ogni programma.


Le parentesi vuote dopo main significano che la funzione
non prende nessun parametro in input.

main()
{

}
Sommario

Caratteristiche fondamentali
Il lessico
Istruzioni di base, strutture di controllo e blocchi
Le variabili e i tipi primitivi
Gli operatori e le espressioni
Problem solving
Variabili e tipi


Variabile: locazione di memoria a cui è dato un
nome con cui chiamarla ed utilizzarla (contenitore di
dati)

programmatore usa il nome senza necessariamente
sapere che esso faccia riferimento ad una locazione di
memoria

Tipo: ogni variabile ha un tipo che indica che genere
di dati la variabile può contenere

una variabile può contenere dati di tipo intero (ad es., 15
o 2038), oppure dati di tipo carattere (ad es., ‘a’ o ‘£’)
 Le dimensioni delle variabili numeriche dipendono
dall’architettura dell’elaboratore sottostante

Classe di memoria: determina la durata di vita
della variabile.
Creazione di variabili

Tutte le variabili devono essere dichiarate prima di poterle
utilizzare


Una dichiarazione di variabile associa un nome alle locazioni
di memoria ad essa corrispondenti e specifica il tipo di dati che
la variabile conterrà:

Tipo Variabile_1, Variabile_2, …;


Per esempio, per creare tre variabili che memorizzino il numero
di cesti, il numero di uova per cesto ed il numero totale di uova:

int numberOfBaskets, eggsPerBasket, totalEggs;


Dichiarare una variabile
Dichiarazione e inizializzazione

Domanda: Che valore assume total dopo
la sua dichiarazione?

Risposta: Non si può dire: la dichiarazione
alloca spazio in memoria ma non dice niente
sul contenuto
Inizializzazione di una variabile

Operatore di assegnamento è: =

int total;
total=0;

Oppure
int total=0;

Definizione: dichiarazione con inizializzazione

il compilatore non segnala errore se si inizializza
una variable con un valore fuori range
Nomi significativi

I commenti non sono l'unico modo di rendere


leggibile il codice
Usate nomi di variabili che siano significativi
• int somma; //informativo!
Senza esagerare con la verbosità
• int indice_ciclo_for //?troppo
Tipi di dato

 La stessa stringa di bit può rappresentare


– Numeri interi positivi
– Numeri interi con segno
– Numeri razionali (reali)
– Valori logici
– Caratteri

 Per poterla interpretare è necessario


conoscerne il tipo
Tipi fondamentali

Rappresentano un singolo valore numerico


Tipo Descrizione

• char singolo carattere


• int intero
• float virgola mobile a singola precisione
• double virgola mobile a doppia precisione
Dettagli


Tipo 
Bit occupati • Intervallo

• char 8 0 – 255
• int 16 -32768 – 32767
• float 32 3.4E-38 – 3.4E+38
• double 64 1.7E-308 – 1.7E+308
• void 0 indefinito

L'operatore unario sizeof()permette di determinare


l'occupazione in memoria sulla propria piattaforma
char<= int <= float <= double
Notazione scientifica

“ammesse” “non ammesse”


15.e-04 1234e
2.345e2 1,135e2
1.15e-3 15e-0.3
0.25
3.
Caratteri

Un carattere è un simbolo utilizzato per rappresentare un
fonema o un numero
– N.B. : il carattere 8 è concettualmente diverso dal valore
8 (che si può rappresentare, ad esempio, in numeri
romani come VIII)

Nei computer alcuni elementi pseudo-grafici sono
rappresentati da caratteri
– CR: Carriage Return (torna all’inizio della riga)
– LF: Line Feed (avanza di una riga)
– BEL: Bell (campanello)

I caratteri alfabetici e quelli speciali (CR, LF, BEL, etc.) si
rappresentano associando ad ognuno di essi un numero
binario di 8 bit (codice ASCII) o 32 bit (Unicode)
Codice ASCII
Rappresentazione in virgola mobile

 I numeri si devono esprimere utilizzando il maggior


numero di cifre significative → virgola mobile
 I numeri si rappresentano in notazione esponenziale
con mantissa ed esponente
x = ±1.a1....am × 2E
 Si assegnano pochi bit all’esponente e il resto (m) alla
mantissa
 Il bit più alto è il segno della mantissa (0 pos,1 neg)
 La mantissa h è compresa tra 0 e 1-2-m
 La mantissa è in forma normale C+h,
dove C vale 0 o 1
Rappresentazione in virgola mobile

x=±1.a1....am × 2E
Segno della
Mantissa (s)

0 00000000 00000000000000000000000

Esponente (E) Mantissa (h)


3 Rappresentazione in eccesso a 127

Segno della
Mantissa

0 10000010 10101000000000000000000

Esponente Mantissa

1*2-1+1*2-3+1*2-5 =0.5+0.125+0.03125
=0.65625
3
Segno della
Mantissa

0 10000010 10101000000000000000000

Esponente Mantissa 0.65625


(1 + 0.65625)*23=13.25
Imprecisioni numeriche
Errore di arrotondamento:
0.1 non ha una rappresentazione finita in binario
0.000110011001100110011001100110011001100110011
0011...
La rappresentazione di un numero reale non è “densa”

Underflow e overflow:
– Tentativo di rappresentare un numero fuori dal range di
valori possibili

Errore di cancellazione:
– a+b vale a, se a>>b
Costanti

Una costante è una locazione di memoria di
un dato

Il valore immagazzinato non può variare
durante l’esecuzione di un programma

– Costanti carattere : ‘A’


– Costanti intere : 23
– Costanti con virgola (double) :123.456 ,
1.23e2, 100.
– Costanti stringa: “cane”
Costanti simboliche

Una costante simbolica è una costante che
è rappresentata da un nome

Una variabile con valore fissato

const float PI=3.14159265;


#define PI 3.14159265
Variabili e costanti in memoria

a = 2;
b = 3.5; 0xffffa001 00000010
c = 2.; 0xffffa002 ????????

Nota: per brevità 0xffffa003 ????????


le variabili intere 0xffffa004 ????????
sono rappresentate 0xffffa005 ????????
usando solo 8 bit 0xffffa006 ????????
0xffffa007 ????????
0xffffa008 ????????
0xffffa009 ????????
Variabili e costanti in memoria

a = 2;
b = 3.5; 0xffffa001 00000010
c = 2.; 0xffffa002 01000000

3.5 = (1 + 0.75)*21 0xffffa003 01100000


NOTA: l’esponente è 0xffffa004 00000000
rappresentato in 0xffffa005 00000000
eccesso 127
0xffffa006 ????????
0xffffa007 ????????
0xffffa008 ????????
0xffffa009 ????????
Variabili e costanti in memoria

a = 2;
b = 3.5; 0xffffa001 00000010
c = 2.; 0xffffa002 01000000

2. = (1 + 0)*21 0xffffa003 01100000


NOTA: l’esponente è 0xffffa004 00000000
rappresentato in 0xffffa005 00000000
eccesso (cap.1,p.19)
0xffffa006 01000000
0xffffa007 00000000
0xffffa008 00000000
0xffffa009 00000000
Variabili e costanti in memoria

a = 2; 0xffffa000 01100110
b = 3.5; 0xffffa001 00000010
c = 2.; 0xffffa002 01000000
f ↔ 102 (6616) 0xffffa003 01100000
0xffffa004 00000000
0xffffa005 00000000
0xffffa006 01000000
0xffffa007 00000000
0xffffa008 00000000
0xffffa009 00000000
Modificatori di tipo


I tipi di dato primitivi possono essere estesi
con l’uso di alcuni modificatori:
 Di dimensione: short, long
 Di “valutazione”: unsigned

Esempio:
 long int l;
 unsigned int i;
Tipi di dato scalare
Tipo Byte Range
int 4 da 231 a 2311
short 2 da 215 a 2151
long 4 da 231 a 2311
Numeri
interi
8 da 263 a 2631
unsigned int 4 da 0 a 2321
unsigned short 2 da 0 a 2161
unsigned long 8 da 0 a 2641
Carattere
(è rappresentato char 1 da 27 a 271
con un numero
intero!!!)
unsigned char 1 da 0 a 281

Numeri
float 4 7 cifre decimali
reali double 8 16 cifre decimali
enum 4 /
puntatore* 4 da 231 a 2311
8 da 263 a 2631
Sommario

Caratteristiche fondamentali
Il lessico
Istruzioni di base, strutture di controllo e blocchi
Le variabili e i tipi primitivi
Gli operatori e le espressioni
Problem solving
Operatori

Un operatore è un simbolo che istruisce C
di eseguire una operazione su uno o più
operandi.

Esistono tre classi di operatori:
 aritmetici (o matematici)
 logici e relazionali
 bit a bit.
Espressioni

Una espressione è una combinazione valida


di costanti, variabili e operatori.
Ha un valore numerico e un tipo.
• 2+5; /*somma tra costanti*/
• x=a+10; /* assegnazione di una
somma tra una costante e una
variabile*/
Operatori aritmetici binari

<op1> + <op2> Somma i due operandi

<op1> - <op2> Sottrae dal primo il secondo


operando

<op1> *<op2> Moltiplica gli operandi

<op1>/ <op2> Divide il primo con il secondo


operando

L’operatore % non può essere applicato ai tipi float,double


Assegnazione+operatore
aritimetico
• <op1> += <op2> <op1>=<op1>+<op2>
• <op1> -= <op2> <op1>=<op1>-<op2>
• <op1> *= <op2> <op1>=<op1>*<op2>
• <op1> /= <op2> <op1>=<op1>/<op2>
• <op1> %= <op2> <op1>=<op1>%<op2>

Ad es. int a=5, b=7;

a+=2; equivale a: a=a+2; (nota: a vale 7)


b/=3; equivale a: b=b/3; (nota: b vale 2)
Conversione di tipo

Nella valutazione di una espressione che


coinvolge variabili numeriche di tipi diversi, qual
è il tipo dell'espressione?

int a=2,b;
float c=3.5,d;
b=a+c;
d=c/a;
Quanto vale a+c? Quanto vale b? Quanto vale d?
Conversione di tipo

Il risultato dell'espressione dipende dalla combinazione


dei tipi utilizzati nell'espressione poichè avviene una
conversione implicita secondo le seguenti regole:
Ogni valore di tipo char o short viene convertito in int
Se l'espressione è ancora eterogenea si “promuove” il valore
di tipo inferiore in tipo superiore secondo la seguente
gerarchia:
int->long->float->double->long double
Nell'assegnamento: la conversione avviene sempre a
favore del tipo della variabile di sinistra
Se si tratta di una promozione non si ha perdita, altrimenti si
può avere perdita
Casting

Conversioni esplicite dette cast secondo la


sintassi:
(tipo) espressione
Esempio:
int somma,n;
float media;
media=(float)somma/n;
L'operatore di cast ha precedenza più alta degli
operatori binari e associa da destra a sinistra.
Assegnamento - Priorità
• x=(25-2*(10+(8/2)));
• i=j=k=0;

= ha priorità più bassa rispetto agli altri
operatori ed associa da destra a sinistra

Poiché anche l’assegnamento è un
operatore si possono scrivere contrazioni:
• z=(x=1)+(y=2);
Operatori speciali

a = 2; a = 2;
b = ++a; a=a+1;
b = a;

a = 2;
b = a++; a = 2;
b = a;
a=a+1;
Operatori - Priorità

Gli operatori possiedono regole di precedenze e


associatività che determinano la valutazione
delle espressioni.
++, –- hanno priorità massima
*, /, % hanno priorità intermedia
+,- hanno priorità minima
Espressioni con operatori dello stesso livello
sono valutati da sinistra verso destra
Esercizio:
w*x/y*z;w*x/y+z/y;w*x/++y+z/y;z-(a+b/2)+w*y

Usare le parentesi per rendere chiaro
l’ordine di valutazione dell’espressione

Non sovraccaricare l’espressione
 Spezzare un espressione complessa in più
espressioni
 Soprattutto se si utilizzano gli operatori (++) o
(--)
Sommario

Caratteristiche fondamentali
Il lessico
Istruzioni di base, strutture di controllo e blocchi
Le variabili e i tipi primitivi
Gli operatori e le espressioni
Problem solving
Problema: conversione di gradi
Fahrenheit in Celsius
Dobbiamo studiare le diverse temperature rilevate in
periodi diversi alcune delle quali sono espresse in
Fahrenheit, mentre altre in Celsius. Vorremmo avere
tutte le temperature in gradi Celsius.
Analisi: Conversione da gradi Fahrenheit a Celsius
Dati in ingresso: temperatura in Fahrenheit (tf)
Risultato: temperatura in Celsius (tc)
Formula: tc=(tf-32)x5/9
Algoritmo (da raffinare):

“Acquisisci” i dati

Calcola la conversione

“Visualizza” il risultato
Programmi e variabili

main() {
float tc, tf = 90., conv;
float offset = 32.;
conv = 5./9.;
tc = (tf – offset) * conv;
}

? 90. ?

tc tf conv
Programmi e variabili

main() {
float tc, tf = 90., conv;
float offset = 32.;
conv = 5./9.;
tc = (tf – offset) * conv;
}

? 90. ? 32.
tc tf conv offset
Programmi e variabili

main() {
float tc, tf = 90., conv;
float offset = 32.;
conv = 5./9.;
tc = (tf – offset) * conv;
}

? 90. 0.555 32.


tc tf conv offset
Programmi e variabili

main() {
float tc, tf = 90., conv;
float offset = 32.;
conv = 5./9.;
tc = (tf – offset) * conv;
}

32.22 90. 0.555 32.


tc tf conv offset
Test


Cosa accade se eseguo il programma?

Nulla, perché non ci sono istruzioni di output!
Input/Output

printf(“%f F = %f C\n”, tf, tc);


Input/Output

printf(“%f F = %f C\n”, tf, tc);


Input/Output

printf(“%f F = %f C\n”, tf, tc);

90.000000 F = ...
Input/Output

printf(“%f F = %f C\n”, tf, tc);

90.000000 F = 32.222222 C...


Input/Output

printf(“%f F = %f C\n”, tf, tc);

90.000000 F = 32.222222 C
>
Emissione dei dati attraverso
printf()

Questa funzione permette di formattare l’output

int printf(<stringa-formato>[,<espressione>]…)

 Emette la stringa indicata nel 1o parametro


 Se il 1o parametro contiene metavariabili, queste formattano
ordinatamente i parametri successivi
printf(“Totale fatturato:%d\n”,12345);
 Restituisce il numero di caratteri emessi
Ancora su printf()


La stringa di formato può essere costituita
da tre componenti:

(obbligatoria) una costante stringa ;

(opzionali) metavariabili e sequenze di
escape
Opzioni di visualizzazione di
caratteri
Principali caratteri di conversione
(output)
descrittore tipo

%f,%e float,double

%d,%i int

%c char (singolo)

%s char (stringa)
Principali codici di escape

codice descrizione

\n newline

\t Tabulazione oriz.

\a alert

\ooo Notazione ottale


Se si vuole visualizzare soltanto una stringa
costante, è preferibile utilizzare la funzione:
int puts(const char *str)
Viene inserito un carattere di newline dopo la
linea visualizzata
Esempio:
puts(“Hello world!”);
Acquisizione dei dati attraverso
scanf()

Questa funzione permette di immettere da tastiera
l’input
int scanf(<stringa-formato>,&var1,[lista
destinazioni])

 Copia ordinatamente in memoria i valori digitati da tastiera


nelle variabili contenute in lista destinazioni
 Le variabili nella lista delle destinazioni devono essere
precedute da & che ne indica l'indirizzo
scanf(“%d %lf”,&count,&average);
 Restituisce il numero di dati acquisiti
Input/Output

scanf(“%f”, &tf);
Input/Output

scanf(“%f”, &tf);

90
Input/Output

scanf(“%f”, &tf);

90 = (1+0.40625)26

010000101011010000000000...
(1/4 + 1/8 + 1/32)
Input/Output

scanf(“%f”, &tf);

90 = (1+0.40625)26

010000101011010000000000...

90.
Principali caratteri di conversione
(input)
descrittore tipo

%f float
%lf double

%d,%i int

%c char (singolo)

%s char (stringa)
scanf()

ATTENZIONE:
Se si inseriscono spazi o invio nell'immissione
di più caratteri (tra un carattere e un altro) è
necessario inserire anche questi nella stringa
di formato tra i segnaposto %c
scanf(“ %c”,&ch);
Lo spazio prima del segnaposto cattura lo
spazio, tabulazione orizzontale, newline
Importanza della
rappresentazione

tf =(tc – offset) * 5./ 9.;


(tc – offset) * 5./ 9;
(tc – offset) * 5 / 9;

tf = 5 / 9 * (tc – offset);
Preelaborazione
Direttive al preprocessore
Preprocessore: programma che “esegue” istruzioni
dette direttive di inclusione e definizione
#include
Consente di includere il contenuto di un altro
file o librerie
#define
Definisce un simbolo (Attenzione!!!)
#ifdef, #ifndef, #else
Compilazione condizionale
#include

#include <stdio.h>
per input/output

#include <stdlib.h>
allocazione dinamica della memoria,
generazione numeri casuali, exit, time

#include <math.h>
libreria matematica
 sin(x), cos(x), sqrt(x), exp(x),
pow(x,y),
log(x), fabs(x), iabs(x)
#define NOME MACRO

Sostituisce ogni occorrenza di NOME con MACRO


Utile per definire costanti simboliche:
es. #define MAX 10+5
Attenzione: se è presente l'istruzione:
b=a*MAX diventa b=a*10+5 (e non b=a*(10+5))
Permette di specificare testo parametrico:
es. #define SCATOLA(X) X scatole di cioccolatini
Quindi SCATOLA(100) diventa: 100 scatole di cioccolatini
IDE


Un Integrated Development Environment (IDE), in italiano
ambiente integrato di sviluppo, è un software che aiuta
i programmatori nello sviluppo del codice


Esempi:
 Dev-C++ (Windows)
 Eclipse (Linux, ma anche Windows e Mac)
 XCode (Mac)
 ...


Compilatore e IDE sono la stessa cosa? NO!
 Negli esempi indicati, il compilatore è sempre gcc!
 Tali IDE integrano un editor di testi che agevola il programmatore, per poi
lanciare “in modo trasparente” il compilatore gcc, esattamente come
abbiamo descritto qualche slide fa!

Sono solo programmi di supporto!
 In alcuni casi il compilatore non è “scindibile” dall’IDE (es: Visual Studio)
Esempio di IDE
Click sul tasto “Compila”

Esecuzione di gcc!