C is used for low-level programming close to hardware, an area in which you sometimes need to shift between bitwise and logical operations, on the same data. Being required to convert a numeric expression to boolean just to perform a test would clutter up the code.
YOu can write things like:
if (modemctrl & MCTRL_CD) {
/* carrier detect is on */
}
rather than
if ((modemctrl & MCTRL_CD) != 0) {
/* carrier detect is on */
}
In one isolated example it's not so bad, but having to do that will get irksome.
Likewise, converse operations. It's useful for the result of a boolean operation, like a comparison, to just produce a 0 or 1: Suppose we want to set the third bit of some word based on whether modemctrl has the carrier detect bit:
flags |= ((modemctrl & MCTRL_CD) != 0) << 2;
Here we have to have the != 0, to reduce the result of the biwise & expression to 0 or 1, but because the result is just an integer, we are spared from having to add some annoying cast to further convert boolean to integer.
Even though modern C now has a bool type, it still preserves the validity of code like this, both because it's a good thing, and because of the massive breakage with backward compatibility that would be caused otherwise.
Another exmaple where C is slick: testing two boolean conditions as a four way switch:
switch (foo << 1 | bar) { /* foo and bar booleans are 0 or 1 */
case 0: /* !foo && !bar */
break;
case 1: /* !foo && bar */
break;
case 2: /* foo && !bar */
break;
case 3: /* foo && bar */
break;
}
You could not take this away from the C programmer without a fight!
Lastly, C sometimes serves as a kind of high level assembly language. In assembly languages, we also do not have boolean types. A boolean value is just a bit or a zero versus nonzero value in a memory location or register. An integer zero, boolean zero and the address zero are all tested the same way in assembly language instruction sets (and perhaps even floating point zero). Resemblance between C and assembly language is useful, for instance when C is used as the target language for compiling another language (even one which has strongly typed booleans!)