Boolean Operators Slighted in C
Steven Brian McKechnie Sargent
sbs at valid.UUCP
Thu May 8 21:39:00 AEST 1986
This group has lately been besieged with a variety of odd C features,
namely
* boolean operators and data types
* builtin functions
* New operators like "< ="
* Arguments passed in registers
All these have the same effect of reducing simplicity for doubtful gain.
**
A Boolean data type distinct from integers clutters the language with
another (moderately useless) type and removes handy idioms like
foo[x == 0] = bar;
(Vide the "enum" type, which doesn't participate in address arithemtic
either.)
Arguments that boolean functions make for more efficient implementations
are pretty puny:
> o A function returning a bool could be implemented on some machines by
> setting the condition codes instead of storing a full integer value in
> the return register.
If the caller wants to use the result of the function as a value, rather
than as input to a branching decision, then it must write branches anyway
to set the value appropriately. Result: bigger slower code.
> >o Multiple flag variables with local scope and no address operator (e.g.
> > variables declared "register bool") could be packed into a single word.
Last I checked, (flags&BOOLEAN_VALUE_OF_MERIT) did what you want. Packing
and unpacking don't come for free, in space or time.
**
Builtin functions confuse language implementation with programming
environment implementation; C lives in enough different environments
that this is just plain bad planning. "Inline functions," a la those
of C++, are another matter entirely (although they have been mentioned
repeatedly in discussions of builtin functions). The benefits that
inline functions offer over CPP macros are: simpler cleaner syntax,
more flexibility because they admit arbitrary control structure within,
and avoidance of horrible botches like putchar(*p++). The disadvantage,
at least in my mind, is uncertainty as to what happens to the free
variables of an inline function. Are they bound lexically where the
inline is declared, or are they bound at the point where the inline is
used, assuming the nearest enclosing scope? Consider:
int oddval; // Not odd, as in = 1 (mod 2), but "odd"
inline max(x, y) {
++oddval;
return (x > y)? x: y;
}
foo(oddval) {
int n = max(oddval, 0);
oddval = max(4, oddval);
return max(oddval, oddval);
}
bar(x) {
...
return max(x, 4);
}
The question that leaps to mind is "what the hell does this code hope
to achieve?" You can of course write just as silly code using the C
preprocessor; but then the question becomes: Why provide redundant ways
to write this unsafely?
**
New operators like "< =", in addition to being hard to spell and understand,
facilitate usages that just aren't that common. I hardly ever want to
say x = (x < y), and I don't mind typing the extra characters. I'm not
interested in paying compiler vendors money for supporting this (or any
other dubious misfeature pounded into the language for "completeness'
sake.")
**
There's also been a discussion about arguments passed in registers,
with one fellow (sorry I forget the name) noting that "microcomputer
people pass arguments in registers." Well, big computer people do
the same thing, and in both cases the disease is one of not thinking
through to the consequences of your actions. Suppose you pass arguments
in registers. Well, if you're like me, you have a lot of variables in
registers. So in order to pass your arguments, you have to save those
registers somewhere. Where do you do it? Put them on the stack, of
course. Well, suppose that you "know" that %3 and %4 are reserved for
parameter passing: then those registers are unavailable to the caller,
whose code gets correspondingly bigger and slower. Well, suppose you're
keeping the arguments in %3 and %4, and the callee can use them "in
place" because caller and callee have agreed to do that (in order to
avoid the overhead of explicit parameter moving). Now, you're writing
spaghetti code, full of "special" communication among routines, with
all the attendant side effects and untraceable bugs. And why for?
The speed gain from non-standard calling sequences seems marginal at
best: one recent posting discussed at great length the most efficient
way to present arguments to the UNIX open(2) call -- which executes
hundreds or thousands of instructions inside namei, involving perhaps
multiple waits for the disk in order to fetch the inode for the file!
Put the arguments on a linked list of bit-wide elements and force the
kernel to chase them down, if you want: it won't make much of a
difference in how fast your programs run.
**
It's lots of fun to talk about wonderful "features" we'd like to see,
but somebody (ANSI?) might be listening and do something about it. So,
for my $.02 worth:
* Always strive to separate language from libraries, libraries
from host environment, and host environment from development
environment. These are all separate entities; make their
interactions as clean as possible.
* Keep implementations simple. This means: eschew oddball
calling sequences that are hard for debuggers to follow.
This means: minimize the number of compile-time options --
the user needs to determine the meaning of his program from
the source code; don't confuse the process by throwing in
options that invisibly change this meaning.
* Be EXTREMELY conservative about adding features. I have yet
to see a feature proposed that fixes a crippling problem
of the language. Quite the opposite: many proposed features,
such as unary + to denote evaluation ordering, help a min-
ority of users while making the language more baroque for
everybody.
I do go on, don't I? Oh, well; enjoy, flame back, or whatever.
Blessed be,
S.
More information about the Comp.lang.c
mailing list