Variabili e Tipi di Dato
Tutto ciò che devi sapere su come il C gestisce i dati in memoria.
Cos'è una Variabile?
Una variabile è uno spazio di memoria con un nome. Il tipo di dato determina quanti byte occupa e come viene interpretato il contenuto.
#include <stdio.h>
int main() {
// Dichiarazione (crea lo spazio in memoria)
int eta;
// Inizializzazione (assegna un valore)
eta = 25;
// Dichiarazione + inizializzazione in una riga
int anno = 2024;
double altezza = 1.75;
char iniziale = 'M';
// Costante: non può essere modificata dopo l'inizializzazione
const double PI = 3.14159265;
printf("Età: %d, Anno: %d\n", eta, anno);
printf("Altezza: %.2f m, Iniziale: %c\n", altezza, iniziale);
printf("PI greco: %.5f\n", PI);
return 0;
}
Tipi di Dato Fondamentali
| Tipo | Dimensione tipica | Range | Formato printf |
|---|---|---|---|
| char | 1 byte | -128 a 127 (o 0 a 255) | %c / %d |
| int | 4 byte | -2.147.483.648 a 2.147.483.647 | %d |
| short | 2 byte | -32.768 a 32.767 | %hd |
| long | 4-8 byte | dipende dalla piattaforma | %ld |
| long long | 8 byte | ±9.223.372.036.854.775.807 | %lld |
| float | 4 byte | ~±3.4×10³⁸ (7 cifre) | %f |
| double | 8 byte | ~±1.7×10³⁰⁸ (15 cifre) | %lf / %f |
| unsigned int | 4 byte | 0 a 4.294.967.295 | %u |
#include <stdio.h>
int main() {
printf("char: %zu byte\n", sizeof(char));
printf("short: %zu byte\n", sizeof(short));
printf("int: %zu byte\n", sizeof(int));
printf("long: %zu byte\n", sizeof(long));
printf("long long: %zu byte\n", sizeof(long long));
printf("float: %zu byte\n", sizeof(float));
printf("double: %zu byte\n", sizeof(double));
return 0;
}
char: 1 byte short: 2 byte int: 4 byte long: 8 byte long long: 8 byte float: 4 byte double: 8 byte
Overflow e Limiti
Quando un valore supera il massimo rappresentabile dal tipo, si verifica un overflow: il valore "torna dall'inizio" (comportamento undefined per i signed in C).
#include <stdio.h>
#include <limits.h> // Costanti per i limiti dei tipi interi
#include <float.h> // Costanti per i limiti dei tipi floating-point
int main() {
printf("INT_MAX = %d\n", INT_MAX);
printf("INT_MIN = %d\n", INT_MIN);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("LLONG_MAX= %lld\n", LLONG_MAX);
printf("FLT_MAX = %e\n", FLT_MAX);
printf("DBL_MAX = %e\n", DBL_MAX);
// Esempio di overflow (comportamento non definito per signed!)
unsigned char uc = 255;
uc++; // 255 + 1 = 0 per unsigned (ben definito)
printf("255 + 1 (unsigned char) = %d\n", uc);
return 0;
}
signed è undefined behavior in C: il compilatore può fare qualsiasi cosa. Usa unsigned se hai bisogno del wrap-around garantito.
Type Casting (Conversione di Tipo)
Puoi convertire esplicitamente un valore da un tipo all'altro con il cast esplicito, oppure il compilatore lo fa automaticamente (cast implicito).
#include <stdio.h>
int main() {
int a = 7, b = 2;
// Divisione intera (risultato: 3, NON 3.5)
printf("7 / 2 (int) = %d\n", a / b);
// Cast esplicito: uno degli operandi diventa double
printf("7 / 2 (double) = %.1f\n", (double)a / b);
// Cast implicito: da int a double
double d = a; // 7 → 7.0
printf("int->double: %.1f\n", d);
// Troncamento: da double a int
double pi = 3.99;
int piInt = (int)pi; // Tronca, NON arrotonda!
printf("3.99 -> int: %d\n", piInt);
// char e int sono intercambiabili
char c = 'A';
printf("'A' come intero: %d\n", (int)c); // 65
printf("65 come char: %c\n", (char)65); // A
return 0;
}
7 / 2 (int) = 3 7 / 2 (double) = 3.5 int->double: 7.0 3.99 -> int: 3 'A' come intero: 65 65 come char: A
double a int tronca (rimuove i decimali), non arrotonda. 3.99 diventa 3, non 4.
Variabili in C++ — Novità
Il C++ aggiunge funzionalità utili per la gestione delle variabili:
#include <iostream>
#include <string> // std::string
using namespace std;
int main() {
// auto: il compilatore deduce il tipo automaticamente
auto x = 42; // int
auto y = 3.14; // double
auto nome = "Mario"; // const char*
// bool: tipo booleano nativo (assente in C puro)
bool attivo = true;
bool chiuso = false;
cout << "Attivo: " << attivo << "\n"; // stampa 1
// std::string: gestione avanzata delle stringhe
string saluto = "Ciao, ";
string utente = "Mondo";
string messaggio = saluto + utente + "!"; // concatenazione con +
cout << messaggio << "\n";
// cin per input (alternativa a scanf)
string cognome;
cout << "Cognome: ";
cin >> cognome;
cout << "Hai scritto: " << cognome << "\n";
return 0;
}
Scrivi un programma che stampi la dimensione in byte di tutti i tipi fondamentali (char, short, int, long, long long, float, double, long double) sul tuo sistema usando sizeof.
#include <stdio.h>
int main() {
printf("%-15s %s\n", "Tipo", "Byte");
printf("----------------------------\n");
printf("%-15s %zu\n", "char", sizeof(char));
printf("%-15s %zu\n", "short", sizeof(short));
printf("%-15s %zu\n", "int", sizeof(int));
printf("%-15s %zu\n", "long", sizeof(long));
printf("%-15s %zu\n", "long long", sizeof(long long));
printf("%-15s %zu\n", "float", sizeof(float));
printf("%-15s %zu\n", "double", sizeof(double));
printf("%-15s %zu\n", "long double", sizeof(long double));
return 0;
}Scrivi un programma che legga un carattere da tastiera e stampi: il carattere stesso, il suo codice ASCII, il carattere successivo e quello precedente nella tabella ASCII.
#include <stdio.h>
int main() {
char c;
printf("Inserisci un carattere: ");
scanf(" %c", &c); // spazio prima di %c per saltare whitespace
printf("Carattere: %c (ASCII: %d)\n", c, (int)c);
printf("Precedente: %c (ASCII: %d)\n", c-1, (int)(c-1));
printf("Successivo: %c (ASCII: %d)\n", c+1, (int)(c+1));
return 0;
}Leggi due interi. Stampa: la divisione intera, il resto (%), la divisione in virgola mobile (double). Poi converti il risultato double in int e mostra la perdita di informazione.
#include <stdio.h>
int main() {
int a, b;
printf("Inserisci due interi: ");
scanf("%d %d", &a, &b);
if (b == 0) {
printf("Errore: divisione per zero!\n");
return 1;
}
printf("Divisione intera: %d / %d = %d\n", a, b, a / b);
printf("Resto: %d %% %d = %d\n", a, b, a % b);
double risultato = (double)a / b;
printf("Divisione reale: %.6f\n", risultato);
printf("Troncato a int: %d\n", (int)risultato);
return 0;
}Leggi un numero di secondi (usa long long). Convertilo in giorni, ore, minuti e secondi rimanenti. Es: 90061 → 1 giorno, 1 ora, 1 minuto, 1 secondo.
#include <stdio.h>
int main() {
long long totale;
printf("Inserisci i secondi totali: ");
scanf("%lld", &totale);
long long giorni = totale / 86400;
long long ore = (totale % 86400) / 3600;
long long minuti = (totale % 3600) / 60;
long long secondi = totale % 60;
printf("%lld secondi = %lld giorni, %lld ore, %lld minuti, %lld secondi\n",
totale, giorni, ore, minuti, secondi);
return 0;
}Scrivi un programma che dimostri visivamente l'overflow: prendi INT_MAX e aggiungi 1 (con cast a long long per vedere il vero valore), poi fai lo stesso con unsigned int (UINT_MAX + 1). Infine, calcola quante volte ci sta INT_MAX in LLONG_MAX.
#include <stdio.h>
#include <limits.h>
int main() {
printf("=== Dimostrazione Overflow ===\n\n");
// INT_MAX + 1 (usando long long per non perdere il valore)
long long oltre = (long long)INT_MAX + 1;
printf("INT_MAX = %d\n", INT_MAX);
printf("INT_MAX + 1 = %lld\n", oltre);
printf("INT_MAX + 1 (int)= %d\n", INT_MAX + 1); // overflow!
printf("\n");
// unsigned: wrap-around ben definito
unsigned int u = UINT_MAX;
printf("UINT_MAX = %u\n", u);
printf("UINT_MAX + 1 = %u\n", u + 1); // 0
printf("\n");
// Quante volte INT_MAX sta in LLONG_MAX?
long long quante = LLONG_MAX / INT_MAX;
printf("LLONG_MAX / INT_MAX = %lld\n", quante);
return 0;
}