Il linguaggio C controlla tutto intorno a me

FYI.

This story is over 5 years old.

Tecnologia

Il linguaggio C controlla tutto intorno a me

Una breve storia del linguaggio programmato per dominare tutti gli altri linguaggi.

C.

C si trova dappertutto e all'interno di qualsiasi cosa. Nel rover marziano Curiosity, nel sistema operativo di ogni computer, di ogni smartphone, nelle virtual machine Java, in Google Chrome, nei bancomat, nelle auto, nei robot delle sale operatorie, nei computer che hanno programmato i robot delle sale operatorie, nei computer che hanno programmato questi ultimi e infine C alimenta se stesso in quanto linguaggio di implementazione di se stesso.

Pubblicità

Quando la civiltà tecnologica umana sarà estinta—forse a causa di una guerra nucleare programmata in C o come risultato di un'epidemia causata da un qualche batterio super-resistente scoperto da un software implementato in C—e noi per sopravvivere torneremo ad abitare le caverne rosicchiando ossa o contendendoci qualsiasi carogna ci capiti a tiro, da qualche parte ci sarà ancora un programma scritto in C in esecuzione.

Tutto questo non solamente perché a molti piace programmare in C, anche se le stime dicono che quasi il 20 percento dei programmatori lo utilizza (guardate il video sotto). C'è molto più di quello a cui comunemente si pensa riferendosi ai "linguaggi di programmazione". Alcuni linguaggi che consideriamo più o meno fondamentali come Java, Python, Ruby, Lisp, eccetera, vengono utilizzati per moltissimi scopi. Anche C è sfruttabile nei campi più vari ma ciò che lo rende speciale è il suo essere diventato de facto il linguaggio stesso delle macchine, che si tratti di un microcontroller da due soldi o di una navicella spaziale.

Una singola puntata di Know Your Language dedicata a C non sarebbe sufficiente. Leggersi 10 minuti di Processing dovrebbe equivalere a sei ore di lettura di C. Io non posso ambire a fornire la stessa quantità di informazioni neanche in due o tre puntate ma posso almeno cercare di avvicinarmi a delineare cosa sia questo fondamentale linguaggio di programmazione.

Pubblicità

Partiamo dall'inizio. Da dove viene C? Com'è diventato la forza della natura che quasi sicuramente continuerà a essere?

Pura fortuna? Tempismo? Preveggenza?

Principalmente la necessità che aguzza l'ingegno.

Ken Thompson seduto e Dennis Ritchie con il PDP-11. Immagine: Peter Hamer

EVOLVERE UNIX

Negli anni Sessanta e nei primi Settanta, il concetto di portabilità dei software non era ancora diffuso, come ricorda in un saggio del 1993 sulla storia dello sviluppo di C uno dei suoi creatori, Dennis Ritchie. Quando dovevi scrivere un programma, lo facevi per un tipo specifico di computer. Se qualcuno se ne usciva fuori con un nuovo computer, eseguirci sopra il sistema operativo Unix, per fare un esempio, significava sostanzialmente riscriverlo per intero.

Oggi la portabilità è così estesa che possiamo utilizzare Windows 10 su qualsiasi apparecchio, dal mio IdeaPad della Lenovo ad un tablet o telefono, da un Raspberry Pi a potenzialmente un'intera galassia di strumenti piccolissimi, economici e super connessi. Windows 10 potrebbe rivelarsi il sistema operativo standard dell'Internet delle cose, ammesso che il concetto di Internet delle cose potesse risultare comprensibile agli informatici del 1972, risulta eccezionale anche a noi per la sua abilità di adattarsi a qualsiasi oggetto tecnologico che possiamo immaginare.

La prima versione di Unix è stata scritta nel 1969 da Ken Thompson, che ora lavora per Google, altro grande esempio di portabilità estrema con Chrome e ChromeOS. OG Unix è stato creato per il DEC PDP-7, un "minicomputer" da 72.000 dollari (64.000 euro) che occupava approssimativamente le dimensioni di una parete. OG Unix venne scritto in istruzioni espresse nel linguaggio assemblativo specifico del PDP-7, cioè nel linguaggio specifico di quella determinata macchina.

Pubblicità

Il tutto consisteva in una semplice interfaccia a righe di comando con un compilatore ed un interprete. L'innovazione di questa versione di Unix era l'autosufficienza: il software poteva essere scritto e testato sullo stesso computer a cui era destinato. In precedenza, il PDP-7 pre-Unix richiedeva che si programmasse su una macchina differente, la GE-635, stampando il tutto per poi darlo in pasto al computer per cui il programma era stato pensato.

Ora la PDP-7 poteva essere utilizzata per sviluppare i suoi stessi programmi e ancora meglio per continuare a sviluppare il suo stesso sistema operativo Unix.

"Il programma assembler del PDP-7 di Thompson superava in semplicità persino quello della DEC", ricorda Ritchie, "valutava espressioni e restituiva gli output corrispondenti. Non c'erano librerie, nessun loader o link editor: l'intero programma sorgente veniva sottoposto all'assembler, il file che produceva come output aveva un nome specifico ed era direttamente eseguibile".

Subito dopo, Malcolm Douglas McIlroy, un tecnico del Bell Labs che sarebbe diventato una delle figure cruciali nello sviluppo di Unix, scrisse un linguaggio di alto livello per Unix, chiamato TMG, uno strumento che ebbe vita breve concepito per essere un "generatore di parser": un programma per scrivere nuovi compilatori, i meta-programmi fondamentali che convertono linguaggi di alto livello (come Fortran all'epoca) in linguaggio assembly.

Pubblicità

TMG diede lo spunto a Thompson per creare un linguaggio di programmazione di sistema (SPL) per quello che poi si sarebbe chiamato Unix. Un SPL programma i software per interfacciarsi con la macchina stessa: sistemi operativi, utility, driver, compilatori. È un intermediario tra qualsiasi altro linguaggio di programmazione e il lavoro svolto dalle macchine.

Un linguaggio di programmazione di sistema si trova ad un livello di astrazione superiore rispetto al linguaggio assembly e alle istruzioni macchina ma, a loro differenza, è indipendente dalla macchina. Trattandosi di un'astrazione, opera al livello di un sistema idealizzato, il semplice schema di quello che un computer è nella realtà. Non è come una virtual machine, tipo Java, piuttosto fornisce le corrispondenze tra linguaggio naturale e quello della macchina senza la necessità di specificare i dettagli del computer su cui verrà eseguito, questo ultimo processo avviene quando il sistema stesso compila il linguaggio di programmazione di sistema in linguaggio Assembly.

Potremmo dire che C è un linguaggio di calcolo di per sé, situato ad un livello di astrazione tale da potersi definire universale ma con l'abilità di comunicare con l'hardware.

Questo è il perché dell'importanza di C, la sua eleganza, longevità e onnipresenza, che è come dire il perché dei computer stessi e lo capiremo più in dettaglio proseguendo con il nostro racconto.

Pubblicità

Il PDP-7. Immagine: Wiki

A, B, C

Quindi, Thompson scrisse B, il discendente di un altro linguaggio chiamato BCPL, un altro generatore di parser che può essere visto come un linguaggio di programmazione di sistema. Nella scala gerarchica dei linguaggi andava a posizionarsi tra il compilatore, il livello in cui il codice più astratto viene convertito in assembly e tutto il resto del computer.

B non era in circolazione da molto tempo quando Ritchie completò C. Il nuovo linguaggio si trattava essenzialmente di B con l'aggiunta dei "tipi di dato". In B, tutti i dati erano la stessa cosa (numeri interi, caratteri, numeri in virgola mobile) e riferirsi ad essi con una variabile significava semplicemente indicare un indirizzo di memoria, qualsiasi cosa contenesse. In pratica, non era molto diverso dal modo in cui linguaggi interpretati come JavaScript gestiscono i dati, ma era comunque limitante in quanto ogni dichiarazione di variabile richiedeva un singola porzione di memoria, indipendentemente da quanta ne fosse effettivamente richiesta.

In C, quindi, un programmatore può specificare se questa o quella unità di dati necessitino di un singolo byte o otto o più byte di memoria. Questo rispondeva ad un nuovo tipo di necessità, dato che i computer più recenti erano in grado di manipolare dati al livello di singoli byte invece che riferirsi ai pacchetti di due byte noti come word. C è arrivato al momento giusto per approfittare di questo cambiamento.

Pubblicità

Coinvolgente vero? Tenete a mente: tipi di dati.

Forse è riduttivo porla in questi termini ma è questo l'aspetto cruciale che portò C a diventare così importante. B concepiva la memoria come una serie di "celle" delle stesse dimensioni, che corrispondevano a due bytes di dati, detti "word" a livello hardware. Nel 1970 circa, nacquero nuove macchine, in particolare la PDP-11, che resero questo concetto sempre più incasinato, richiedendo procedure complesse per elaborare stringhe di dati. Il primo passo in avanti giunse nel 1971 grazie a NB o "new B," un linguaggio agile creato da Ritchie che aggiungeva qualche tipo di dati.

I tipi di dati erano "int" e "char," utilizzati per memorizzare numeri interi e numeri interi che corrispondono a caratteri (lettere). NB consentì ai programmatori di indicare singole lettere e numeri ma intere serie di essi. Così nel caso in cui avessi voluto memorizzare questo mio articolo, avrei potuto specificare una singola variabile con un singolo nome, facendo sì che quella singola variabile corrispondesse ad una lista di diverse migliaia di caratteri consecutivi. Questi nomi di variabili singole venivano associate a degli indirizzi di memoria consentendo un livello di organizzazione dei dati flessibile e comprensibile per la macchina.

Durante la transizione da B a C vennero sviluppare altre migliorie, tipi di dati più generalizzabili, traducibilità diretta in linguaggio assembly, insomma, Ritchie era andato oltre B e "NB" creando qualcosa di diverso: C.

Pubblicità

Dopo il battesimo del linguaggio le cose cominciarono a muoversi molto velocemente e C a somigliare sempre di più a quello che utilizziamo oggi, inclusa l'aggiunta di operatori booleani come && e || per specificare due espressioni differenti e confrontarle. L'operatore booleano && fa sì che il computer restituisca come output "true" se entrambe le espressioni sono vere o "false" se non lo sono. L'operatore ||, invece, restituisce "true" nel caso in cui siano vere solo una o entrambe. Questi mattoncini della programmazione sono fondamentali non solo per C ma per qualsiasi altro linguaggio di programmazione successivo.

Nello stesso periodo emerse anche il concetto di "preprocessore" che consentì di includere in un programma interi file e librerie di codice utilizzando la semplice istruzione "#include". Un'altra innovazione fu la definizione delle macro, praticamente insiemi di istruzioni: tramite l'espressione "#define", un breve frammento di codice viene associato ad un nome. Dal punto di vista concettuale il preprocessore è un modo per ordinare al compilatore di eseguire del lavoro extra prima di compilare il programma.

Ancora una volta: ci troviamo di fronte alla nascita di uno strumento di programmazione fondamentale che in seguito ha preso piede. Il livello di complessità raggiunto era pronto a svilupparsi nelle sfilze di righe codice che divennero i software successivi.

IL LIBRO BIANCO

Circa nel 1972/1973 C venne portato a termine. Ritchie riscrisse il kernel Unix per il PDP-11 in C e altri si preoccuparono di riscrivere il compilatore in C per diversi tipi di macchine.

"Nonostante molti compilatori efficenti per l'IBM System/370 e altre macchine fossero basati su di esso, molto dello sforzo per le modifiche consisteva nel liberarsi dei presupposti del PDP-11", scrissero Ritchie e Thompson in uno studio del 1978 dei Bell Labs: "Portability of C Programs and the UNIX System", aggiungendo, "persino prima che ci venisse in mente l'idea di trasporre UNIX su altre macchine, era chiaro che C era così efficiente da giustificare la creazione di compilatori per un numero sempre maggiore di macchine".

Mentre venivano rilasciati i compilatori per le nuove macchine e mentre l'utilizzo di C si diffondeva a macchia d'olio, vennero rilasciate le prime librerie del linguaggio, incluse le prime librerie per l'input/output ovvero le routine standard I/O di C. Indubbiamente quello che contribuì al successo di C fu anche l'ampia disponibilità di documentazione, sotto forma del libro di testo di Ritchie The C Programming Language, soprannominato il "libro bianco" che era e rimane la guida più autorevole oltre ad essere il testo introduttivo fondamentale per questo linguaggio. All'interno delle sue pagine nacque anche il celebre programma "hello, world":

Nella decade seguente, vennero scritti compilatori in C per mainframe, minicomputer, microcomputer e infine anche per il PC della IBM. Nel 1983, l'American National Standards Institute (ANSI) iniziò a lavorare su una specifica per rendere C uno standard, con il risultato raggiunto finalmente nel 1989 sotto forma di ANSI C, essenzialmente l'unico e vero C, assicura ai programmatori che il linguaggio si comporti nello stesso modo indipendentemente da come o da dove viene implementato. ANSI C è il primo C definitivo.

Il C definitivo assomiglia molto alla definizione astratta di computer. Potrei scrivere proprio ora un programma che manipoli i singoli bit all'interno di un indirizzo di memoria attraverso una macchina concreta, di qualsiasi macchina si tratti, utilizzando degli operatori standard di C. Posso predire con un margine ragionevole di certezza come il mio codice in C sarà tradotto in istruzioni macchina e posso utilizzare questa conoscenza per scrivere del codice più veloce e agile, oppure per scrivere linguaggi di programmazione completamente nuovi. Un alfabeto per scrivere nuovi alfabeti: A,B,C.