Ansel said:
High-level language: any language above assembly languages.
Low-level language: assembly languages and below.
Why? Because the low-level languages are CPU-ish: the elements of
programming are instructions or even lower, op codes. High-level languages
abstract that away, so there is the division line, at least traditionally
(as I learned it anyway and still ascribe to that because it makes good
sense).
A low-level language would be a specific assembler, a machine oriented
language, or a 'high-level' assembler, which latter might look, deceptively,
like high-level, but they are cruder in their capabilities.
But as a high-level language, C is near the bottom of the range. I would
classify it more from it's data handling:
o Static data types always known at compile-time
o Primitive types based around likely machine types
o Collections (arrays, structs) of types which are always of fixed bounds
known at compile-time
o Always explicit memory management
o Always explicit pointer handling (although some constructions look
implicit)
C, arguably, has dynamic (and perhaps flexible) arrays, but handled
explicitly, and things such as VLAs, which are interesting but not enough to
raise it to the next tier! (Pass a VLA to a function, and you still just
have a bare pointer with no idea what length it is.)
For many people, this is enough: if using C to implement a higher-level
language for example, you don't want C's own capabilities to get in the way.
Syntax is less important: more higher-level syntax can be added to C, but if
it's data-handling is the same, then it's still really at the same level
(but, it wouldn't hurt either...).