Operatore sizeof in C++

Concetti Chiave
  • L'operatore sizeof restituisce la dimensione, in byte, di un'espressione o di un nome di tipo.
  • sizeof è associativo a destra e restituisce un'espressione costante di tipo size_t.
  • sizeof non valuta il suo operando, rendendolo sicuro da usare con puntatori non validi.
  • La dimensione restituita da sizeof dipende dal tipo coinvolto, come char, puntatori, array e tipi di classe.
  • sizeof di un array restituisce la dimensione totale dell'array, non convertendolo in un puntatore.
  • Il risultato di sizeof può essere utilizzato per specificare la dimensione di un array.

L'Operatore sizeof

L'operatore sizeof restituisce la dimensione, in byte, di un'espressione o di un nome di tipo. L'operatore è associativo a destra. Il risultato di sizeof è un'espressione costante di tipo size_t. L'operatore assume una di due forme:

sizeof (type)
sizeof expr

Nella seconda forma, sizeof restituisce la dimensione del tipo restituito dall'espressione data. L'operatore sizeof è insolito nel fatto che non valuta il suo operando:

Libro data, *p;

// dimensione richiesta per contenere un oggetto di tipo Libro
sizeof(Libro);

// dimensione del tipo di data, cioè sizeof(Libro)
sizeof data;

// dimensione di un puntatore
sizeof p;

// dimensione del tipo a cui p punta, cioè sizeof(Libro)
sizeof *p;

// dimensione del tipo del membro costo di Libro
sizeof data.costo;

// modo alternativo per ottenere la dimensione di costo
sizeof Libro::costo;

Il più interessante di questi esempi è sizeof *p. Primo, poiché sizeof è associativo a destra e ha la stessa precedenza di *, questa espressione si raggruppa da destra a sinistra. Cioè, è equivalente a sizeof(*p). Secondo, poiché sizeof non valuta il suo operando, non importa che p sia un puntatore non valido (cioè, non inizializzato). Dereferenziare un puntatore non valido come operando di sizeof è sicuro perché il puntatore non viene effettivamente usato. sizeof non ha bisogno di dereferenziare il puntatore per sapere quale tipo restituirà.

A partire dallo standard C++11, possiamo usare l'operatore di ambito per chiedere la dimensione di un membro di un tipo classe. Normalmente possiamo accedere ai membri di una classe solo attraverso un oggetto di quel tipo. Non abbiamo bisogno di fornire un oggetto, perché sizeof non ha bisogno di recuperare il membro per conoscere la sua dimensione.

Il risultato dell'applicazione di sizeof dipende in parte dal tipo coinvolto:

  • sizeof char o un'espressione di tipo char è garantito essere 1.
  • sizeof di un tipo riferimento restituisce la dimensione di un oggetto del tipo riferito.
  • sizeof di un puntatore restituisce la dimensione necessaria per contenere un puntatore.
  • sizeof di un puntatore dereferenziato restituisce la dimensione di un oggetto del tipo a cui il puntatore punta; il puntatore non deve essere valido.
  • sizeof di un array è la dimensione dell'intero array. È equivalente a prendere il sizeof del tipo elemento per il numero di elementi nell'array. Nota che sizeof non converte l'array in un puntatore.
  • sizeof di una string o di un vector restituisce solo la dimensione della parte fissa di questi tipi; non restituisce la dimensione usata dagli elementi dell'oggetto.

Poiché sizeof restituisce la dimensione dell'intero array, possiamo determinare il numero di elementi in un array dividendo la dimensione dell'array per la dimensione dell'elemento:

// sizeof(ia) / sizeof(*ia) restituisce il numero di elementi in ia
constexpr size_t sz = sizeof(ia) / sizeof(*ia);

// ok sizeof restituisce un'espressione costante
int arr2[sz];

Poiché sizeof restituisce un'espressione costante, possiamo usare il risultato di un'espressione sizeof per specificare la dimensione di un array.

Esercizi

  • Scrivi un programma che stampa la dimensione di vari tipi primitivi del C++.

    Soluzione:

    #include <iostream>
    #include <string>
    #include <vector>
    
    int main() {
        std::cout << "Dimensione di char: " << sizeof(char) << " byte\n";
        std::cout << "Dimensione di int: " << sizeof(int) << " byte\n";
        std::cout << "Dimensione di float: " << sizeof(float) << " byte\n";
        std::cout << "Dimensione di double: " << sizeof(double) << " byte\n";
        std::cout << "Dimensione di bool: " << sizeof(bool) << " byte\n";
        std::cout << "Dimensione di long: " << sizeof(long) << " byte\n";
        std::cout << "Dimensione di long long: " << sizeof(long long) << " byte\n";
        std::cout << "Dimensione di puntatore a int: " << sizeof(int*) << " byte\n";
        std::cout << "Dimensione di puntatore a char: " << sizeof(char*) << " byte\n";
        std::cout << "Dimensione di string: " << sizeof(std::string) << " byte\n";
        std::cout << "Dimensione di vector<int>: " << sizeof(std::vector<int>) << " byte\n";
    
        return 0;
    }
    
  • Cosa fanno le seguenti espressioni? Spiega il risultato di ciascuna.

    int x[10]; int *p = x;
    cout << sizeof(x)/sizeof(*x) << endl;
    cout << sizeof(p)/sizeof(*p) << endl;
    

    Soluzione:

    La prima espressione sizeof(x)/sizeof(*x) calcola il numero di elementi nell'array x. sizeof(x) restituisce la dimensione totale dell'array in byte (ad esempio, 40 byte se int è 4 byte e l'array ha 10 elementi). sizeof(*x) restituisce la dimensione di un singolo elemento dell'array, che è la dimensione di un int (ad esempio, 4 byte). Dividendo la dimensione totale dell'array per la dimensione di un singolo elemento otteniamo il numero di elementi nell'array, che è 10.

    La seconda espressione sizeof(p)/sizeof(*p) calcola la dimensione del puntatore p divisa per la dimensione dell'oggetto a cui p punta. sizeof(p) restituisce la dimensione del puntatore stesso (ad esempio, 8 byte su un sistema a 64 bit). sizeof(*p) restituisce la dimensione di un int, che è 4 byte. Dividendo la dimensione del puntatore per la dimensione di un int otteniamo 2 (ad esempio, 8/4 = 2). Questo risultato non ha un significato pratico in questo contesto, poiché non rappresenta il numero di elementi in un array, ma piuttosto il rapporto tra la dimensione del puntatore e la dimensione del tipo a cui punta.

  • Utilizzando la tabella degli operatori, metti tra parentesi le seguenti espressioni affinché corrispondano alla valutazione predefinita:

    sizeof x + y
    sizeof p->mem[i]
    sizeof a < b
    sizeof f()
    

    Soluzione:

    • sizeof(x) + y
    • sizeof((p->mem)[i])
    • (sizeof(a)) < b
    • sizeof(f())