On Tue, Mar 11, 2025 at 10:22 PM Douglas McIlroy
<douglas.mcilroy(a)dartmouth.edu> wrote:
Beating a nearly dead horse, I'm shipping this to
COFF instead of TUHS
to which it responds.
I find
if (a == b && c == d)
perfectly reasonable, but
if ((a == b) && (c == d))
to be just silly.
Amusing. IT's odd that no one has mentioned the use of spaces for
grouping. When the operands of == are simple, I prefer to vary the
spacingy spacing. It's less intrusive than extra parentheses and just
as clear.
if(a==b && c==d) or
if( a==b && c==d )
I definitely agree that judicious use of spacing can aid
comprehension, though it can only be a hint to the programmer, since
it's not semantically meaningful to the compiler. I would fear
something like this, given a, b, and c bool:
if (b||c && a) /* ?if (b or c) and a? */
Surely experience and good taste can find a happy medium here.
K&R usually flank every infix operator with
spaces, unlike classical
mathematical usage, where spacing reflects operator precedence. I
usually write a*b + c*d, not a * b + c * d, which wantonly flattens
the parse tree. Here's an example from K&R, where uniform spacing
hampers intelligibility.
for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
n = 10 * n + (s[i] - '0');
The "extra" parens in the second line admirably show the intent of the
subtraction. Other groupings are not so well indicated. I would write
it like this:
for(i=0; s[i]>='0' && s[i]<='9'; ++i)
n = 10*n + (s[i]-'0');
(I'll admit that the juxtaposition ]>= is less than perspicuous.)
I personally like this elements of this style, but I have grown fond this:
for (i = 0; '0' <= s[i] && s[i] <= '9'; ++i)
n = 10*n + (s[i] - '0');
Which makes the range comparison obvious and mimics what I'd expect to
see in a written formula using standard mathematical notation. Here,
factors are clustered together without space, while terms are
separated by spaces; again closer to the usual conventions for typeset
mathematical formulae.
Long identifiers constitute a level of grouping that
takes precedence
even over multiplication. So I may flank long identifiers with spaces.
I suppose then + in a sum of products might deserve double spaces.
That's probably a bridge too far, but double spaces after the
semicolons in the "for" statement above seem justifiable
K&R aren't absolutely rigid about spacing. In the following oddly
mixed specimen, the first of three operands in a conjunction is spaced
tightly, but the third is not; I guess they feel about != the way I do
about long identifiers.
i<lim-1 && (c = getchar()) != '\n' && c != EOF
The middle conjunct is a challenge. I'd probably squeeze out the
spaces. But I might instead try to hold it together with outer parens.
Here, I think a single expression is trying to do too much. Assuming
this extracted from a loop, one might write this as:
while ((c = getchar()) != EOF) {
if (i < lim - 1 && c != '\n')
/* do something with c and increment i */
}
Though I'm a fan of Dijkstra's guarded statements, which can be
simulated in C via conditions and break:
while ((c = getchar()) != EOF) {
if (c == '\n' || i >= lim)
break;
/* Do something with c and i. */
}
The question of indentation levels and characters for them comes up
frequently. Here, I use 4 space indent because gmail won't accept tab
literals, which I'm most accustomed to from Unix source code. Nowadays
it doesn't matter that much, but I imagine in the early PDP-11 days it
did, from a space saving perspective on small disks.
I was always struck by how John Lions's commentary formatted the
source code so that the first indentation level was 4 spaces, and
subsequent levels 8 spaces (I think. My copy isn't to hand just now).
I thought that was visually rather appealing.
- Dan C.