[comp.lang.c] `if

chris@mimsy.UUCP (Chris Torek) (12/11/88)

In article <846@starfish.Convergent.COM> jerry@starfish.Convergent.COM
(Gerald Hawkins) writes:
>Is it ever ok to use the form:
[slightly edited]
>	if (a = b * 2 + 39)	/* intentional assignment within condition */
>		...
>INSTEAD OF:
>	a = b * 2 + 39			/* more normal */
>	if (a)
>		...
>I say "no!"  The code is unsupportable.  EVERYONE who ever reads it will
>assume it is a trivial error (and perhaps try to correct it).

Not necessarily everyone, but it is certainly a dubious construct.
This is especially true since you can write

	if ((a = b * 2 + 39) != 0)
		...

and make it clear that you did not accidentally omit one `='.
I still prefer two separate statements, with one exception:

	/* typical sequence from Unibus drivers */
	if (unit >= NFOO)
		return (ENXIO);
	ui = fooinfo[unit];
	if (ui == NULL || !ui->ui_alive)
		return (ENXIO);

Here one would like to write:

	ui = fooinfo[unit];
	if (unit >= NFOO || ui == NULL || !ui->ui_alive)
		return (ENXIO);

but it is not safe to refer to (fooinfo[k] where k>=NFOO).
Hence:

	if (unit >= NFOO || (ui = fooinfo[unit]) == NULL || !ui->ui_alive)
		return (ENXIO);

This is rather borderline; if the contents of the `if' statement
were more complex I would be more solidly for it.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

frank@rsoft.UUCP (Frank I. Reiter) (12/12/88)

> Is  if(a = b + c/4)  (or something like this) legal ?

I do it all the time, but I do it like this :

|----
| if(a = b + c/4)   /* Note assignment */
|    whatever();
|----

Similarly I sometime code a switch like this :

|----
|  switch(somevar) {
|  case DELETE :
|       deletesomething();
|       /* Note fall-through */
|  case NEXT :
|       gotonextone();
|       break;
|  case ......
|----

-- 
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
Frank I. Reiter             \ /     UUCP:     {uunet,ubc-cs}!van-bc!rsoft!frank
Langley, British Columbia   / \      BBS:     Mind Link @ (604)533-2312
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

djones@megatest.UUCP (Dave Jones) (12/13/88)

There is a construct that is a little awkward using standard
(Pascal-like) structured statements:  You want to read a value
from an external source, exit the loop if the value is a sentinal,
or process the value and continue if not a sentinal.

{
  int ch;
  do
    { 
      ch = getchar();
      if(ch != EOF) process(ch);
    } 
  while (ch != EOF);
}


The expression !EOF gets evaluated twice.  A compiler that
does common subexpression-removal and global flow-analysis 
would fix things, but not all compilers are that smart.  
Besides, I'm one of those old fashioned guys who wants the
compiler to do what I say. Or rather, I would like to be able 
to say what I really want the compiler to do.

So we could do this:

for(;;)
  {
     int ch = getchar();
     if(ch == EOF) break;
     process(ch);
  }

This is nice, in as much as it moves the declaration of ch one
level deeper.  But it has the somewhat unstructured "break".
Still, not too bad.  I prefer the following:


{ int ch;
  while( (ch = getchar()) != EOF )
    process(ch);
}


This says it almost literally,  "While I get a ch that is not 
a sentinal, I want to continue processing."

badri@valhalla.ee.rochester.edu (Badri Lokanathan) (12/14/88)

In article <14945@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> I still prefer two separate statements, with one exception:
> (example follows.) 

I am missing something here. Why is

a = expr; if (a == b) {statement}

preferable, in general, to

if ((a = expr) == b) {statement}

I think the second form is more concise and often more readable,
as per K & R II ed., pp. 16-17.
-- 
"I care about my fellow man              {) badri@ee.rochester.edu
 Being taken for a ride,                //\\ {ames,cmcl2,columbia,cornell,
 I care that things start changing     ///\\\ garp,harvard,ll-xn,rutgers}!
 But there's no one on my side."-UB40   _||_   rochester!ur-valhalla!badri

ok@quintus.uucp (Richard A. O'Keefe) (12/14/88)

In article <1071@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>{ int ch;
>  while( (ch = getchar()) != EOF )
>    process(ch);
>}
>
>This says it almost literally,  "While I get a ch that is not 
>a sentinal, I want to continue processing."

I very much like embedded assignments, but that's a poor argument.
To exit a loop when you find a sentinel (that's two Es, no As), you can do
	/* C version */			-- ADA version
	for (;;) {			loop
	    ch = getchar();		    get(ch);
	    if (ch == EOF) break;	    exit when ch = sentinel;
	    process(ch);		    process(ch);
	}				end loop

I use the "while" version as an idiom for reading from a stream,
but it isn't as general a method as the use of 'break'.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/14/88)

In article <1683@valhalla.ee.rochester.edu> badri@valhalla.ee.rochester.edu (Badri Lokanathan) writes:
>I am missing something here. Why is
>a = expr; if (a == b) {statement}
>preferable, in general, to
>if ((a = expr) == b) {statement}

Writing a whole bunch of unrelated actions on the same line of text isn't
particularly readable no matter what the actions are.  Try

	a = expression;

	if ( a == b )
		statement

There are several principles of style that apply to coding.  One that is
relevant here is:
	Actions that are thought of as separate tasks
	should be separated visually.

If setting the value of `a' is conceptually only loosely related to
determining whether `a' now matches `b', then it is better to visually
separate these actions.  On the other hand, if they are best thought
of as closely coupled, then combining them is appropriate:

	for ( p = &qhead; (p = p->link) != &qhead; )
		/* operate on node *p */

Sometimes, as in this example, it's really a "judgement call".

	for ( p = qhead.link; p != &qhead; p = p->link )
		/* operate on node *p */

is just about as acceptable.  However, the following is poor:

	p = qhead.link;
	while ( p != &qhead )
	    {
		/* operate on node *p */
		p = p->link;
	    }

C's "for" statement was designed for bringing together visually
all the parts of loop control; use it for that.  (A recent posting
showed a "for" statement whose three parts were not all involved
in controlling the loop; that violates the stylistic principle I
gave above.)

I agree with the recommendation that programmers read "The Elements
of Programming Style" by Kernighan and Plauger.  There are other
books that give good stylistic advice, but that's the best starter.
Incidentally, this book doesn't use C in its examples, but the
principles are valid for most programming languages.

There isn't any way to automate good programming style, any more
than there is any good way to guarantee good program design.
Careful thought is essential.

chris@mimsy.UUCP (Chris Torek) (12/14/88)

>In article <14945@mimsy.UUCP> I wrote:
>>I still prefer ....

In article <1683@valhalla.ee.rochester.edu> badri@valhalla.ee.rochester.edu
(Badri Lokanathan) writes:
>I am missing something here. Why is
>
>a = expr; if (a == b) {statement}
>
>preferable, in general, to
>
>if ((a = expr) == b) {statement}

It is not.  That is why I said `I prefer' rather than `it is better'.
The one that I believe is objectively worse than its alternatives is

	if (l = r) ...

vs any of

0.	l = r;
	if (l) ...

1.	l = r;
	if (l != 0) ...

2.	if ((l = r) != 0) ...
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

ark@alice.UUCP (Andrew Koenig) (12/15/88)

In article <861@quintus.UUCP>, ok@quintus.uucp (Richard A. O'Keefe) writes:
 
> I very much like embedded assignments, but that's a poor argument.
> To exit a loop when you find a sentinel (that's two Es, no As), you can do
> 	/* C version */			-- ADA version
> 	for (;;) {			loop
> 	    ch = getchar();		    get(ch);
> 	    if (ch == EOF) break;	    exit when ch = sentinel;
> 	    process(ch);		    process(ch);
> 	}				end loop
> 
> I use the "while" version as an idiom for reading from a stream,
> but it isn't as general a method as the use of 'break'.

Once upon a time I proposed an extension to the C syntax
that would allow a loop to have its (only) exit in the middle,
rather than at the beginning or end.  In that syntax, the
example above would have looked like this:

	do	ch = getchar();
	while (ch != EOF)
		process(ch);

or, with braces:

	do {
		ch = getchar();
	} while (ch != EOF) {
		process(ch);
	}

You will see that this syntax sort of merges

	do S while (E);

and

	while (E) S

into

	do S while (E) S2

with the first two simply becoming degenerate forms of the third.

I couldn't sell anyone on the idea at the time.  It's way too
late now, of course.
-- 
				--Andrew Koenig
				  ark@europa.att.com

logan@vsedev.VSE.COM (James Logan III) (12/16/88)

In article <8536@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
# 
# Once upon a time I proposed an extension to the C syntax
# [ explanation of construct deleted ]
#
# 	do {
# 		ch = getchar();
# 	} while (ch != EOF) {
# 		process(ch);
# 	}
# 

It looks sort of strange, but when I read it I immediately
thought of a few recent situations where it would have been
handy.  

			-Jim
-- 
Jim Logan		logan@vsedev.vse.com
(703) 892-0002		uucp:	..!uunet!vsedev!logan
			inet:	logan%vsedev.vse.com@uunet.uu.net

evil@arcturus.UUCP (Wade Guthrie) (12/17/88)

The sound of a programmer trying to read a C program flinging himself
against a wall.

In article <8536@alice.UUCP>, ark@alice.UUCP (Andrew Koenig) writes:
> 	do	ch = getchar();
> 	while (ch != EOF)
> 		process(ch);
> or, with braces:
> 	do {
> 		ch = getchar();
> 	} while (ch != EOF) {
> 		process(ch);
> 	}

I really do not mean to flame this person, but the above constructions
are EXCEEDINGLY hard to read.  I much perfer the method which already
exists:

	while ( (c=getchar()) != EOF)
		process(c);

because it is easier for humans to parse (not to mention the fact that it
requires less space.


Wade Guthrie
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

peter@ficc.uu.net (Peter da Silva) (12/18/88)

In article <3049@arcturus>, evil@arcturus.UUCP (Wade Guthrie) writes:
> The sound of a programmer trying to read a C program flinging himself
> against a wall.

Is that like the sound of one hand clapping?

> In article <8536@alice.UUCP>, ark@alice.UUCP (Andrew Koenig) writes:
> > 	do	ch = getchar();
> > 	while (ch != EOF)
> > 		process(ch);
> > or, with braces:
> > 	do {
> > 		ch = getchar();
> > 	} while (ch != EOF) {
> > 		process(ch);
> > 	}

This is a wonderful and clean extension to 'C' that solves the loop problem
once and for all!

> I really do not mean to flame this person, but the above constructions
> are EXCEEDINGLY hard to read.  I much perfer the method which already
> exists:

Only if you're not used to it. have you ever done extensive bourne-shell
or Forth programming, where this construct is a common idiom?

> 	while ( (c=getchar()) != EOF)
> 		process(c);

How about (in K&R indenting style, for consistancy):

	do {
		Ask question.
		Get answer.
		Categorise answer.
	} while(answer != exit) {
		Stuff...
		switch(answer) {
		  ...
		}
		And more stuff...
	}

That is, what about the case where the first part of the loop is too complex
to fit in a while statement. Sure, you could put a break in, but isn't this
clearer? The existing while and do loops become special cases.
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.