Have you wondered why some of the features of C language are unintuitive? As we’ll see in this column, there are historical reasons for many of C’s features.
- Can you guess why there is no distinct format specifier for ‘double’ in the
scanfformat string, although it is one of the four basic data types? (Remember we use
%lffor printing the double value in
%dis for integers).
- Why is some of the precedence of operators in C wrong? For example, equality operators (
!=, etc) have higher precedence than logical operators (
- 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?
- Why is the output file of the C compiler called
- 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 ‘
%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.
- 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. Here is the explanation given by Dennis Ritchie: “Early C had no separate operators for
||. 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
|operators were interpreted as
||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
|were as they are now. Primarily at the urging of Alan Snyder, the
||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
&past an existing operator.”
- 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.
a.outstands for ‘assembler.output’ file. 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.outto indicate that it was the output file from the assembler. No assembly needs to be done in modern compilers; instead, linking and loading of object files is done. However, this tradition continues and the output of
ccis by default
With this month, JoP is successfully entering its third year. Thanks for all your continuous feedback and support! Keep filling my mailbox as usual and I’ll be more than happy to help you. Wishing you a happy new year!!
- Dennis M. Ritchie, “Operator precedence”, net.lang.c, 1982
Featured image courtesy: Bill Bradford. Reused under terms of CC-BY 2.0 License.