La réponse à votre question dépend du langage C concerné.
Le langage décrit dans le manuel de référence C de 1974 de Dennis Ritchie était un langage de bas niveau offrant une certaine commodité de programmation par rapport aux langages de niveau supérieur. Les dialectes dérivés de cette langue ont également tendance à être des langages de programmation de bas niveau.
Cependant, lors de la publication de la norme 1989/1990 C, elle ne décrivait pas le langage de bas niveau devenu populaire pour la programmation de machines réelles, mais décrivait un langage de niveau supérieur qui pouvait être - mais n'était pas obligé - -implémenté en termes de niveau inférieur.
En tant qu'auteurs de la note C, l'un des éléments qui ont rendu le langage utile est que de nombreuses implémentations peuvent être traitées comme des assembleurs de haut niveau. Parce que C était également utilisé comme alternative à d'autres langages de haut niveau et que de nombreuses applications n'exigeaient pas la capacité de faire des choses que les langages de haut niveau ne pouvaient pas faire, les auteurs de la norme ont permis aux implémentations de se comporter de manière arbitraire. si les programmes ont essayé d'utiliser des constructions de bas niveau. Par conséquent, le langage décrit par le standard C n'a jamais été un langage de programmation bas niveau.
Pour comprendre cette distinction, considérons comment Ritchie's Language et C89 afficheraient l'extrait de code:
struct foo { int x,y; float z; } *p;
...
p[3].y+=1;
sur une plate-forme où "char" correspond à 8 bits, "int" à 16 bits big-endian, "float" à 32 bits et les structures n'ayant pas d'exigences de remplissage ou d'alignement particulières, la taille de "struct foo" est de 8 octets.
Dans la langue de Ritchie, le comportement de la dernière instruction prend l'adresse stockée dans "p", lui ajoute 3 * 8 + 2 [ie 26] octets, et extrait une valeur de 16 bits des octets de cette adresse et de la suivante. , ajoutez un à cette valeur, puis écrivez cette valeur de 16 bits sur les deux mêmes octets. Le comportement serait défini comme agissant sur les 26ème et 27ème octets suivant celui de l'adresse p sans tenir compte du type d'objet qui y était stocké.
Dans le langage défini par le standard C, dans le cas où * p identifie un élément d'un "struct foo []" suivi d'au moins trois éléments plus complets de ce type, la dernière instruction ajoute un membre à y le troisième élément après * p. Le comportement ne serait défini par la norme dans aucune autre circonstance.
Le langage de Ritchie était un langage de programmation de bas niveau car, s'il permettait au programmeur d'utiliser des abstractions telles que des tableaux et des structures lorsque cela convenait, il définissait le comportement en termes de disposition sous-jacente des objets en mémoire. En revanche, le langage décrit par C89 et les normes ultérieures définit les choses en termes d'abstraction de niveau supérieur et ne définit que le comportement du code qui est cohérent avec cela. Une mise en œuvre de qualité adaptée à la programmation de bas niveau se comportera de manière utile dans plus de cas que prévu par la norme, mais il n'existe aucun document "officiel" spécifiant ce qu'une mise en œuvre doit faire pour être adaptée à ces objectifs.
Le langage C inventé par Dennis Ritchie est donc un langage de bas niveau et a été reconnu comme tel. Cependant, le langage inventé par le Comité de normalisation C n'a jamais été un langage de bas niveau en l'absence de garanties fournies par la mise en œuvre qui vont au-delà des mandats de la norme.