Odpowiedź na twoje pytanie zależy od tego, o jaki język C pyta.
Język opisany w Dennis Ritchie's 1974 C Reference Manual był językiem niskiego poziomu, który oferował niektóre z wygody programowania w językach wyższego poziomu. Dialekty wywodzące się z tego języka również były niskopoziomowymi językami programowania.
Kiedy opublikowano normę C 1989/1990 C, nie opisywała ona języka niskiego poziomu, który stał się popularny przy programowaniu rzeczywistych maszyn, ale opisała język wyższego poziomu, który mógłby być - ale nie był wymagany - -implementowane na niższych poziomach.
Jak zauważają autorzy C Standard, jedną z rzeczy, które sprawiły, że język był użyteczny, było to, że wiele implementacji można było traktować jako asemblery wysokiego poziomu. Ponieważ C był również używany jako alternatywa dla innych języków wysokiego poziomu i ponieważ wiele aplikacji nie wymagało zdolności do robienia rzeczy, których języki wysokiego poziomu nie byłyby w stanie zrobić, autorzy Standardu zezwolili implementacjom na zachowanie się w dowolny sposób jeśli programy próbowały używać konstrukcji niskiego poziomu. W związku z tym język opisany w standardzie C nigdy nie był językiem programowania niskiego poziomu.
Aby zrozumieć to rozróżnienie, zastanów się, w jaki sposób Ritchie's Language i C89 mogłyby zobaczyć fragment kodu:
struct foo { int x,y; float z; } *p;
...
p[3].y+=1;
na platformie, gdzie „char” to 8 bitów, „int” to 16 bitów big-endian, „float” to 32 bity, a struktury nie mają specjalnych wymagań dotyczących dopełnienia lub wyrównania, więc rozmiar „struct foo” wynosi 8 bajtów.
W języku Ritchie zachowanie ostatniej instrukcji pobierałoby adres zapisany w „p”, dodawało do niego 3 * 8 + 2 [tj. 26] bajtów i pobierało 16-bitową wartość z bajtów pod tym adresem i następnym , dodaj jedną do tej wartości, a następnie zapisz tę 16-bitową wartość do tych samych dwóch bajtów. Zachowanie byłoby zdefiniowane jako działanie na 26. i 27. bajcie następującym po adresie pod adresem p, bez względu na rodzaj przechowywanego tam obiektu.
W języku zdefiniowanym w standardzie C, w przypadku, gdy * p identyfikuje element „struct foo []”, po którym następują co najmniej trzy bardziej kompletne elementy tego typu, ostatnia instrukcja doda jeden element do elementu y trzeci element po * p. Zachowanie nie byłoby zdefiniowane przez Standard w żadnych innych okolicznościach.
Język Ritchie był językiem programowania niskiego poziomu, ponieważ chociaż pozwalał programiście korzystać z abstrakcji, takich jak tablice i struktury, gdy było to wygodne, definiował zachowanie pod względem podstawowego układu obiektów w pamięci. Natomiast język opisany w C89 i późniejszych standardach definiuje rzeczy w kategoriach abstrakcji wyższego poziomu i definiuje tylko zachowanie kodu, które jest z tym zgodne. Wdrożenia wysokiej jakości odpowiednie dla programowania niskiego poziomu będą zachowywały się użytecznie w większej liczbie przypadków niż wymaga tego norma, ale nie ma „oficjalnego” dokumentu określającego, co wdrożenie musi zrobić, aby było odpowiednie do takich celów.
Język C wymyślony przez Dennisa Ritchiego jest zatem językiem niskiego poziomu i został uznany za taki. Język wymyślony przez Komitet ds. Norm C nigdy jednak nie był językiem niskiego poziomu przy braku gwarancji zapewniających wdrożenie, które wykraczają poza mandaty Standardu.