Vai al contenuto
Appunti di InformaticaUniTo · 2026/27

Programmazione I · Teoria · Canale B · Lezione 02B

Memoria e variabili, assegnamento ed espressioni

Che cosa c'è dietro int x = 10? Un nome, un tipo, una casella di memoria e un valore. Qui impari a disegnare la memoria di un programma C riga per riga, come negli esercizi d'esame sullo stato della memoria, e a fare i primi conti con gli interi, sapendo dove il C ti sorprende: numeri troppo grandi, virgole che spariscono.

Slide
02B_assegnamento_memory_model («Memoria e Variabili») · 41 pagine
Corso
Prof. Elvio Amparore · A.A. 2026/27
Tempo di studio
90–120 minuti, meglio con gcc a portata di mano
  • Definizione o risultato da sapere
  • Trappola / errore tipico
  • Conta all'esame
  • Oltre le slide
  • L'idea e il metodo, passo per passo
  • Da ricordare

La lezione in 8 punti

  • Un tipo dice come leggere i bit in memoria: quanti byte occupano, quali valori sono ammessi, quali operazioni si possono fare. Senza tipi ci sarebbero solo sequenze di 0 e 1.
  • Una variabile ha quattro cose: un nome (contatore), un tipo (int), un indirizzo (dove sta in memoria) e un valore (che cosa contiene adesso). L'indirizzo lo sceglie il sistema, non tu.
  • I tipi numerici del C sono finiti: int contiene solo gli interi da circa −2,1 miliardi a +2,1 miliardi, float approssima i reali con un numero limitato di cifre.
  • Con la dichiarazione int x; si riserva lo spazio per x in una zona della memoria chiamata stack (pila). Il valore iniziale è indeterminato: negli schemi si scrive «?».
  • Nel modello della memoria ci sono un'area codice e un'area dati. PC indica l'istruzione da eseguire, FP l'inizio delle variabili di main, SP la prossima casella libera. Lo stack si disegna in alto e cresce verso il basso.
  • L'assegnamento variabile = espressione; calcola prima il valore a destra, poi lo copia nella casella a sinistra.
  • Sugli interi: +, -, *, / (divisione intera: 13 / 3 fa 4) e % (resto: 13 % 3 fa 1). *, /, % vengono prima di + e -, da sinistra a destra. printf usa gli specificatori: %d per int, %f per float, %zu per size_t.
  • Scorciatoie: x += 3 vuol dire x = x + 3 (e così -=, *=, /=, %=); x++ aumenta x di 1, x-- lo diminuisce di 1.

Canali A, B e C

Canale A (Fiandrotti). Il deck 2025/26 «Assegnazione, memory model, scambio» (55 slide) ha gli stessi contenuti di questa lezione e in più lo scambio di due variabili (swap), che nel canale B 2025/26 era una lezione a parte, la settimana dopo. Il deck 2026/27 del canale A per questa settimana non l'ho ancora visto.

Canale C (Mazzei). Tipi, variabili, assegnamento, modello della memoria con FP e SP e scambio sono nella lezione 02 «Introduzione al C» (70 slide), insieme a main, printf e gcc.

Canale B 2025/26. La lezione «Memoria e Variabili, operatore di assegnamento» (39 slide) era quasi uguale a questa.

L'esame è unico per i tre canali, e gli esercizi sullo stato della memoria partono proprio da questo modello.

§1

Dai bit ai tipi

slide 2–3

In memoria ci sono solo bit. Prendi questi 8 bit:

0100 0001

Che cosa sono? Dipende da come li leggi:

  • come numero intero sono 65 (64 + 1, ricordi la base 2 della lezione 02 di Fondamenti);
  • come carattere sono la lettera A, perché nel codice ASCII la A ha il numero 65;
  • come pezzo di un numero più lungo non vogliono dire niente da soli.

I bit da soli non bastano: serve una regola per leggerli. Questa regola è il tipo. Le slide lo dicono così: un tipo è un ponte tra i bit e il senso che vogliamo dare ai dati.

Un tipo del C stabilisce sei cose (slide 3):

Che cosaEsempio
nome, usato nel codiceint, float, struct Persona
dimensione in memoriaint di solito 4 byte, double 8 byte
dominio: i valori ammessiint: gli interi in un certo intervallo
operazioni consentitesomma tra interi, radice quadrata sui reali
rappresentazione dei bitcomplemento a 2 per gli interi, IEEE 754 per i reali
compatibilità con altri tipiregole di conversione automatiche e con il cast

La rappresentazione dei bit (complemento a 2, IEEE 754) la studi in Fondamenti dell'Informatica (lezione 03) e in Architettura degli Elaboratori. Qui ti basta sapere che esiste.

Prova tu

Gli stessi 8 bit 0011 0000 letti come intero valgono 48. Che cosa sono letti come carattere ASCII?

Il carattere 0 (la cifra zero): nella tabella ASCII le cifre partono dal codice 48. È un esempio di come lo stesso contenuto cambi senso cambiando il tipo.

Da ricordare

Un tipo dice come leggere i bit: quanti byte, quali valori, quali operazioni, come sono codificati, come si convertono in altri tipi.

§2

Le variabili

slide 4–6

Nella macchina di Von Neumann della lezione 02A le istruzioni lavoravano sugli indirizzi: LOAD, R0, @400. Programmare così vuol dire ricordarsi a memoria che all'indirizzo 400 c'è il prezzo e al 404 la quantità. I linguaggi di alto livello danno un nome a quelle caselle: invece di «la casella 400» scrivi prezzo. Questo nome con il suo tipo è una variabile.

Pensa a un cassetto di una cassettiera:

  • sull'etichetta c'è scritto il nome;
  • la forma del cassetto dice che cosa ci può stare: è il tipo;
  • la posizione del cassetto nella cassettiera è l'indirizzo;
  • quello che c'è dentro adesso è il valore, e può cambiare.

Definizione · Variabile (slide 4–5)

Una variabile associa un nome e un tipo a una porzione di memoria che contiene un valore. È definita da: nome, tipo, indirizzo (la posizione in memoria) e valore (il contenuto attuale, che può cambiare durante l'esecuzione).

Come si legge. Una variabile è una casella di memoria a cui dai un nome. Il tipo dice quanti byte occupa e come leggerli. Il valore è quello che c'è dentro in questo momento.

L'esempio della slide 5:

int contatore = 1;
float media = 60.54;
contatoremedia
nomecontatoremedia
tipointfloat
indirizzo (esempio)0x904A0x367F
valore160.54

Gli indirizzi si scrivono di solito in esadecimale, con il prefisso 0x (lezione 01 di Fondamenti). Quelli della tabella sono inventati: l'indirizzo vero lo decidono il compilatore e il sistema quando il programma parte, e cambia da un'esecuzione all'altra. Il programmatore non lo sceglie: sceglie il tipo, cioè quali valori può contenere la variabile, come sono codificati, quali operazioni si possono fare e come interagisce con altri tipi (slide 6).

Nota · «Solo in parte automatica»

La slide 6 avverte che in C la gestione della memoria è solo parzialmente automatica. Le variabili di questa lezione nascono e spariscono da sole. Più avanti nel corso vedrai la memoria che chiedi e restituisci tu, con malloc e free.

Prova tu

Quali delle quattro proprietà di una variabile possono cambiare mentre il programma gira?

Solo il valore. Nome, tipo e indirizzo restano gli stessi per tutta la vita della variabile.

Da ricordare

Variabile = nome + tipo + indirizzo + valore. Tu scegli nome e tipo; l'indirizzo lo sceglie il sistema; il valore cambia con gli assegnamenti.

§3

I tipi del C

slide 7–9, 12

La slide 7 mostra la famiglia completa dei tipi del C:

  • tipi primitivi (già pronti nel linguaggio): interi, naturali (senza segno), reali;
  • tipi derivati, costruiti da altri: puntatori, array, struct, union, enum, funzioni;
  • void, il tipo «vuoto»;
  • typedef, per dare un nome nuovo a un tipo;
  • qualificatori come const, che si aggiungono a un tipo.

Per ora ne bastano pochi (slide 8):

TipoPer che cosaNota
intnumeri interi ()ci sono anche short, long, long long
unsigned intnumeri naturali (), senza segno
floatnumeri con la virgola ()c'è anche double, più preciso
charcaratterioccupa 1 byte (8 bit)
size_tdimensioni, numeri di elementiintero senza segno, da <stddef.h>
boolvero o falso (true/false, cioè 1/0)da <stdbool.h>

La slide 12 li raggruppa per uso: numeri naturali (le versioni unsigned), interi (short, int, long, long long), reali (float, double), dimensioni (size_t, ptrdiff_t), caratteri e byte (char, unsigned char), logico (bool).

I numeri del C sono finiti (slide 9)

Gli insiemi della matematica , e sono infiniti. Una variabile occupa un numero fisso di byte, quindi un tipo può contenere solo un numero finito di valori.

Un int occupa di solito 4 byte, cioè 32 bit: le combinazioni possibili sono , circa 4,3 miliardi. Metà vanno ai negativi e metà allo zero e ai positivi:

Un unsigned int usa gli stessi 32 bit solo per lo zero e i positivi: da 0 a .

Per i reali il limite è un altro: float e double hanno un numero limitato di cifre, quindi molti numeri vengono approssimati. La lezione 03 di Fondamenti mostra come funziona la virgola mobile.

L'idea · Un contachilometri

Un contachilometri a 6 cifre arriva a 999 999 e poi torna a 000 000. Un int fa qualcosa di simile quando supera il valore massimo: è l'overflow, che vedi nella sezione sull'assegnamento.

Prova tu

Quanti valori diversi può contenere un tipo da 1 byte? E da 2 byte?

1 byte = 8 bit: valori. 2 byte = 16 bit: valori.

Da ricordare

I tipi del C sono modelli finiti dei numeri della matematica: gli interi hanno un minimo e un massimo, i reali hanno una precisione limitata.

§4

Dichiarare una variabile

slide 10

Prima di usare una casella bisogna chiederla, dicendo il nome e il tipo:

int x;

Questa riga è una dichiarazione: dice al compilatore che esiste una variabile di nome x e di tipo int. Quando il programma arriva a questa riga, riserva lo spazio per un int (4 byte) in una zona della memoria chiamata pila o stack. Da quel momento puoi usare la casella con il nome x.

Il nome x esiste solo nel sorgente C. Nell'assembly della lezione 02A non ci sono nomi: il compilatore trasforma ogni nome in un indirizzo. Per questo le slide dicono che l'identificatore è simbolico.

Prova tu

Dopo int voti; il programma sa già quanti byte occupa voti? Sa già che valore contiene?

Sa quanti byte occupa: il tipo int lo dice (di solito 4). Non sa che valore contiene: nessuno ce l'ha ancora scritto.

Da ricordare

Ogni variabile va dichiarata con tipo e nome prima di usarla. La dichiarazione riserva lo spazio sullo stack.

§5

Stampare i valori con printf

slide 11, 37–38

La prima cosa che si fa con una variabile è guardarne il valore.

int x = 10;          // variabile intera
float y = 3.14f;     // variabile reale (in virgola mobile)
printf("La variabile x vale: %d\n", x);   // %d → int
printf("La variabile y vale: %f\n", y);   // %f → float

Stampa:

La variabile x vale: 10
La variabile y vale: 3.140000

printf riceve prima una stringa di formato, poi i valori da stampare, separati da virgole. Dentro la stringa:

  • il testo normale viene stampato così com'è;
  • ogni specificatore di formato (%d, %f, …) è un posto da riempire: il primo specificatore prende il primo valore dopo la stringa, il secondo il secondo, e così via.
printf("x vale %d, y vale %d\n", x, y);

Il primo %d stampa x, il secondo %d stampa y (slide 37).

%f stampa sei cifre dopo la virgola: per questo 3.14 diventa 3.140000. La f in fondo a 3.14f dice che la costante è un float; senza, 3.14 sarebbe un double.

Larghezza e zeri (slide 38)

IstruzioneStampa (con x = 7)Che cosa fa
printf("x vale %d\n", x);x vale 7quante cifre servono
printf("x vale %6d\n", x);x vale 7almeno 6 posizioni, spazi a sinistra
printf("x vale %06d\n", x);x vale 000007almeno 6 posizioni, zeri a sinistra

Risultati provati con gcc 16.1. Tutti i formati sono nel manuale della libreria GNU C (link nella slide 38 e nelle fonti).

Trappola · Lo specificatore deve corrispondere al tipo

printf("%f", x) con x di tipo int è un errore: printf legge i bit dell'intero come se fossero un reale e stampa un numero senza senso. Con -Wall -Werror gcc lo blocca:

error: format '%f' expects argument of type 'double', but argument 2 has type 'int' [-Werror=format=]

(printf riceve i float già convertiti in double: per questo il messaggio parla di double.)

Prova tu

Che cosa stampa printf("%d + %d = %d\n", 2, 3, 2 + 3);?

2 + 3 = 5. Tre specificatori, tre valori nell'ordine; il terzo è il risultato dell'espressione 2 + 3.

Da ricordare

printf("testo con %d", valore): uno specificatore per ogni valore, nello stesso ordine, e del tipo giusto (%d int, %f float, %zu size_t, %c char).

§6

Quanto spazio occupa un tipo: sizeof

slide 13–15

Il C non fissa la dimensione esatta dei tipi, perché deve funzionare su macchine molto diverse, dai microcontrollori ai server. Fissa solo un ordine:

sizeof è un operatore che dice quanti byte occupa un tipo o una variabile. Il risultato ha tipo size_t, e si stampa con %zu:

printf("int occupa %zu byte\n", sizeof(int));

Le slide 14–15 danno una tabella di riferimento per le macchine a 32 e 64 bit, con intervalli e specificatori. Le righe principali:

TipoByte (tipico)IntervalloSpecificatore
char1da −128 a 127%c (come carattere)
short2da a %hd
int4da a %d
long4 o 8dipende dalla macchina%ld
long long8da a %lld
unsigned int4da 0 a %u
size_t4 (32 bit) o 8 (64 bit)da 0 in su%zu
float4circa da a %f
double8circa da a %lf (in scanf)

La slide 15 dice esplicitamente: non serve impararle a memoria. Servono gli specificatori più usati (%d, %u, %f, %c, %zu) e l'idea che ogni tipo ha un intervallo.

Nota · Su Windows long è da 4 byte

La tabella delle slide dà long da 8 byte sulle macchine a 64 bit. È vero su Linux e macOS, ma non su Windows: sul mio PC (Windows 11 a 64 bit, gcc 16.1) sizeof(long) vale 4. È proprio il motivo per cui il C fissa solo l'ordine tra i tipi e non le dimensioni. Sullo stesso PC: char 1, short 2, int 4, long long 8, float 4, double 8, bool 1, size_t 8.

Nota · Due refusi nella tabella

Nella slide 14 lo specificatore di ptrdiff_t è scritto %t: quello vero è %td. Nella slide 15 compare «unsigned signed int», che non è un tipo del C: il nome è unsigned int.

Prova tu

Che cosa stampa printf("%zu\n", sizeof(char)); su qualunque macchina?

1. Lo standard del C fissa sizeof(char) uguale a 1: il char è l'unità con cui si misurano gli altri tipi.

Da ricordare

sizeof(tipo) dà i byte, di tipo size_t, stampati con %zu. Il C garantisce solo l'ordine char ≤ short ≤ int ≤ long ≤ long long: le dimensioni vere dipendono dalla macchina.

§7

I nomi delle variabili

slide 16–17

Le regole per gli identificatori (lezione 02A):

  • possono contenere lettere, cifre e trattino basso _;
  • devono cominciare con una lettera o con _ (di solito una lettera minuscola);
  • niente spazi né simboli speciali;
  • devono essere unici nel loro ambito: due variabili nello stesso blocco non possono avere lo stesso nome.

Esempio della slide 16: int laMiaPrimaVariabile;.

Oltre alle regole ci sono le convenzioni del corso (slide 17):

  • tutto minuscolo, oppure camelCase per i nomi di più parole: sommaTot, media7giorni, elementoTrovato (la prima parola minuscola, le altre con l'iniziale maiuscola);
  • nomi che dicono il ruolo del dato: somma, media, massimoTrovato;
  • nomi corti come s, m, flag solo quando il senso è chiaro dal contesto;
  • mai nomi senza senso come pippo o nonSoCheNomeDare.

Il nome non è un dettaglio: un buon nome rende il codice leggibile, riduce gli errori e aiuta a ragionare. All'esame il codice viene anche letto da un docente.

Trappola · Un nome che comincia con una cifra

int 2x = 3; non compila. gcc 16.1:

error: invalid suffix 'x' on integer constant

Il compilatore legge 2 come un numero e x come un «suffisso» sbagliato del numero.

Prova tu

Quali di questi nomi sono validi? media7giorni, 7giorni, somma_tot, somma tot.

Validi: media7giorni (la cifra non è all'inizio) e somma_tot. Non validi: 7giorni (comincia con una cifra) e somma tot (contiene uno spazio).

Da ricordare

Lettere, cifre e _, senza cominciare con una cifra. Nomi che dicono il ruolo, in camelCase: sommaTot, elementoTrovato.

§8

Dove e per quanto vive una variabile

slide 18

Una variabile ha due proprietà in più, legate a dove e quando esiste.

La visibilità (scope) dice in quale parte del codice puoi usare il nome:

  • locale (o automatica, il caso normale): dichiarata dentro una funzione o un blocco { … }, si usa solo lì dentro;
  • globale: dichiarata fuori da tutte le funzioni, si vede in tutto il file (e in altri file, se dichiarata extern);
  • statica: con static, conserva il valore tra una chiamata e l'altra ma resta visibile solo nel suo blocco.

La durata (lifetime) dice per quanto tempo la variabile occupa memoria:

  • le variabili locali stanno sullo stack e spariscono quando finisce il blocco;
  • le variabili globali e statiche esistono per tutto il programma;
  • le variabili dinamiche stanno in un'altra zona, lo heap: le crei tu con malloc() e restano finché non le liberi (più avanti nel corso).

Prova tu

Una variabile dichiarata dentro le graffe di main è locale o globale? Dove sta in memoria?

È locale: si vede solo dentro main. Sta sullo stack e sparisce quando main finisce.

Per capire il C bisogna sapere come la memoria viene presa e restituita. Il corso usa per questo un modello della memoria, che vedi nella prossima sezione.

Conta all'esame · Niente static e niente globali

Nelle regole d'esame (scheda del corso) static è vietato negli esercizi di programmazione. Anche le variabili globali non servono: usa solo variabili locali e parametri.

Da ricordare

Locali: visibili nel loro blocco, vivono sullo stack finché il blocco dura. Globali e statiche: vivono per tutto il programma. Dinamiche: nello heap, finché non le liberi.

§9

Il modello della memoria

slide 19–26

Negli esercizi d'esame sullo stato della memoria si disegna la memoria di un programma riga per riga. Il modello è questo.

Area codice e area dati (slide 19–21)

Quando lanci un programma, il sistema lo carica dal disco in memoria centrale e gli riserva una zona divisa in due:

  • l'area codice, con le istruzioni del programma: ha una dimensione fissa;
  • l'area dati, con le variabili: può crescere, ma fino a un massimo.

Le variabili vengono messe nell'area dati una dopo l'altra, nell'ordine in cui sono dichiarate. Nel disegno ogni riga è una parola di 32 bit (4 byte).

Tre indirizzi speciali (slide 22)

Tre registri della CPU tengono il segno di dove si è arrivati:

NomeSiglaChe cosa indica
program counterPCl'istruzione da eseguire, nell'area codice (all'inizio: la prima istruzione di main)
frame pointerFPl'indirizzo da cui cominciano le variabili di main
stack pointerSPl'indirizzo in cui verrà messa la prossima variabile

All'inizio, prima di qualunque dichiarazione, FP e SP coincidono.

Lo stack cresce verso il basso (slide 23–24)

La parte dell'area dati che contiene le variabili si chiama stack (pila). Nel modello si disegna in alto e cresce verso gli indirizzi più bassi: ogni nuova variabile va sotto la precedente.

È come una pila di piatti appesa al soffitto: ogni piatto nuovo si aggiunge sotto. Quando dichiari int x;, SP scende di 4 byte (la dimensione di un int) e la casella tra il vecchio e il nuovo SP diventa x.

Nota · Una convenzione di disegno

La direzione in cui cresce lo stack dipende dal processore e dal sistema operativo. Nel corso si usa sempre questa: stack in alto, crescita verso il basso (slide 23).

Se le variabili sono troppe e lo spazio dello stack finisce, il programma si interrompe con un errore a runtime di tipo stack overflow (slide 24).

Il valore iniziale è «?» (slide 25)

int main(void) {
    int x;
}

Dopo int x; lo spazio è riservato, ma nessuno ci ha scritto: dentro c'è quello che era rimasto in quei byte da prima. Il valore è indeterminato, e negli schemi si scrive «?».

Ho provato a stamparlo:

#include <stdio.h>
int main(void) {
    int x;
    printf("x vale %d\n", x);
    return 0;
}

Senza -Werror gcc avvisa (warning: 'x' is used uninitialized) e il programma stampa un numero a caso, sul mio PC x vale 32758. Con -Wall -Werror non compila:

error: 'x' is used uninitialized [-Werror=uninitialized]

Trappola · «?» non vuol dire 0

Una variabile locale non inizializzata non vale 0: vale quello che capita. Negli esercizi sullo stato della memoria, una casella mai assegnata si scrive «?», e un calcolo che la usa dà «?».

Più variabili (slide 26)

int main(void) {
    int x;
    int y;
    int z;
}

Tre int da 4 byte: 12 byte, nell'ordine di dichiarazione. Lo stato dopo l'ultima dichiarazione:

IndirizzoContenutoNome
FP − 4?x
FP − 8?y
FP − 12?z ← SP

(In alto gli indirizzi più grandi: la riga di x è la prima sotto FP, e SP è sceso di 12 byte.)

Prova tu

Dopo int a; int b; di quanti byte è sceso SP rispetto a FP? E in che riga sta b?

Di 8 byte (due int da 4). b sta sotto a, all'indirizzo FP − 8, perché è stata dichiarata dopo.

Da ricordare

Area codice fissa, area dati con lo stack in alto che cresce verso il basso. PC = istruzione corrente, FP = inizio delle variabili di main, SP = prossima casella libera. Variabili in ordine di dichiarazione, valore iniziale «?».

§10

L'assegnamento

slide 27–29

Una casella con «?» non serve a molto. Per metterci un valore si usa l'operatore =:

int x;
x = 10;

Oppure tutto in una riga, dichiarazione e primo valore insieme: è una definizione con inizializzazione.

int x = 10;

Come funziona (slide 29)

La forma generale è:

variabile = espressione;

Si esegue in tre passi:

  1. si calcola l'espressione a destra (in gergo: viene valutata);
  2. il calcolo produce un valore;
  3. il valore viene copiato nella casella della variabile a sinistra; quello che c'era prima va perso.

I due lati hanno nomi tecnici:

  • il lato sinistro è l'lvalue (da left value): deve indicare una casella di memoria, cioè una variabile;
  • il lato destro è l'rvalue (right value): un valore o un'espressione da calcolare.

Per questo x = x + 1 ha senso in C: prima si calcola x + 1 con il valore vecchio, poi il risultato va in x. Non è un'equazione, è un ordine: «metti in x il valore di x + 1». E per questo 10 = x non compila: 10 non è una casella.

Trappola · = non è il confronto

In C = assegna. Per chiedere «sono uguali?» si usa ==, che vedrai con le condizioni. Nello pseudocodice della lezione 01A l'assegnamento si scriveva con la freccia ←.

Quando il valore non ci sta (slide 28)

L'assegnamento deve rispettare il tipo. Tre casi:

IstruzioneChe cosa succedeStampa
int x = -8;normalex vale -8
int x = 2147483649;il numero supera il massimo di int (): overflowx vale -2147483647
int x = 8.89;un int non ha la virgola: la parte decimale viene tagliatax vale 8

Ho compilato gli ultimi due con gcc 16.1 e -Wall -Werror: compilano tutti e due senza nessun avviso, e stampano esattamente quello che dicono le slide. Il compilatore non ti protegge: le slide parlano di overflow «non rilevabili dal compilatore».

  • Nel primo caso il numero 2 147 483 649 è : per entrare in 32 bit «gira» come il contachilometri e diventa .
  • Nel secondo 8.89 diventa 8: la virgola viene troncata, non arrotondata (anche 8.99 diventerebbe 8).

Nota · Gli spazi nel numero

La slide 28 scrive int x = 2 147 483 649; con gli spazi per leggerlo meglio. In C un numero non può contenere spazi: nel codice va scritto 2147483649.

Oltre le slide · Un avviso che -Wall non dà

gcc può avvisare della virgola persa con l'opzione -Wconversion, che non fa parte di -Wall: warning: conversion from 'double' to 'int' changes value from '8.89…' to '8'. All'esame si usa solo -Wall -Werror, quindi conta saperlo tu.

Prova tu

Dopo int a = 5; a = a * 2 + 1; quanto vale a?
  1. Si calcola prima la destra con il valore vecchio: , poi 11 va in a.

Da ricordare

variabile = espressione;: prima si calcola la destra, poi il valore si copia a sinistra. Il valore deve stare nel tipo: un intero troppo grande «gira» (overflow), un reale messo in un int perde la parte decimale. gcc non lo segnala.

§11

Le espressioni con gli interi

slide 30–32

Per ora solo espressioni con variabili intere. Gli operatori:

OperatoreChe cosa faEsempioRisultato
+somma7 + 29
-sottrazione7 - 25
*moltiplicazione7 * 214
/divisione intera7 / 23
%resto della divisione (modulo)7 % 21
( )raggruppa(7 + 2) * 218

Esempi della slide 30:

int incasso = biglietti * prezzoBiglietto;
int utile = incasso - costoSala;

Divisione intera e resto (slide 32)

Dividere 13 caramelle in 3 sacchetti uguali: in ogni sacchetto ne vanno 4 e ne avanza 1.

int a = 13, b = 3;
int quot = a / b;    // 4
int r = a % b;       // 1, perche' 13 = 3*4 + 1
  • / tra due interi dà solo la parte intera del quoziente. La parte decimale viene scartata, non arrotondata: 7 / 2 fa 3, non 3,5 e non 4.
  • % dà il resto: quello che avanza.

Il resto serve molto per la parità: se n % 2 vale 0 il numero è pari, se vale 1 è dispari (per n positivo o zero).

Oltre le slide · Con i numeri negativi

Il C tronca verso lo zero: -13 / 3 fa −4 (non −5) e -13 % 3 fa −1. Vale sempre (a / b) * b + a % b == a. Per questo il controllo di parità di un negativo dispari dà −1, non 1: meglio scrivere n % 2 != 0 per «dispari». Risultati provati con gcc 16.1.

Trappola · Divisione per zero

x / 0 e x % 0 tra interi compilano, ma all'esecuzione il programma si interrompe (lezione 02A, errori a runtime).

Chi viene prima (slide 31)

Quando un'espressione ha più operatori, contano le precedenze:

  1. le parentesi ( ) prima di tutto, anche annidate;
  2. poi *, /, %;
  3. poi + e -;
  4. a parità di livello, da sinistra a destra;
  5. l'assegnamento = per ultimo.
EspressioneCome si calcolaRisultato
2 + 3 * 4prima 3 * 414
(2 + 3) * 4prima la parentesi20
20 / 4 * 2da sinistra: 20 / 4 = 5, poi 5 * 210
7 / 2 * 27 / 2 = 3, poi 3 * 26

Risultati provati. L'ultima riga mostra che con gli interi 7 / 2 * 2 non torna 7: la divisione ha già perso il resto.

Prova tu

Quanto vale 10 - 4 % 3 * 2?
  1. Prima % e * da sinistra: 4 % 3 = 1, poi 1 * 2 = 2. Poi la sottrazione: 10 - 2 = 8.

Da ricordare

Tra interi / taglia la parte decimale e % dà il resto. Ordine: parentesi, poi * / %, poi + -, da sinistra a destra, e = alla fine.

§12

Un programma seguito nella memoria

slide 33–36

Il programma «treVariabili» delle slide, eseguito riga per riga:

#include <stdio.h>

int main(void) {
    int x;
    int y;
    int z;
    x = 5;
    y = 2;
    z = x + y;
    printf("Le var x, y  e z valgono %d, %d, %d\n", x, y, z);
}
Dopo la rigax (FP − 4)y (FP − 8)z (FP − 12)Che cosa è successo
int x; int y; int z;???12 byte riservati, SP è sceso a FP − 12
x = 5;5??assegnamento di un numero
y = 2;52?
z = x + y;527si leggono x e y, si somma, si scrive in z
printf(…)527stampa Le var x, y e z valgono 5, 2, 7

Nella slide 35 la riga z = x + y; è affiancata dalle istruzioni assembly che fa davvero la macchina, come nella lezione 02A:

LOAD,  R0, @x      ; copia x in un registro
LOAD,  R1, @y      ; copia y in un altro registro
ADD,   R0, R1      ; somma nei registri
STORE, R0, @z      ; scrive il risultato nella casella di z

Una riga di C diventa quattro istruzioni macchina. Il nome x è diventato l'indirizzo @x.

Metodo · Disegnare lo stato della memoria

  1. Una riga per ogni variabile, nell'ordine di dichiarazione, sotto FP.
  2. Al momento della dichiarazione scrivi «?».
  3. A ogni assegnamento: calcola la destra con i valori attuali, poi sostituisci il valore della variabile a sinistra.
  4. Le altre caselle non cambiano.
  5. Se un calcolo usa una casella «?», il risultato è «?».

Prova tu

Disegna lo stato dopo int a = 4; int b; b = a % 3; a = a + b;.

a = 4, b = ?; poi b = 4 % 3 = 1; poi a = 4 + 1 = 5. Stato finale: a = 5, b = 1.

Da ricordare

Lo stato della memoria cambia solo con gli assegnamenti, una casella alla volta. Una riga di C come z = x + y; corrisponde a più istruzioni macchina: LOAD, LOAD, ADD, STORE.

§13

Tutti i modi di dare un valore

slide 39–41

La slide 39 riassume i quattro modi di mettere un valore in una variabile:

ModoCodiceNota
inizializzazione nella dichiarazioneint x = 10;valore dato subito
assegnamento dopoint x; poi x = 10;in mezzo x vale «?»
da tastieraint n; poi scanf("%d", &n);serve la & (Lab01)
assegnamento multiploa = b = c = 0;si legge da destra: c = 0, poi b = c, poi a = b

L'assegnamento multiplo funziona perché un assegnamento è anche un'espressione che vale il valore assegnato: c = 0 vale 0, e quel valore va in b, e così via.

Assegnamenti composti (slide 40)

Capita spesso di aggiornare una variabile partendo dal suo valore: somma = somma + voto. Il C ha una scorciatoia:

OperatoreVuol direEsempioRisultato
x += yx = x + yx = 5; x += 3;8
x -= yx = x - yx = 5; x -= 2;3
x *= yx = x * yx = 5; x *= 4;20
x /= yx = x / yx = 20; x /= 5;4
x %= yx = x % yx = 20; x %= 6;2

Risultati provati.

Più uno e meno uno (slide 41)

x++;    // come x = x + 1
x--;    // come x = x - 1

Servono ogni volta che si conta: i contatori dei cicli, che arriveranno presto. Le slide li usano solo come istruzioni a sé, su una riga da soli. Dentro un'espressione più grande (y = x++ * 2) hanno un comportamento più sottile, che il corso non tratta: non usarli così.

Prova tu

Con int n = 10; che cosa vale n dopo n -= 3; n *= 2; n++;?
  1. 10 - 3 = 7, poi 7 * 2 = 14, poi 14 + 1 = 15.

Da ricordare

x op= y vuol dire x = x op y. x++ e x-- aggiungono o tolgono 1: usali da soli, su una riga.

§14

I simboli di questa lezione

SimboloSi leggeVuol direEsempio
=«uguale», «prende»assegnamento: copia il valore di destra nella variabile di sinistrax = 10;
+ - * / %più, meno, per, diviso, modulooperazioni sugli interi; / intera, % resto13 / 3 = 4, 13 % 3 = 1
+= -= *= /= %=«più uguale», …aggiorna la variabile con un'operazionex += 3
++, --«più più», «meno meno»aumenta o diminuisce di 1x++
%d«percento di»in printf: un intprintf("%d", x)
%f«percento effe»in printf: un float o doubleprintf("%f", y)
%zu«percento zeta u»un size_t, per esempio un sizeofprintf("%zu", sizeof(int))
%6d, %06dalmeno 6 posizioni, con spazi o con zeri000007
sizeof«size of»byte occupati da un tipo o da una variabilesizeof(int) = 4
0x«zero ics»numero in esadecimale, usato per gli indirizzi0x904A
3.14fcostante float (senza f è double)float y = 3.14f;
PC, FP, SPprogram counter, frame pointer, stack pointeristruzione corrente, inizio delle variabili, prossima casella libera
?«indeterminato»casella riservata ma mai assegnataint x;
, , interi, naturali, realigli insiemi della matematica che i tipi approssimanoint ≈
§15

Verso l'esame

esame

L'esame di Programmazione I è al PC, unico per i tre canali: appelli lunedì 25/01/2027 e giovedì 11/02/2027. Questa lezione entra in due tipi di esercizio.

Esercizi sullo stato della memoria. Nel 2025/26 erano uno dei due esercizi di teoria del canale C (scheda del corso). Si chiede il valore delle variabili dopo una sequenza di istruzioni, che cosa viene stampato, quali caselle sono ancora «?». Il metodo è quello della sezione «Un programma seguito nella memoria»: una colonna per variabile, una riga per istruzione. Più avanti nel corso lo stesso schema si allarga alle chiamate di funzione, con un frame per ogni chiamata.

Esercizi di programmazione. Ogni programma usa variabili, assegnamenti, printf. Gli errori che costano:

  1. specificatore sbagliato in printf (%f per un int): con -Wall -Werror il programma non compila;
  2. variabile usata prima di darle un valore: non compila con -Werror, e se compilasse darebbe valori a caso;
  3. divisione intera dove serviva quella con la virgola (7 / 2 fa 3);
  4. overflow con numeri grandi: il compilatore non avvisa;
  5. stampe diverse da quelle richieste: CodeRunner confronta il testo carattere per carattere, spazi compresi.

Conta all'esame · Nomi e leggibilità

Le soluzioni vengono anche lette (riepilogo d'esame 2025/26 del canale C). Nomi chiari in camelCase, una variabile per ogni ruolo, niente static (vietato) né variabili globali.

§16

Quiz

come all'esame
  1. Quale tra queste NON è una delle quattro cose che definiscono una variabile, secondo la slide 5?
  2. Dopo queste righe, quanto vale r? int a = 17, b = 5; int r = a % b;
  3. Che cosa stampa printf("%d\n", 7 / 2 * 2);?
  4. Dopo int x; (variabile locale, senza altre istruzioni), che cosa contiene x nel modello della memoria?
  5. Nel modello della memoria del corso, dopo int a; int b; int c; in main, di quanti byte è sceso lo stack pointer rispetto al frame pointer (int da 4 byte)?
  6. Che cosa stampa questo programma (compilato senza -Werror)? int x = 8.89; printf("x vale %d\n", x);
  7. Quale istruzione stampa x vale 000042 con int x = 42;?
  8. Con int n = 4; quanto vale n dopo n += 2; n *= 3; n--;?
  9. Quale di questi identificatori NON è valido in C?
  10. Su quale di queste affermazioni sulle dimensioni dei tipi il C dà una garanzia?
§17

Esercizi

con soluzioni

1 · Stato della memoria base

Disegna lo stato della memoria (valori di tutte le variabili) dopo ogni riga, e scrivi che cosa viene stampato.

int main(void) {
    int a = 3;
    int b;
    int c = a * 4;
    b = c - a;
    a = a + b;
    printf("%d %d %d\n", a, b, c);
}
Soluzione
Dopoabc
int a = 3;3
int b;3?
int c = a * 4;3?12
b = c - a;3912
a = a + b;12912

Stampa 12 9 12. Nella riga a = a + b si usa il valore vecchio di a (3): . Verificato con gcc.

2 · Precedenze base

Calcola a mano, poi controlla con un programma: (a) 5 + 12 / 4 * 2; (b) (5 + 12) / 4 * 2; (c) 5 + 12 / (4 * 2); (d) 17 % 5 * 3 - 1.

Soluzione

(a) 12 / 4 = 3, 3 * 2 = 6, 5 + 6 = 11. (b) 17 / 4 = 4 (intera), 4 * 2 = 8. (c) 4 * 2 = 8, 12 / 8 = 1, 5 + 1 = 6. (d) 17 % 5 = 2, 2 * 3 = 6, 6 - 1 = 5. Verificati con gcc 16.1.

3 · Ore e minuti base

Scrivi un programma che, dato int minuti = 135;, stampa 135 minuti = 2 ore e 15 minuti usando / e %.

Soluzione
#include <stdio.h>

int main(void) {
    int minuti = 135;
    int ore = minuti / 60;
    int resto = minuti % 60;
    printf("%d minuti = %d ore e %d minuti\n", minuti, ore, resto);
    return 0;
}

Stampa 135 minuti = 2 ore e 15 minuti (compilato con -Wall -Werror). 135 / 60 = 2, 135 % 60 = 15.

4 · Le cifre di un numero medio

Con int n = 4827; stampa separatamente le unità, le decine, le centinaia e le migliaia, usando solo / e %.

Soluzione
#include <stdio.h>

int main(void) {
    int n = 4827;
    int unita = n % 10;
    int decine = n / 10 % 10;
    int centinaia = n / 100 % 10;
    int migliaia = n / 1000;
    printf("%d %d %d %d\n", migliaia, centinaia, decine, unita);
    return 0;
}

Stampa 4 8 2 7. n / 10 toglie l'ultima cifra (482), % 10 prende quella che ora è l'ultima (2). È lo stesso trucco della conversione in base 2 di Fondamenti, con 10 al posto di 2.

5 · Trova gli errori medio

Il programma seguente con gcc -Wall -Werror non compila. Trova i problemi.

#include <stdio.h>

int main(void) {
    int totale;
    float media = 7.5f;
    int 1voto = 28;
    totale = totale + 28;
    printf("totale %f, media %f\n", totale, media);
    return 0;
}
Soluzione
  1. int 1voto comincia con una cifra: invalid suffix 'voto' on integer constant. Si chiama per esempio voto1.
  2. totale = totale + 28; usa totale mai inizializzato: 'totale' is used uninitialized. Va scritto int totale = 0;.
  3. Nel printf totale è un int stampato con %f: format '%f' expects argument of type 'double', but argument 2 has type 'int'. Va %d.

gcc 16.1 al primo tentativo segnala gli errori 1 e 3. L'errore 2 compare solo dopo aver corretto il nome: finché la riga di 1voto non compila, gcc non arriva a controllare i valori delle variabili. Per questo si ricompila dopo ogni correzione.

Versione corretta:

#include <stdio.h>

int main(void) {
    int totale = 0;
    float media = 7.5f;
    int voto1 = 28;
    totale = totale + voto1;
    printf("totale %d, media %f\n", totale, media);
    return 0;
}

Stampa totale 28, media 7.500000.

6 · Media che non torna medio

Con int a = 7, b = 8; il programma calcola int media = (a + b) / 2; e stampa 7. Perché non 7,5? Come si ottiene 7,5?

Soluzione

(a + b) / 2 è una divisione tra interi: fa 7, il resto si perde. E anche mettendo il risultato in un float (float media = (a + b) / 2;) si ottiene 7.000000, perché la divisione è già stata fatta tra interi. Per avere 7,5 uno dei due operandi deve essere reale: float media = (a + b) / 2.0f; stampa 7.500000. Le conversioni tra tipi le vedrai nel laboratorio 02.

7 · Lo scambio di due variabili difficile

Con int x = 3, y = 8; scrivi le istruzioni che scambiano i valori: alla fine x deve valere 8 e y 3. Perché x = y; y = x; non funziona?

Soluzione

x = y; mette 8 in x e il 3 si perde; poi y = x; copia 8 in y. Alla fine valgono tutti e due 8. Serve una terza variabile che conservi il valore:

int x = 3, y = 8;
int tmp = x;    // tmp = 3
x = y;          // x = 8
y = tmp;        // y = 3
Dopoxytmp
inizio38
int tmp = x;383
x = y;883
y = tmp;833

Lo scambio è l'argomento della prossima lezione nel canale B 2025/26 e nel deck del canale A.

8 · Che cosa stampa tipo esame

Senza compilare, scrivi l'output esatto.

#include <stdio.h>

int main(void) {
    int a = 10, b = 4, c;
    c = a / b;
    a %= b;
    b *= c;
    c = a + b * 2;
    printf("a=%d b=%03d c=%4d\n", a, b, c);
    return 0;
}
Soluzione
  • c = 10 / 4 = 2.
  • a %= b: a = 10 % 4 = 2.
  • b *= c: b = 4 * 2 = 8.
  • c = a + b * 2 = 2 + 16 = 18.

Stampa a=2 b=008 c= 18: %03d su 3 posizioni con zeri, %4d su 4 posizioni con due spazi davanti a 18. Verificato con gcc 16.1.

§18

Domande di ripasso

autoverifica

Rispondi a voce alta o per iscritto, poi apri la risposta.

Che cos'è un tipo e quali cose stabilisce?

Una regola che dà significato ai bit in memoria. Stabilisce nome, dimensione in byte, valori ammessi, operazioni possibili, rappresentazione interna e regole di conversione verso altri tipi.

Quali sono le quattro proprietà di una variabile? Quale sceglie il programmatore?

Nome, tipo, indirizzo e valore. Il programmatore sceglie nome e tipo; l'indirizzo lo decidono compilatore e sistema; il valore cambia durante l'esecuzione.

Perché un int non può contenere qualunque intero?

Perché occupa un numero fisso di byte (di solito 4, cioè 32 bit): può rappresentare solo valori, da a . I tipi del C sono modelli finiti degli insiemi infiniti della matematica.

Che cosa garantisce il C sulle dimensioni dei tipi?

Solo l'ordine: char ≤ short ≤ int ≤ long ≤ long long, e float ≤ double; più sizeof(char) uguale a 1. Le dimensioni vere dipendono dalla macchina (su Windows a 64 bit long è di 4 byte).

Che cosa succede in memoria quando si dichiara int x;?

Viene riservato lo spazio per un int (4 byte) sullo stack: lo stack pointer scende di 4. Il contenuto resta indeterminato («?») finché non si assegna un valore.

Che cosa indicano PC, FP e SP nel modello della memoria?

PC: l'istruzione da eseguire nell'area codice. FP: l'indirizzo da cui partono le variabili di main. SP: dove verrà messa la prossima variabile. All'inizio FP e SP coincidono.

Come si esegue un assegnamento variabile = espressione;?

Si valuta l'espressione a destra, si ottiene un valore e lo si copia nella casella della variabile a sinistra (lvalue), cancellando il valore precedente.

Che cosa fanno / e % tra interi?

/ dà il quoziente intero, scartando la parte decimale (13 / 3 = 4); % dà il resto (13 % 3 = 1).

Qual è l'ordine di valutazione in a + b * c - d / e?

Prima b * c e d / e (precedenza più alta, da sinistra), poi la somma e la sottrazione da sinistra a destra: (a + (b * c)) - (d / e).

Che cosa vogliono dire %d, %f, %zu, %6d e %06d?

Intero; reale; size_t (per esempio il risultato di sizeof); intero su almeno 6 posizioni con spazi a sinistra; intero su almeno 6 posizioni con zeri a sinistra.

Che differenza c'è tra visibilità e durata di una variabile?

La visibilità (scope) dice dove si può usare il nome; la durata (lifetime) dice per quanto tempo la variabile occupa memoria. Una locale è visibile nel suo blocco e vive sullo stack finché il blocco dura; una globale si vede in tutto il file e vive per tutto il programma.

Che cosa fanno x *= 3 e x--?

x *= 3 equivale a x = x * 3; x-- equivale a x = x - 1.

§19

Glossario

termini
Tipo
Regola che dà significato ai bit: dimensione, valori ammessi, operazioni, rappresentazione, conversioni.
Variabile
Nome e tipo associati a una porzione di memoria che contiene un valore.
Indirizzo
Posizione in memoria di una variabile; lo decide il sistema, spesso scritto in esadecimale (0x…).
Dichiarazione
Riga che introduce una variabile con tipo e nome, per esempio int x;.
Inizializzazione
Primo valore dato a una variabile nella dichiarazione: int x = 10;.
Assegnamento
variabile = espressione;: calcola la destra e copia il valore nella variabile.
lvalue / rvalue
Lato sinistro (una casella di memoria) e lato destro (un valore da calcolare) di un assegnamento.
Overflow
Valore che non sta nel tipo: un int oltre il massimo «gira» verso i negativi.
Stack (pila)
Zona dell'area dati dove stanno le variabili locali; nel modello del corso cresce verso il basso.
Heap
Zona della memoria per le variabili dinamiche, create con malloc e liberate a mano.
Program counter (PC)
Registro con l'indirizzo dell'istruzione da eseguire.
Frame pointer (FP)
Indirizzo da cui partono le variabili della funzione in esecuzione (per ora main).
Stack pointer (SP)
Indirizzo in cui verrà messa la prossima variabile.
Stack overflow
Errore a runtime: lo spazio dello stack è finito.
Valore indeterminato («?»)
Contenuto di una variabile locale dichiarata e mai assegnata.
sizeof
Operatore che dà i byte occupati da un tipo o da una variabile; il risultato è un size_t.
size_t
Tipo intero senza segno per dimensioni e conteggi, da stddef.h; si stampa con %zu.
Specificatore di formato
Codice come %d o %f in printf che indica dove e come stampare un valore.
Divisione intera
/ tra interi: tiene solo la parte intera del quoziente.
Modulo
%: il resto della divisione intera.
Precedenza
Regola che dice quale operatore si calcola prima.
camelCase
Convenzione per i nomi composti: sommaTot, elementoTrovato.
Visibilità (scope)
Parte del codice in cui si può usare un nome.
Durata (lifetime)
Tempo in cui una variabile occupa memoria.
Assegnamento composto
x += y e simili: aggiorna una variabile con un'operazione.
§20

Checklist

0 su 14

Spunta quando sai fare la cosa senza guardare gli appunti. Le spunte restano salvate in questo browser.