Linguaggio C

Il C è un linguaggio di programmazione di alto livello. Progettato e realizzato da Dennis Ritchie (1941–2011) nel 1972, fu sviluppato presso gli AT&T Bell Laboratories (New Jersey, USA) per ottenere un linguaggio adatto all’implementazione dei sistemi operativi. Utilizzato per la prima volta in modo estensivo per la riscrittura del sistema operativo UNIX dal linguaggio assembly, può essere impiegato con tutti i principali sistemi operativi oggi disponibili.

Stephen C. Johnson, alla fine degli anni Settanta, scrisse il Portable C Compiler (pcc) trasportabile su sistemi diversi dal PDP-11 (Programmed Data Processor-11, la serie di computer su cui furono sviluppate le prime versioni di UNIX): da allora il C cominciò a essere utilizzato come linguaggio in altri sistemi operativi, come MS-DOS (Microsoft Disk Operating System) e CP/M-80 (Control Program for Microcomputers).

Per combinare efficienza e potenza in un linguaggio tendenzialmente compatto, il C non possiede al suo interno alcune funzionalità tipiche di altri linguaggi (per esempio la gestione diretta di monitor e tastiera), demandate a librerie separate da utilizzare all’occorrenza. Questa soluzione consente maggiore flessibilità: il programmatore può decidere se impiegare funzioni esistenti oppure implementarne di proprie, sfruttando gli elementi di base offerti dal linguaggio.

La grande facilità d’uso e la diffusione del C resero necessario evitare la proliferazione di “dialetti”: nel 1983 fu istituito un comitato ANSI per definire uno standard del linguaggio C, da cui nacque lo standard ANSI/ISO C (C89/C90), base delle evoluzioni successive.

In sintesi

  • Nato nel 1972 ai Bell Labs per sistemi operativi.
  • Efficiente, portabile, con funzioni di I/O delegate a librerie.
  • Standardizzato (C89/C90) per evitare dialetti.
  • Ancora oggi alla base di molto software di sistema.
Esercizio – C: perché demandare I/O a librerie?
Per separare le responsabilità: il linguaggio resta minimale e portabile; i dettagli di I/O, dipendenti dalla piattaforma, vivono in librerie intercambiabili.

Linguaggio C++

Sebbene il C possieda tutti gli strumenti per implementare qualunque tipo di dato astratto, tale implementazione è demandata interamente al programmatore. Il C++ nacque dall’esigenza di una maggiore astrazione dei dati, che consentisse di definire nuovi tipi in modo semplice, risolvendo numerosi problemi con la stessa efficienza e portabilità del C.

L’ideatore è il danese Bjarne Stroustrup che, insieme ai fondatori di C e UNIX, nei laboratori AT&T iniziò nel 1979 a gettare le basi di un nuovo linguaggio che mantenesse il C come base per garantirne efficienza, portabilità e compatibilità con l’elevato numero di programmi esistenti.

La prima versione prese il nome di “C con classi”; nel 1986 divenne C++. Nel 1990 uscì The Annotated C++ Reference Manual (ARM), base dello standard ISO 1998 (C++98), poi evoluto (C++11/14/17/20/23).

Il C++ mantiene le virtù del C ma aggiunge OOP e astrazione di alto livello, consentendo migrazioni progressive dalla programmazione procedurale.

Perché il C++ ha avuto successo

  • Compatibile con il C; ampia base di codice e librerie.
  • Standard ISO che migliorano portabilità e interoperabilità.
  • Efficienza elevata; adatto a software di sistema e applicazioni desktop.
  • Sintassi che ha influenzato C#, Java e molti linguaggi di scripting.
In sintesi

  • Estende il C con classi, OOP e astrazione.
  • Compatibilità forte con codice C esistente.
  • Standard evolutivi (C++98 → C++23).
  • Ottimo compromesso tra performance e produttività.
Esercizio – C++: perché l’OOP aiuta nel software grande?
Permette incapsulamento, riuso e gerarchie di tipi, riducendo complessità accidentale e facilitando manutenzione e test.

Livelli di funzionalità di un computer

Il livello software più basso corrisponde al linguaggio macchina/assembly. Sopra troviamo il sistema operativo (I/O, RAM, CPU, memoria di massa) e quindi il software di sviluppo (editor, compilatori, interpreti, linker) che traducono linguaggi ad alto livello in codice eseguibile.

Compilatori e interpreti

  • Interprete: traduce ed esegue una riga per volta (ottimo per iterazioni rapide).
  • Compilatore: traduce l’intero programma in un binario (esecuzione veloce). C/C++ sono compilati.
In sintesi

  • Macchina → OS → Tool di sviluppo → Applicazioni.
  • Compilatore produce binario; interprete esegue “al volo”.
  • C/C++ sono compilati e ad alte prestazioni.
Esercizio – Vantaggio pratico del compilato rispetto all’interprete?
Prestazioni e distribuzione: il binario gira senza dipendenze dall’interprete e con velocità nativa.

Struttura di un programma in C/C++

Un programma deve contenere main(), punto d’ingresso dell’esecuzione.

Esempio completo (C)

#include <stdio.h>
#include <stdlib.h>

int main() {
    int a, b, somma;

    printf("Inserire il primo numero: ");
    scanf("%d", &a);

    printf("Inserire il secondo numero: ");
    scanf("%d", &b);

    somma = a + b;

    printf("La somma di %d e %d e`: %d\n", a, b, somma);

    return 0;
}

Direttive, main, dichiarazioni, corpo

  • Direttive (#include) — non terminano con ;
  • main() — funzione principale, di norma int main() con return 0;
  • Sezione dichiarativa — dichiarazioni variabili (buona pratica: all’inizio della funzione)
  • Corpo — istruzioni tra { }, ogni istruzione termina con ;
Indentazione: rientri coerenti migliorano leggibilità e manutenzione.
Struttura di un programma in C/C++
Diagramma a blocchi: Direttive → main → Dichiarazioni → Corpo → return 0.
Flusso tipico: Direttive → main() → Dichiarazioni → Istruzioni → return 0;
In sintesi

  • #include per librerie; nessun ; nelle direttive.
  • int main() è il punto d’ingresso; return 0; indica successo.
  • Dichiarazioni chiare all’inizio; corpo tra graffe con ; a fine istruzione.
Esercizio – Sposta una dichiarazione vicino all’uso: pro e contro?
Pro: riduce lo scope e migliora leggibilità locale. Contro: alcune linee guida preferiscono raggruppare in alto per vista d’insieme.

I commenti

Commenti in C

somma = a + b; /* commento
                   su più righe */

Commenti in C++

// commento su una riga
std::cout << "C++"; // commento in coda
C C++
Inizio commento /* //
Fine commento */ Fine riga
Note Più righe Una riga (o usa /*...*/)
In sintesi

  • // per righe singole; /*...*/ per blocchi.
  • Commenti chiari spiegano il “perché”, non l’ovvio “come”.
  • Evita commenti obsoleti: aggiornali o rimuovili.
Esercizio – Trasforma due righe in un commento di blocco
/* Riga 1
   Riga 2 */

Il primo programma in C/C++

C C++
#include <stdio.h>

int main() {
    /* Hello World in C */
    printf("Hello world! in C");
    return 0;
}
#include <iostream>
using namespace std;

int main() {
    // Hello World in C++
    cout << "Hello world! in C++";
    return 0;
}
Nota: return 0; alla fine di main() segnala terminazione corretta al sistema.
Esercizio – Aggiungi un a-capo all’output
// C
printf("Hello world! in C\n");

// C++
std::cout << "Hello world! in C++\n";
// oppure
std::cout << "Hello world! in C++" << std::endl;

Le direttive #include e namespace

#include inserisce nel sorgente i file header con dichiarazioni e funzioni delle librerie standard.

C C++
#include <stdio.h>
#include "miofile.h"
#include <iostream>
#include "miofile"
Header con estensione .h Header senza .h per standard C++

In C++ si usa lo spazio dei nomi std:

using namespace std; // per esempio didattico
// oppure qualificare:
std::cout << "ciao\n";
In sintesi

  • <...> cerca nei percorsi di sistema; "..." parte dalla cartella del progetto.
  • In C++ preferisci <iostream>, <cstdio>, <cmath>, ecc.
  • Valuta l’uso di std:: invece di using namespace std; in codice “di produzione”.
Esercizio – Trova l’errore: #include <iostream.h>
In C++ moderno non si usa l’estensione .h per gli header standard. Corretto: #include <iostream>.

La gestione dell’input/output

Operatori di ridirezione (C++)

  • << inserzione → output verso cout
  • >> estrazione → input da cin
std::cout << "Ciao a tutti";
std::cout << "x=" << x << " y=" << y << std::endl;
int numero; std::cin >> numero;

Confronto C vs C++

C C++
Output printf(...) cout << ...
Input scanf(...) cin >> ...
A-capo \n \n o endl
Esempio printf("x=%d y=%d\n",x,y); cout << "x=" << x << " y=" << y << endl;
Esercizio – Somma di due interi (C++)
#include <iostream>
int main(){
    int a,b; std::cout << "a? "; std::cin >> a;
    std::cout << "b? "; std::cin >> b;
    std::cout << "somma=" << (a+b) << '\n';
    return 0;
}
In sintesi

  • C usa printf/scanf con specificatori di formato.
  • C++ usa cout/cin senza specificatori.
  • endl forza anche il flush del buffer di output.

Gli operatori in C/C++

Aritmetici

Simbolo Operatore Note
+ Addizione Con stringhe C++ concatena
- Sottrazione
* Moltiplicazione
/ Divisione Tra interi tronca
% Modulo Solo interi

Esempio (divisione intera)

C C++
#include <stdio.h>
int main() {
    int q = 100/22, r = 100%22;
    printf("100/22 = %d con resto %d", q, r);
    return 0;
}
#include <iostream>
int main() {
    int q = 100/22, r = 100%22;
    std::cout << "100/22 = " << q << " con resto " << r << '\n';
    return 0;
}
Glossario: Eccezione = evento che interrompe il flusso (es. divisione per zero).

Unari incremento/decremento

int x=10, y;
y = x++; // postfisso: usa x poi incrementa → y=10, x=11
y = ++x; // prefisso: incrementa poi usa → x=12, y=12
Esercizio – Valuta l’output
int i=15; int a=(i++)*2; // a=30, i=16
i=15; a=(++i)*2;         // a=32, i=16
In sintesi

  • / tra interi tronca; % solo su interi.
  • ++x vs x++: prefisso modifica prima, postfisso dopo.
  • Usa parentesi per controllare la precedenza.

Operatori di confronto, logici e assegnamento

Relazionali

Forma Descrizione
a == b uguale
a != b diverso
a < b minore
a > b maggiore
a <= b minore o uguale
a >= b maggiore o uguale

Logici

Operatore Nome Short-circuit
&& AND Se sinistro è false, destro non valutato
|| OR Se sinistro è true, destro non valutato
! NOT Inverte verità

Assegnamento semplice e composto

Esteso Compatto Significato
x = x + y; x += y; Somma e assegna
x = x - y; x -= y; Sottrai e assegna
x = x * y; x *= y; Moltiplica e assegna
x = x / y; x /= y; Dividi e assegna
x = x % y; x %= y; Modulo e assegna
Esercizio – Espressione vera solo se a e b sono positivi
(a > 0) && (b > 0)
In sintesi

  • Relazionali producono booleani (true/false).
  • &&/|| valutano in short-circuit.
  • Assegnamenti composti rendono il codice più conciso.

Appendice: Standard C e C++ (cronologia)

Standard Anno Novità principali (non esaustive)
C89/C90 1989/1990 Primo standard ANSI/ISO
C99 1999 Commenti //, inline, nuovi tipi interi, dichiarazioni flessibili
C11 2011 _Atomic, _Thread_local, Unicode
C++98 1998 Primo standard ISO, OOP, STL
C++11 2011 auto, nullptr, lambda, range-for, move semantics
C++17 2017 if constexpr, filesystem, structured bindings
C++20 2020 Concepts, Modules, Coroutines, Ranges
C++23 2023 Migliorie libreria, std::expected, std::print (implementazioni)
Nota: la disponibilità dipende dal compilatore e dallo standard selezionato (es. -std=c++20 per g++).

Scheda riepilogativa: Operatori in C/C++

Operatori aritmetici

Simbolo Nome Esempio Effetto
+ Addizione a + b Somma
- Sottrazione a - b Differenza
* Moltiplicazione a * b Prodotto
/ Divisione a / b Interi: troncamento
% Modulo a % b Resto divisione intera

Incremento / Decremento

Forma Tipo Esempio Risultato
++x Prefisso y = ++x; Incrementa poi usa
x++ Postfisso y = x++; Usa poi incrementa
--x Prefisso y = --x; Decrementa poi usa
x-- Postfisso y = x--; Usa poi decrementa

Relazionali

Simbolo Significato
== Uguale
!= Diverso
< Minore
> Maggiore
<= Minore o uguale
>= Maggiore o uguale

Logici

Simbolo Nome Short-circuit
&& AND
|| OR
! NOT

Assegnamento (composti)

Esteso Compatto Significato
x = x + y; x += y; Somma e assegna
x = x - y; x -= y; Sottrai e assegna
x = x * y; x *= y; Moltiplica e assegna
x = x / y; x /= y; Dividi e assegna
x = x % y; x %= y; Modulo e assegna
In sintesi

  • Ricorda short-circuit per && e ||.
  • / tra interi tronca; % solo per interi.
  • Parentesi per chiarire la precedenza nelle espressioni complesse.

Ripassiamo insieme

Come si dichiara una variabile?

tipo Identificatore;
tipo Identificatore1, Identificatore2, ...;
tipo Identificatore = valore;

I linguaggi C e C++: le basi (mappa modificabile)

Un programma è un algoritmo tradotto in un linguaggio di programmazione, comprensibile al computer.

Costituenti C/C++: parole chiave, identificatori, commenti, segni di punteggiatura, operatori.

Commenti — C: /* ... */ • C++: // ... (fino a fine riga)

I/O in Cprintf(...), scanf(...)

I/O in C++cout << ..., cin >> ...

Istruzioni di input/output: esempi rapidi

Obiettivo C C++
Output di un messaggio printf("Messaggio "); cout << "Messaggio ";
Output di x printf("%d", x); cout << x;
Output “Valore = x” printf("Valore = %d", x); cout << "Valore = " << x;
Input in x scanf("%d", &x); cin >> x;

Test – Vero/Falso

  1. Algoritmo e programma sono sinonimi (I) …… ☐ V ☐ F
  2. Il linguaggio C non possiede parole chiave (A) …… ☐ V ☐ F
  3. int z; dichiara una variabile intera di nome z (H) …… ☐ V ☐ F
  4. Il simbolo // indica l’inizio di un commento in C++ (B) …… ☐ V ☐ F
  5. cin >> z; visualizza il valore di z (G) …… ☐ V ☐ F
  6. cout >> "Benvenuto"; produce un errore (F) …… ☐ V ☐ F
Mostra soluzioni
  1. F — algoritmo ≠ programma
  2. F — il C ha keyword
  3. V
  4. V
  5. Fcin legge, non stampa
  6. V — si usa <<, non >>

Test – Risposta multipla

  1. Sequenza per ottenere un eseguibile:
    A) editing → linker → compilatore
    B) editing → compilatore → debug
    C) compilatore → editing → linker
    D) editing → compilatore → linker
  2. Istruzioni di preprocessore: quale è FALSA?
    A) si chiamano direttive al preprocessore
    B) iniziano con # + nome direttiva
    C) non terminano con ;
    D) terminano con ;
  3. Che cosa sono i file header?
    A) file opzionali
    B) file che contengono le librerie del C
    C) file da integrare in caso di errore
    D) file per intestazione testuale
  4. Differenza tra #include <stdio.h> e #include "stdio.h":
    A) nessuna
    B) librerie diverse
    C) percorsi di ricerca diversi
    D) la seconda viene ignorata se assente
  5. const definisce costanti tipizzate. Significa:
    A) ogni costante simbolica ha un tipo
    B) ogni costante esplicita ha un tipo
    C) le costanti possono avere tipi diversi
    D) possono cambiare tipo
  6. Quale è FALSA sugli statement:
    A) sono istruzioni
    B) sono righe di dichiarazioni
    C) racchiudono l’elaborazione
    D) sono errori del compilatore
  7. Quale è FALSA sugli specificatori di formato:
    A) introdotti da %
    B) identificano il tipo da stampare
    C) indicano percentuale
    D) in printf formattano il valore
  8. Dopo a = x++:
    A) a e x uguali
    B) a > x
    C) a < x
    D) dipende da x
  9. Espressione corretta per la media:
    A) a+b+c+d/4
    B) a+(b+c+d)/4
    C) (a+b+c+d/4
    D) (a+b+c+d)/4
  10. Calcola:
    int Totale, x = 1;
    Totale = ++x + 15 * x-- + x++;
    
    A) 31
    B) 32
    C) 30
    D) 16
  11. x %= 2 assegna a x:
    A) il 2% di x
    B) il resto di 2/x
    C) 0 oppure 1
    D) un numero > 1

Soluzioni

  1. D
  2. D
  3. B
  4. C
  5. A
  6. D
  7. C
  8. C
  9. D
  10. (in C/C++ classico l’espressione ha comportamento indefinito)
  11. C

Abilità

1) Trova gli errori presenti nel seguente codice.

#include <iostream.h>
int main( )
{
float a, b; /* questo è un commento corretto.
a = 100; // questo commento è valido //
b = 10 */ questo commento è valido /*
return 0;
}
Mostra soluzione
#include <iostream>

int main() {
    float a, b; /* commento di blocco chiuso correttamente */
    a = 100;
    // commento di riga valido
    b = 10;
    return 0;
}

Note: header standard C++ senza .h; i commenti non si incrociano.

2) In ognuna delle seguenti dichiarazioni di costanti c’è almeno un errore: trovali.

const char C "a";
const int POSTI = 100,00;
const char[2] MESS = "Buongiorno!";
const PGRECO = 3,14;
char const C = 'a';
int POSTI =100;
const char ERRORE "Rilevato errore'
Mostra soluzione
const char C = 'a';                 // carattere singolo
const int POSTI = 100;              // punto, non virgola
const char MESS[] = "Buongiorno!";  // dimensione dedotta
const double PGRECO = 3.14;         // tipo esplicito
const int POSTI2 = 100;             // nuovo nome se serve
const char ERRORE[] = "Rilevato errore"; // apici corretti

3) Qual è l’output prodotto dal seguente programma?

#include <stdio.h>
int main()
{
    int a=5, b=6, c;
    printf("++a = %d\n", ++a);
    printf("a = %d\n", a);
    printf("a++ = %d\n", a++);
    printf("a = %d\n", a);
    c = ++b;
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    c = b++;
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    return 0;
}
Mostra soluzione
++a = 6
a = 6
a++ = 6
a = 7
b = 7
c = 7
b = 8
c = 7

Competenze

4) Media di tre numeri
#include <iostream>
int main(){ double a,b,c; std::cin>>a>>b>>c;
    std::cout << "media=" << (a+b+c)/3 << '\n'; }
5) Area del cerchio circoscritto a un quadrato (lato L)

Raggio = L/√2. Area = π r² = π (L²/2).

double L; std::cin>>L; double area = 3.1415926535 * (L*L/2.0);
6) Triangolo isoscele con lati AB (lato obliquo) e BC (base): perimetro e area

Perimetro = 2·AB + BC. Altezza = √(AB² − (BC/2)²). Area = (BC·altezza)/2.

7) Radice di ax = b (a ≠ 0)
double a,b; std::cin>>a>>b;
if(a!=0) std::cout<<"x="<<b/a; else std::cout<<"impossibile";
8) Costo del vino a €1,70/L
double litri; std::cin>>litri; std::cout<<litri*1.70;
9) Gradi (G), primi (P), secondi (S) ➜ secondi totali
long G,P,S; std::cin>>G>>P>>S;
long tot = G*3600 + P*60 + S;
10) Giorni/ore/minuti/secondi ➜ secondi totali
long g,o,m,s; std::cin>>g>>o>>m>>s;
long N = g*86400 + o*3600 + m*60 + s;
11) Additivo per vernice

10 g/kg fino a 10 kg, poi 5 g/kg oltre. Esempio:

double kg; std::cin>>kg;
double add = (kg<=10? kg*10.0 : 10*10.0 + (kg-10)*5.0);
12) Scatti telefonici e bolletta

Scatti usati = attuale − precedente. Totale = scatti·costo + canone.

long prev, cur; double costo, canone;
std::cin>>prev>>cur>>costo>>canone;
long scatti = cur - prev;
double totale = scatti*costo + canone;