C has a reputation for being a "hacker" language, in contrast to say Haskell, which is abstract with ties to logic and category theory. There is even the quip "C is a portable assembler".

However, C depends on many advances in computer science, notably lexical scoping. Indeed, C has converged to intuitionistic logic: multiple arguments and one return value, function invocation that enters and exits from the same place.

Compilers use a stack to store local variables since lexical scoping means they fall in/out of scope in a first-in, first-out manner. There is nothing in assembly preventing you from using multiple stacks or swapping stacks, despite programmers referring to "the" stack. One must take this as C's dedication to lexical scoping.

When a function returns, it returns to its caller. This is not necessary in assembly and in fact prior to structured programming procedures could have multiple return points—the return keyword was far from inevitable!

C makes a great effort to implement function calls; processor jumps (which take an address in RAM) are quite far from named invocations. Procedures are stored in special formats (e.g. ELF), and then filled in when loaded into memory. The intricacy of modern linkers and enmeshment in the operating system is necessary so that programming is amenable to humans but in any case it is quite far from assembly/machine code.


Sometimes one calls procedures for effects (modifying an array behind a pointer). However, as I have written, the way control flow works in intuitionistic logic happens to be suitable for this style of programming: it goes strictly forward.