La respuesta a su pregunta depende de qué lenguaje C se está preguntando.
El lenguaje descrito en el Manual de referencia C de Dennis Ritchie de 1974 era un lenguaje de bajo nivel que ofrecía algo de la conveniencia de programación de los lenguajes de alto nivel. Los dialectos derivados de ese lenguaje también tendían a ser lenguajes de programación de bajo nivel.
Sin embargo, cuando se publicó el Estándar C 1989/1990, no describía el lenguaje de bajo nivel que se había hecho popular para programar máquinas reales, sino que describía un lenguaje de nivel superior que podía ser, pero no era obligatorio para ser implementado en términos de nivel inferior.
Como señalan los autores del Estándar C, una de las cosas que hizo el lenguaje útil fue que muchas implementaciones podrían tratarse como ensambladores de alto nivel. Debido a que C también se usó como alternativa a otros lenguajes de alto nivel, y como muchas aplicaciones no requerían la capacidad de hacer cosas que los lenguajes de alto nivel no podían hacer, los autores del Estándar permitieron que las implementaciones se comportaran de manera arbitraria Si los programas intentaron usar construcciones de bajo nivel. En consecuencia, el lenguaje descrito por el estándar C nunca ha sido un lenguaje de programación de bajo nivel.
Para comprender esta distinción, considere cómo Ritchie's Language y C89 verían el fragmento de código:
struct foo { int x,y; float z; } *p;
...
p[3].y+=1;
en una plataforma donde "char" es de 8 bits, "int" es de 16 bits big-endian,
"float" es de 32 bits, y las estructuras no tienen ningún relleno especial o alineación
requisitos para que el tamaño de "struct foo" sea de 8 bytes.
En el lenguaje de Ritchie, el comportamiento de la última declaración tomaría
la dirección almacenada en "p", agregue 3 * 8 + 2 [es decir, 26] bytes a ella, y buscar
un valor de 16 bits de los bytes en esa dirección y la siguiente, agregue uno
a ese valor, y luego vuelva a escribir ese valor de 16 bits a los mismos dos
bytes El comportamiento se definiría como actuar en los días 26 y 27.
bytes que siguen a la dirección p sin tener en cuenta el tipo de
el objeto fue almacenado allí.
En el idioma definido por el Estándar C, en el caso de que * p identifique
un elemento de "struct foo []" al que le siguen al menos tres más
elementos completos de ese tipo, la última declaración agregaría uno a miembro
y del tercer elemento después de * p. El comportamiento no sería definido por el
Estándar bajo cualquier otra circunstancia.
El lenguaje de Ritchie era un lenguaje de programación de bajo nivel porque, mientras que
Permitió a un programador utilizar abstracciones como matrices y estructuras cuando
conveniente, definió el comportamiento en términos del diseño subyacente de
objetos en la memoria. Por el contrario, el lenguaje descrito por C89 y posteriores.
estándares define las cosas en términos de una abstracción de nivel superior, y sólo
define el comportamiento del código que es consistente con eso. Las implementaciones de calidad adecuadas para la programación de bajo nivel se comportarán de manera útil en más casos de los exigidos por la Norma, pero no hay un documento "oficial" que especifique qué debe hacer una implementación para ser adecuada para tales fines.
El lenguaje C inventado por Dennis Ritchie es, por lo tanto, un lenguaje de bajo nivel, y fue reconocido como tal. El lenguaje inventado por el Comité de Normas C, sin embargo, nunca ha sido un lenguaje de bajo nivel en ausencia de garantías proporcionadas por la implementación que van más allá de los mandatos de la Norma.