In informatica (principalmente nella programmazione), un tipo di dato si dice primitivo o atomico se definisce un insieme di valori atomici, che non possono essere ulteriormente suddivisi o decomposti in tipi di dati più semplici. Si tratta pertanto di un tipo di dato fondamentale, fornito nativamente da un linguaggio di programmazione come elemento costruttivo di base.
I tipi primitivi vengono spesso adoperati in opposizione ai tipi strutturati o composti che, al contrario, aggregano più tipi atomici per formare delle strutture dati più complesse (come gli array, i record o le classi).
A seconda dell'architettura hardware e del compilatore, i tipi primitivi corrispondono generalmente in modo diretto alle rappresentazioni dei dati native così come vengono salvati e manipolati dal processore in uso, garantendo pertanto la massima velocità possibile di elaborazione.
Tuttavia, questa corrispondenza non ha validità universale e rischia di generare confusione tra la classificazione strutturale (tipi primitivi e composti) e la modalità di gestione del dato in memoria (tipi valore e tipi riferimento).
La prima distinzione riguarda unicamente la struttura del dato, separando i tipi indivisibili (atomici) da quelli divisibili (composti). La seconda classificazione, al contrario, definisce come il dato viene gestito in memoria, distinguendo tra tipi valore (il cui contenuto è allocato direttamente all'interno della variabile) e tipi riferimento (quando la variabile memorizza solo l'indirizzo di memoria in cui risiede il dato reale). Non è sempre vero che un tipo primitivo sia anche un tipo valore, così come non è affatto detto che un tipo composto sia anche un tipo di riferimento.
I tipi primitivi si distinguono nel panorama della programmazione per alcune proprietà architetturali e semantiche:
Benché ogni linguaggio di programmazione possa definire un proprio set specifico di primitivi, la maggior parte dei linguaggi moderni e di alto livello condivide un gruppo di tipi di dati standard:
Rappresentano i numeri appartenenti all'insieme dei numeri interi. Possono variare in base allo spazio di memoria allocato (ad esempio 8, 16, 32 o 64 bit) e alla possibilità di includere o meno il segno aritmetico (con segno o senza segno). Esempi tipici sono byte, short, int e long.
Rappresentano un'approssimazione dei numeri reali, dotati di parte frazionaria. La maggior parte dei sistemi moderni implementa questi tipi seguendo lo standard internazionale IEEE 754. I formati più diffusi sono a precisione singola (tipicamente indicati come float a 32 bit) e a doppia precisione (indicati come double a 64 bit).
Rappresentano una variabile logica a due stati, che può assumere unicamente i valori di vero (true) o falso (false). Vengono ampiamente impiegati per la valutazione di espressioni condizionali e per il controllo del flusso logico dei programmi.
Rappresentano un singolo simbolo grafico o di controllo. Solitamente identificati con la parola chiave char, la loro codifica è storicamente basata sullo standard ASCII (a 8 bit) o, nei linguaggi più recenti, sullo standard Unicode (a 16 o 32 bit, come UTF-16) per consentire la rappresentazione di alfabeti internazionali.
(Nota: Le stringhe di testo, sebbene fornite nativamente in quasi tutti i linguaggi, sono spesso considerate tipi derivati o composti poiché strutturate internamente come sequenze o array di caratteri).
L'implementazione e i dettagli di allocazione dei tipi primitivi differiscono sensibilmente a seconda del paradigma del linguaggio di programmazione:
In C e C++, le dimensioni fisiche in bit dei tipi primitivi fondamentali (come int o char) non sono definite in modo rigido dallo standard del linguaggio, ma dipendono strettamente dall'architettura del processore di destinazione e dal compilatore utilizzato. Per superare questa variabilità, standard più recenti (come C99 e C++11) hanno introdotto librerie di tipi interi a dimensione fissa definita, quali int32_t o uint64_t.
Java è un linguaggio orientato agli oggetti, ma per ragioni di velocità e prestazioni ha mantenuto anche i tipi primitivi, che non sono oggetti ma semplici valori salvati direttamente in memoria. Nella definizione di questi tipi, adotta una classificazione rigida e indipendente dall'architettura hardware sottostante. Definisce esattamente otto tipi primitivi, le cui specifiche e dimensioni sono immutabili su qualunque piattaforma di esecuzione (Java Virtual Machine):
Tuttavia, ci sono situazioni in cui Java pretende assolutamente un oggetto e non accetta un tipo primitivo. Per evitare problemi di compatibilità, per ciascuno di questi tipi il linguaggio fornisce una classe "wrapper" corrispondente (come Integer per il tipo int) che consente di trattare i primitivi come oggetti qualora sia richiesto dall'architettura orientata agli oggetti del software. Queste classi forniscono inoltre ulteriori vantaggi e flessibilità, permettendo ad esempio di definire valori null e sfruttare metodi standard. La conversione tra tipo primitivo e classe wrapper (o viceversa) viene infine gestita automaticamente dal linguaggio attraverso un processo denominato autoboxing.
In JavaScript, i tipi primitivi sono predefiniti a livello di runtime e i loro valori sono immutabili (ossia non modificabili dopo la creazione). Il linguaggio individua i seguenti primitivi:
Python si discosta dall'approccio classico orientato ai tipi valore: nel linguaggio "tutto è un oggetto". Ne consegue che anche i tipi fondamentali come int, float e bool sono istanze di classi specifiche con metodi integrati, benché l'interprete esegua ottimizzazioni interne trasparenti per garantire l'efficienza tipica dei tipi primitivi tradizionali.