La risposta alla tua domanda dipende da quale linguaggio C sta chiedendo.
La lingua descritta nel Manuale di riferimento C del 1974 di Dennis Ritchie era un linguaggio di basso livello che offriva alcune delle comodità di programmazione di linguaggi di livello superiore. Allo stesso modo, i dialetti derivati da quel linguaggio erano tendenzialmente linguaggi di programmazione di basso livello.
Quando lo standard C del 1989/1990 fu pubblicato, tuttavia, non descriveva il linguaggio di basso livello che era diventato popolare per la programmazione di macchine reali, ma invece descriveva un linguaggio di livello superiore che poteva essere - ma non doveva essere- -attuato in termini di livello inferiore.
Come gli autori della nota C Standard, una delle cose che ha reso utile il linguaggio è stata che molte implementazioni potevano essere trattate come assemblatori di alto livello. Poiché C era anche usato come alternativa ad altri linguaggi di alto livello e poiché molte applicazioni non richiedevano la capacità di fare cose che i linguaggi di alto livello non potevano fare, gli autori dello Standard permisero alle implementazioni di comportarsi in modo arbitrario se i programmi tentassero di usare costrutti di basso livello. Di conseguenza, il linguaggio descritto dallo standard C non è mai stato un linguaggio di programmazione di basso livello.
Per comprendere questa distinzione, considera come Ritchie's Language e C89 visualizzerebbero lo snippet di codice:
struct foo { int x,y; float z; } *p;
...
p[3].y+=1;
su una piattaforma in cui "char" è 8 bit, "int" è big-endian a 16 bit, "float" è 32 bit e le strutture non hanno particolari requisiti di riempimento o allineamento, quindi la dimensione di "struct foo" è di 8 byte.
Su Ritchie's Language, il comportamento dell'ultima istruzione prenderebbe l'indirizzo memorizzato in "p", aggiungerebbe 3 * 8 + 2 [ie 26] byte ad esso e otterrebbe un valore a 16 bit dai byte a quell'indirizzo e al successivo , aggiungine uno a quel valore e quindi riscrivi quel valore a 16 bit negli stessi due byte. Il comportamento sarebbe definito come agendo sul 26 ° e 27 ° byte dopo quello all'indirizzo p, indipendentemente dal tipo di oggetto memorizzato lì.
Nel linguaggio definito dallo standard C, nel caso in cui * p identifichi un elemento di "struct foo []" seguito da almeno altri tre elementi completi di quel tipo, l'ultima istruzione ne aggiungerebbe uno al membro y di il terzo elemento dopo * p. Il comportamento non sarebbe definito dalla norma in nessun'altra circostanza.
Il linguaggio di Ritchie era un linguaggio di programmazione di basso livello perché, mentre permetteva a un programmatore di usare astrazioni come matrici e strutture quando era conveniente, definiva il comportamento in termini di layout sottostante degli oggetti in memoria. Al contrario, il linguaggio descritto da C89 e successivi standard definisce le cose in termini di un'astrazione di livello superiore e definisce solo il comportamento del codice che è coerente con quello. Le implementazioni di qualità adatte alla programmazione di basso livello si comporteranno utilmente in più casi di quelli previsti dalla norma, ma non esiste un documento "ufficiale" che specifichi cosa deve fare un'implementazione per essere adatta a tali scopi.
Il linguaggio C inventato da Dennis Ritchie è quindi un linguaggio di basso livello ed è stato riconosciuto come tale. Il linguaggio inventato dal C Standards Committee, tuttavia, non è mai stato un linguaggio di basso livello in assenza di garanzie fornite dall'implementazione che vanno al di là dei mandati dello Standard.