Scarica Fondamenti di informatica e più Sintesi del corso in PDF di Informatica solo su Docsity!
FONDAMENTI DI INFORMATICA
Guida completa per l'esame orale — Ingegneria
Trattazione tecnica e approfondita di tutti i moduli del programma
MODULO 1 — Elaborazione automatica dell'informazione e
algoritmi
1.1 Algoritmi e programmi
Un algoritmo è una sequenza finita, ordinata e non ambigua di passi (istruzioni) che, a partire da un insieme di dati in ingresso (input), produce un risultato definito (output) in un tempo finito. Il concetto è fondamentale in informatica e precede storicamente i calcolatori elettronici. Le proprietà essenziali di un algoritmo sono:
- Finitezza: l'algoritmo deve terminare dopo un numero finito di passi, per qualunque input valido.
- Definitezza (non ambiguità): ogni passo deve essere descritto in modo preciso e inequivocabile.
- Efficacia: ogni passo deve essere elementare ed eseguibile da un agente di calcolo.
- Input/Output: deve ricevere zero o più dati in ingresso e produrre almeno un risultato. Un programma è la traduzione di un algoritmo in un linguaggio formale comprensibile da un elaboratore. Il programma è quindi la realizzazione concreta dell'algoritmo in un determinato linguaggio di programmazione. La differenza fondamentale è che l'algoritmo è una descrizione astratta (indipendente dalla macchina), mentre il programma è legato all'ambiente di esecuzione. Concetto chiave Algoritmo → astrazione della soluzione. Programma → implementazione concreta eseguibile da un calcolatore. Tra i due si colloca il linguaggio di programmazione come mezzo espressivo.
1.2 Una notazione grafica per esprimere algoritmi: i diagrammi di flusso
I diagrammi di flusso (flowchart) sono una rappresentazione grafica standardizzata degli algoritmi. Ogni tipo di operazione è associato a un simbolo geometrico convenzionale:
- Ellisse (Terminatore): indica l'inizio (START) e la fine (END) dell'algoritmo.
- Parallelogramma (I/O): rappresenta operazioni di lettura (input) o scrittura (output) di dati.
- Rettangolo (Processo): descrive un'operazione di calcolo o assegnazione (es. x ← a + b).
- Rombo (Decisione): rappresenta un predicato (condizione booleana) con due rami di uscita: SI/NO o VERO/FALSO.
- Frecce (Frecce di flusso): collegano i simboli e indicano il verso di esecuzione. Le strutture di controllo fondamentali rappresentabili con i flowchart sono:
- Sequenza: esecuzione lineare di blocchi uno dopo l'altro.
- Selezione (if-then-else): biforcazione condizionale tramite rombo.
- Iterazione (ciclo): ritorno a un blocco precedente tramite freccia di retroazione.
I diagrammi di flusso, sebbene intuitivi, presentano limiti per algoritmi complessi (poco scalabili). Sono stati in parte sostituiti da pseudocodice e notazioni strutturate come i diagrammi N-S (Nassi-Shneiderman).
1.3 Linguaggi di programmazione
Un linguaggio di programmazione è un sistema formale composto da:
- Sintassi: insieme di regole grammaticali che definiscono la struttura delle istruzioni valide (cosa è scritto correttamente).
- Semantica: il significato associato alle costruzioni sintatticamente corrette (cosa fa l'istruzione).
- Pragmatica: convenzioni d'uso per scrivere programmi leggibili e mantenibili. I linguaggi si classificano per livello di astrazione:
- Linguaggio macchina (livello 0): istruzioni in codice binario direttamente eseguibili dalla CPU. Dipendente dall'architettura hardware.
- Linguaggio assembly (livello 1): rappresentazione simbolica del linguaggio macchina (mnemonici). Es: MOV AX, 5. Richiede assemblatore.
- Linguaggi ad alto livello (livello 2+): astrazione dalla macchina fisica. Esempi: C, C++, Java, Python, Pascal. Richiedono compilatore o interprete.
1.4 Il progetto di programmi
La progettazione di un programma richiede un approccio metodico. Le fasi principali del ciclo di sviluppo software sono:
- Analisi del problema: comprensione dei requisiti, definizione degli input/output attesi, identificazione dei vincoli.
- Progettazione dell'algoritmo: scelta della strategia risolutiva, rappresentazione in pseudocodice o flowchart.
- Codifica: traduzione dell'algoritmo nel linguaggio di programmazione scelto.
- Test e verifica: esecuzione con casi di test rappresentativi per individuare errori logici e di runtime.
- Manutenzione: correzione di bug e miglioramenti successivi al rilascio. Un principio fondamentale è la programmazione strutturata (Dijkstra, 1968): qualunque algoritmo è esprimibile usando sole tre strutture di controllo — sequenza, selezione e iterazione — senza uso di GOTO. Questo rende i programmi più leggibili, verificabili e manutenibili. La tecnica del raffinamento stepwise (per raffinamenti successivi) consiste nel partire da una descrizione ad alto livello del problema e scomporla progressivamente in sottoproblemi via via più semplici, fino a raggiungere istruzioni direttamente implementabili (divide et impera). MODULO 2 — Rappresentazione dell'informazione
2.1 Sistemi numerici
Un sistema di numerazione posizionale è caratterizzato da una base B (intero > 1) e da B cifre distinte (simboli). Il valore di un numero è determinato dal valore delle singole cifre moltiplicato per la potenza della base corrispondente alla posizione. Dato un numero d ₙ ₙ₋₁d ...d ₁ ₀d in base B, il suo valore decimale è: V = d ₙ·Bⁿ + d ₙ₋₁·Bⁿ ⁻¹ + ... + d ₁·B¹ + d ₀·B⁰
- 0+1 = 1 (carry = 0)
- 1+1 = 0 (carry = 1) — si genera un riporto verso la posizione superiore
- 1+1+1 = 1 (carry = 1) — somma con riporto Sottrazione binaria Analogamente all'addizione, con le regole del prestito (borrow). In pratica, nei calcolatori si usa il complemento a due per eseguire la sottrazione come addizione. Moltiplicazione binaria Analoga alla moltiplicazione decimale ma semplificata: ogni cifra moltiplicante è 0 o 1, quindi si tratta di spostare e sommare copie del moltiplicando (shift e add). Divisione binaria Analoga alla divisione lunga decimale. Si usa l'algoritmo di sottrazione e spostamento (shift and subtract).
2.5 Overflow e Underflow
Con un numero finito di bit non è possibile rappresentare tutti i numeri interi. Quando il risultato di un'operazione supera il range rappresentabile si verifica:
- Overflow: il risultato è troppo grande per essere contenuto nel numero di bit disponibile. In aritmetica senza segno, un overflow si verifica quando il carry dal bit più significativo viene perduto. Es. con 4 bit: 15 + 1 = 0 (anziché 16).
- Underflow: in aritmetica con segno, il risultato è un numero negativo con valore assoluto troppo grande. Es: per numeri in virgola mobile, un underflow si verifica quando il risultato è più piccolo del minimo numero positivo rappresentabile, cioè si azzera (flush to zero). Come si rileva l'overflow in complemento a due? Il carry-in nel bit del segno è diverso dal carry- out: se i due carry differiscono, si è verificato overflow. Equivalentemente: la somma di due positivi dà un negativo, o la somma di due negativi dà un positivo. Regola pratica per l'overflow (complemento a 2) Overflow si verifica se e solo se i due operandi hanno lo stesso segno e il risultato ha segno opposto.
2.6 Rappresentazione dei numeri interi (senza segno)
Con n bit si rappresentano gli interi nell'intervallo [0, 2ⁿ - 1]. La codifica è semplicemente quella binaria posizionale. Esempio con 8 bit: range [0, 255].
2.7 Rappresentazione dei numeri con segno
Modulo e segno (Sign-Magnitude) Il bit più significativo (MSB) rappresenta il segno (0 = positivo, 1 = negativo), i restanti n-1 bit rappresentano il valore assoluto. Problemi: doppia rappresentazione dello zero (+0 e -0), circuiteria di addizione complessa. Complemento a uno (One's Complement) Il negativo di un numero si ottiene invertendo tutti i bit. Anche qui esiste la doppia rappresentazione dello zero. Range per n bit: [-(2ⁿ ¹-1), +(2ⁿ ¹-1)].⁻ ⁻
Complemento a due (Two's Complement) Il metodo standard adottato da tutti i moderni processori. Il negativo di N si ottiene invertendo tutti i bit e sommando 1. Non c'è doppia rappresentazione dello zero. La somma funziona correttamente con la stessa circuiteria usata per i numeri non negativi. Range per n bit: [ -2ⁿ ⁻¹ , 2ⁿ ⁻¹ - 1 ]. Per 8 bit: [-128, +127]. Esempio con 8 bit: +5 = 00000101 → -5 = 11111010 + 1 = 11111011 Proprietà fondamentale del complemento a 2 La somma di N e -N dà sempre 0 (con overflow del carry fuori dall'n-esimo bit, che viene ignorato). Questo permette di usare lo stesso sommatore hardware per operazioni con e senza segno.
2.8 Rappresentazione in virgola fissa e virgola mobile
Virgola fissa (Fixed Point) La posizione della virgola è fissata a priori. I bit a sinistra rappresentano la parte intera, quelli a destra la parte frazionaria. Semplice da implementare, ma il range e la precisione sono fissi. Usato in applicazioni embedded e DSP. Virgola mobile (Floating Point — IEEE 754) Un numero in virgola mobile è rappresentato nella forma: (-1)^S × M × 2^E dove S è il bit di segno, M è la mantissa (parte frazionaria normalizzata) ed E è l'esponente. Lo standard IEEE 754 definisce:
- Singola precisione (float, 32 bit): 1 bit segno + 8 bit esponente (bias 127) + 23 bit mantissa implicita.
- Doppia precisione (double, 64 bit): 1 bit segno + 11 bit esponente (bias 1023) + 52 bit mantissa. La normalizzazione impone che la mantissa abbia la forma 1.xxx...x (il leading 1 è implicito e non memorizzato, guadagnando 1 bit extra di precisione). L'esponente è memorizzato in forma biased (con bias): il valore reale dell'esponente è E_stored - bias. Valori speciali IEEE 754: +∞ e -∞ (esponente tutto 1, mantissa 0), NaN (Not a Number, esponente tutto 1, mantissa ≠ 0), denormalized numbers (esponente tutto 0, per rappresentare valori molto piccoli vicino a zero).
2.9 Codici e Rappresentazione dei Caratteri
- ASCII (7 bit, 128 caratteri): standard storico per caratteri latini, cifre, punteggiatura e caratteri di controllo. ASCII esteso (8 bit) aggiunge 128 caratteri per simboli nazionali.
- ISO 8859 (8 bit): famiglia di standard per diverse lingue europee. ISO 8859-1 (Latin-1) usato per Europa occidentale.
- Unicode: standard universale che assegna un code point (numero intero) a ogni carattere di tutte le scritture del mondo. Oltre 143.000 caratteri.
- UTF-8: codifica a lunghezza variabile di Unicode (1-4 byte per carattere). Compatibile con ASCII per i primi 128 caratteri. La più usata sul web.
- UTF-16: codifica a 2 o 4 byte. Usata internamente da Java, JavaScript, Windows.
MODULO 3 — Struttura di un elaboratore
3.1 Struttura di un elaboratore: visione d'insieme
L'architettura di von Neumann (1945) è il modello concettuale alla base di quasi tutti i moderni elaboratori. I componenti fondamentali sono: Unità Centrale di Elaborazione (CPU), Memoria Centrale e Bus di sistema. I dati e le istruzioni risiedono nella stessa memoria (stored-program concept).
3.2 Memoria Centrale (RAM)
La memoria centrale (RAM — Random Access Memory) è un dispositivo di memorizzazione volatile ad accesso casuale. Le sue caratteristiche principali sono:
- È organizzata in celle (locazioni) di dimensione fissa, tipicamente 1 byte.
- Ogni cella ha un indirizzo univoco (un numero intero progressivo partendo da 0).
- L'accesso a qualsiasi cella richiede sempre lo stesso tempo (accesso casuale, non sequenziale).
- È volatile: il contenuto viene perso allo spegnimento dell'alimentazione. La memoria è organizzata gerarchicamente in base a velocità e costo:
- Registri: memoria velocissima all'interno della CPU, pochi decine di elementi, accesso in 1 ciclo clock.
- Cache L1, L2, L3: memoria SRAM veloce integrata nel chip della CPU. Contiene copie delle locazioni di RAM più usate (principio di località).
- RAM (DRAM): memoria principale, capienza elevata (GB), più lenta della cache.
- Memoria di massa: HDD, SSD — non volatile, capacità enorme (TB), accesso molto più lento.
3.3 Unità Centrale di Elaborazione (CPU)
La CPU è il componente che esegue le istruzioni. È composta principalmente da:
- Unità Aritmetico-Logica (ALU): esegue operazioni aritmetiche (somma, sottrazione, ...) e logiche (AND, OR, NOT, XOR, ...) su operandi binari.
- Unità di Controllo (CU): interpreta le istruzioni del programma e genera i segnali di controllo per coordinare tutte le altre unità della CPU e del sistema.
- Registri: piccole memorie interne alla CPU ad altissima velocità. I più importanti sono: Program Counter (PC) — indirizzo dell'istruzione corrente; Instruction Register (IR) — istruzione in fase di esecuzione; Stack Pointer (SP) — cima dello stack; Flag Register — bit di stato (zero, carry, overflow, ...); Registri generali — operandi e risultati intermedi.
- Bus interni (Data Bus, Address Bus, Control Bus): connettono CPU, memoria e periferiche.
3.4 Funzionamento elementare: il ciclo Fetch-Decode-Execute
L'elaboratore funziona eseguendo ciclicamente la sequenza:
- FETCH (Prelievo): il contenuto della cella di memoria all'indirizzo contenuto nel PC viene copiato nell'IR. Il PC viene incrementato al valore dell'istruzione successiva.
- DECODE (Decodifica): l'Unità di Controllo interpreta il codice operativo (opcode) dell'istruzione nell'IR e determina l'operazione da eseguire e gli operandi coinvolti.
- EXECUTE (Esecuzione): l'operazione specificata viene eseguita (calcolo ALU, accesso in memoria, salto condizionato, operazione I/O, ecc.). Questo ciclo si ripete indefinitamente (finché la macchina è accesa o un'istruzione HALT non lo interrompe). La frequenza con cui il ciclo viene eseguito è scandita dal clock di sistema (misurato in Hz, GHz nei processori moderni). Stored Program Concept (von Neumann) La caratteristica rivoluzionaria dell'architettura di von Neumann è che programma e dati risiedono nella stessa memoria. Le istruzioni sono trattate come dati e possono quindi essere lette e scritte dinamicamente. Questo permette di eseguire programmi diversi sullo stesso hardware semplicemente cambiando il contenuto della memoria. MODULO 4 — Cenni sui sistemi di elaborazione e software di base
4.1 Software di base e Sistema Operativo
Il software di base è l'insieme dei programmi che gestiscono le risorse hardware e forniscono un ambiente di lavoro per il software applicativo. Il componente principale è il Sistema Operativo (SO). Le funzioni fondamentali del SO sono:
- Gestione dei processi: creazione, scheduling, sincronizzazione, terminazione dei processi.
- Gestione della memoria: allocazione e deallocazione della RAM, paginazione, memoria virtuale.
- Gestione del file system: organizzazione gerarchica di file e directory, controllo degli accessi.
- Gestione dei dispositivi I/O: astrazione dell'hardware tramite driver, gestione delle interruzioni.
- Interfaccia utente: shell (interfaccia a riga di comando) o GUI (interfaccia grafica).
4.2 Traduzione ed esecuzione dei programmi
I programmi scritti in linguaggi ad alto livello devono essere tradotti in linguaggio macchina per poter essere eseguiti. Esistono due approcci fondamentali:
- Compilazione: un programma (il compilatore) traduce l'intero codice sorgente in codice oggetto (o direttamente in codice macchina) in una fase distinta precedente l'esecuzione. Il risultato è un file eseguibile che può essere eseguito direttamente. Vantaggi: efficienza a runtime elevata (il codice è già tradotto). Esempi: C, C++, Fortran.
- Interpretazione: un programma (l'interprete) legge ed esegue le istruzioni sorgente una alla volta, senza produrre un file eseguibile separato. Più flessibile, ma generalmente più lento. Esempi: Python (classicamente), shell script, BASIC.
- Approccio ibrido: il codice sorgente viene compilato in un formato intermedio (bytecode), che viene poi eseguito da una macchina virtuale (VM) o JIT-compilato. Esempi: Java (bytecode → JVM), Python moderno (bytecode → CPython VM), .NET (CIL → CLR).
4.6 Ambiente di programmazione
Un ambiente di programmazione (o Integrated Development Environment, IDE) integra in un unico strumento tutti gli strumenti necessari per sviluppare software:
- Editor di testo con syntax highlighting, completamento automatico, refactoring.
- Compilatore/interprete integrato per la compilazione e l'esecuzione.
- Debugger per l'esecuzione passo-passo, ispezione delle variabili, breakpoint.
- Strumenti di build (Makefile, cmake, gradle) per automatizzare la compilazione di progetti complessi.
- Gestore di versioni integrato (Git, SVN). Esempi: Visual Studio Code, Eclipse, IntelliJ IDEA, CLion, Xcode.
4.7 Preprocessore
In linguaggi come C e C++, il preprocessore è un primo stadio di elaborazione che viene eseguito prima del compilatore vero e proprio. Le direttive al preprocessore iniziano con il simbolo #. Le principali sono:
- #include: include il contenuto di un altro file nel punto corrente. Usato per includere gli header delle librerie. Es: **#include ** include lo standard I/O.
- #define: definisce una macro (sostituzione testuale). Es: #define PI 3.14159. Le macro con parametri permettono una rudimentale parametrizzazione.
- #ifdef / #ifndef / #endif: compilazione condizionale. Permettono di includere o escludere blocchi di codice in base a condizioni (es. per supportare diversi sistemi operativi, o per evitare inclusioni multiple di header).
- #pragma: direttive dipendenti dal compilatore (es. #pragma once per evitare inclusioni multiple).
4.8 Commenti
I commenti sono annotazioni nel codice sorgente che vengono completamente ignorati dal compilatore/interprete. Servono a documentare il codice, spiegare la logica, rendere il programma leggibile e manutenibile. In C/C++: // commento su singola riga e / commento su più righe / In Python: # commento Una buona pratica di programmazione richiede commenti chiari, aggiornati e significativi (spiegano il perché, non il cosa — il cosa si vede dal codice).
4.9 Librerie e File di Intestazione (Header)
Una libreria è una raccolta di funzioni (e dati) precompilate riutilizzabili. Invece di riscrivere funzioni comuni (I/O, matematica, gestione stringhe), si usa il codice già verificato e ottimizzato delle librerie. In C/C++, il meccanismo funziona così:
- File di intestazione (.h): contiene le dichiarazioni (prototipi) delle funzioni e la definizione dei tipi. Viene incluso nel codice sorgente con #include per informare il compilatore dell'esistenza delle funzioni.
- File di libreria (.a, .lib per statiche; .so, .dll per dinamiche): contiene il codice macchina precompilato delle implementazioni. Viene collegato dall'eseguibile dal linker. Librerie standard del C:
- : I/O standard (printf, scanf, fopen, fclose, ...
- : funzioni di utilità generale (malloc, free, exit, atoi, ...)
- : manipolazione di stringhe (strlen, strcpy, strcmp, ...)
- : funzioni matematiche (sin, cos, sqrt, pow, ...) Documento generato per preparazione esame — Fondamenti di Informatica — Ingegneria