Why didn't C have a boolean data type prior to C99?
If you spend a little time in the library, you don't have to speculate. Here are some statements taken from Dennis Ritchie's paper on the evolution of C. The context is that Dennis is building on Ken Thompson's language B, which was implemented on the very tiny PDP-7, a word-addressed machine. Because of growing interest, the group got one of the very first PDP-11s. Dennis writes,
The advent of the PDP-11 exposed several inadequacies of B's semantic model. First, its character-handling mechanisms, inherited with few changes from BCPL, were clumsy: using library procedures to spread packed strings into individual cells and then repack, or to access and replace individual characters, began to feel awkward, even silly, on a byte-oriented machine.
The B and BCPL model implied overhead in dealing with pointers: the language rules, by defining a pointer as an index in an array of words, forced pointers to be represented as word indices. Each pointer reference generated a run-time scale conversion from the pointer to the byte address expected by the hardware.
For all these reasons, it seemed that a typing scheme was necessary to cope with characters and byte addressing, and to prepare for the coming floating-point hardware. Other issues, particularly type safety and interface checking, did not seem as important then as they became later.
(Emphasis mine.)
The paper goes on to describe Dennis's struggles to invent a new pointer semantics, to make arrays work, and to come to terms with this newfangled struct
idea. Notions of type safety and distinguishing Booleans from integers did not seem important until much later :-)
C is actually little more than a higher-level assembly language. Yes, it got control structures and whatnot and it even got types which assembler certainly doesn't need.
But the language was designed decades ago. And since every boolean result gets down to individual bits in the processor's status word it obviously was sufficient to just using an integral data type for it. And it made the compiler probably a little less complex since you can omit some type checking (in later languages control structures need a boolean value, in C they just need an integral value of either 0 or something else).
It was common (and still is in some cases) to treat zero as false and any non-zero as true. This has advantages for shorthand: for example, instead of while (remaining != 0)
you can just use while (remaining)
.
Some languages standardised on true being -1. The reason for this is that in twos-complement notation (which most computers use to represent negative numbers), the bitwise-not of 0 is -1 (in 8-bit binary , 11111111
is decimal -1).
Over time it was realised that using a compiler-defined constant would prevent a lot of potential confusion. It's been a while since I've done C++, but I'm fairly sure any non-zero value will still evaluate "true".