Recall that a programming language is a specification, usually written in English -with the ambiguity of natural language- in some technical report. A programming language is not the same as its implementation in some compiler or interpreter software.
A programming language (specification) is not only or mostly about syntax (which might be formalized with EBNF), but more about semantics. The semantics can be somehow formalized, e.g. with denotational semantics.
I cannot reasonably expect from my C compiler to accept all syntactically or semantically valid programs. I'm sure no compiler & computer could process a C file containing some function with a billion of C statements, deeply nested and combined in a weird control graph. Actually, I do enable optimizations by compiling with gcc -O2
, and I empirically observed that compilation time is proportional to the square of the length of the longest function. Hence, when I generate C code -I love metaprogramming-, I need to be careful to avoid generating C functions with more than a dozen of thousands of statements. AFAIK the standard or my compiler documentation is not stating that limitation.
Sadly, the JLS does not even try to partly formalize some of the semantics of the language.
Some programming languages have some partial formalizations of their semantics. A good example of that is the R5RS for Scheme, it has partly formalized some of the semantical aspects of Scheme (but leaving some well deserved freedom to implementors, like order of evaluation of arguments in function call, is challenging to formalize).
But programming languages have also some pragmatics. Read Scott's book on programming language pragmatics, it is enlightning.
A good example of pragmatics is the malloc standard function of C11. Its n1570 (draft) standard says (in §7.22.3, notably §7.22.3.5):
If the space cannot be allocated, a null pointer is returned.
...
void *malloc(size_t size);
...
The malloc
function allocates space for an object whose size is specified by size
and whose value is indeterminate.
...
The malloc
function returns either a null pointer or a pointer to the allocated space.
...
My understanding of the C11 standard is that the following function is a valid implementation of malloc
(conforming to the letter of the C11 standard, and probably some POSIX one).
void* malloc(size_t sz) { errno = ENOMEM; return NULL; }
(of course that implementation is against the spirit of C; I claim that it follows the letter of the standard)
but no sane C developer is expecting such an implementation. A C programmer is expecting malloc
to usually succeed. What that precisely means is unclear.
Now, can we ever achieve a fully complete specification of anything ?
I believe that no. A real computer is different of the abstraction we are programming. Take my Linux laptop for example. If I drop it from my desk, if it battery explodes, or if someone fires a gun bullet into it, it is not behaving as the abstraction I am programming. And a fully complete specification would require a complete description of the environment (that is, the physical universe we are in), which is not possible.
Some quantum scientists claim that the universe has a finite but very huge number of states. If that is "true", no complete description of it can exist in a subpart, e.g. on Earth.
Computer programming is always about abstractions. Read also about undefined behavior (in particular C.Lattner's blog entries about it) and about unspecified behavior. Notice that the C standard is purposely (and for good reasons) mentioning in many places undefined behavior. Try hard to avoid it.