Have you wondered why some of the features of C language are unintuitive? As we’ll see in this article, there are historical reasons for many of C’s features.
1. Can you guess why there is no distinct format specifier for ‘double’ in the printf/scanf format string, although it is one of the four basic data types? (Remember we use %lf for printing the double value in printf/scanf; %d is for integers).
In older versions of C, there was no ‘double’—it was just ‘long float’ type—and that is the reason why it has the format specifier ‘%lf ’ (‘%d ’ was already in use to indicate signed decimal values). Later, double type was added to indicate that the floating point type might be of ‘double precision’ (IEEE format, 64-bit value). So a format specifier for long float and double was kept the same.
2. Why is some of the precedence of operators in C wrong? For example, equality operators (==, != etc) have higher precedence than logical operators (&&, ||).
The confusion in the precedence of the logical and equality operators is the source of numerous bugs in C. For example, in (a && b == c && d), == has higher precedence than &&. So it is interpreted as, ( (a && (b == c) && d), which is not intuitive.
There is a historical background for this wrong operator precedence. To quote from Dennis M. Ritchie, ‘Operator precedence’ post (in net.lang.c, 1982): “Early C had no separate operators for & and && or | and ||. Instead it used the notion (inherited from B and BCPL) of ‘truth-value context’: where a Boolean value was expected, after ‘if ’ and ‘while’ and so forth; the & and | operators were interpreted as && and || are now; in ordinary expressions, the bit-wise interpretations were used. It worked out pretty well, but was hard to explain. (There was the notion of ‘top-level operators’ in a truth-value context.) The precedence of & and | were as they are now. Primarily at the urging of Alan Snyder, the && and || operators were added. This successfully separated the concepts of bit-wise operations and short-circuit Boolean evaluation. However, I had cold feet about the precedence problems. For example, there were lots of programs with things like: if (a==b & c==d) … In retrospect it would have been better to go ahead and change the precedence of & to higher than ==, but it seemed safer just to split & and && without moving & past an existing operator.”
3. In the original C library, math.h> has all operations done in double precision, i.e., long float or double (and not single precision, i.e., float). Why?
Since C was originally designed for writing UNIX (system programming), the nature of its application reduced the necessity for floating point operations. Moreover, in the hardware of the original and initial implementations of C (PDP-11) floating point arithmetic was done in double precision (long float or double type) only. Writing library functions seemed to be easy if only one type was handled. For these reasons, the library functions involving mathematics (math.h>) were done for double types, and all the floating point calculations were promoted and were done in double precision only. For the same reason, when we use a floating point literal, such as 10.0, it is treated as double precision and not single precision.
4. Why is the output file of the C compiler called a.out?
The a.out stands for ‘assembler.output’ file [cm.bell-labs.com/who/dmr/chist.html]. The original UNIX was written using an assembler for the PDP-7 machine. The output of the assembler was a fixed file name, which was a.out to indicate that it was the output file from the assembler. It is not mandatory for assembly to be done in modern compilers; instead, linking and loading of object files is done. This tradition continues and the output of cc is by default a.out!