Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!utgpu!water!watmath!clyde!cbosgd!ihnp4!ptsfa!hoptoad!academ!killer!jfh
From: jfh@killer.UUCP
Newsgroups: comp.lang.c
Subject: Re: Writing readable code
Message-ID: <1102@killer.UUCP>
Date: Mon, 6-Jul-87 14:09:36 EDT
Article-I.D.: killer.1102
Posted: Mon Jul  6 14:09:36 1987
Date-Received: Sat, 11-Jul-87 16:44:14 EDT
References: <1158@copper.TEK.COM> <6858@auspyr.UUCP> <17171@cca.CCA.COM> <221@amanue.UUCP>
Organization: The Unix(tm) Connection, Dallas, Texas
Lines: 143
Summary: Some code is very common (at least where I live) ...

In article <221@amanue.UUCP>, jr@amanue.UUCP (Jim Rosenberg) writes:
> In article <13008@topaz.rutgers.edu>, ron@topaz.rutgers.edu (Ron Natalie)
> writes:
> >  [... other pet peeves ... ]
> >
> > 2.  Needless use of the comma operator and parenthesis to demonstrate
> >     manhood to the obliteration of code readability, e.g.
> > 
> > 	    if((fd=open("foo",1)<0)
> > 
> >     SCREW this, too difficult, how about writing the code to indicate
> >     what is going on:
> > 
> > 	    fd = open("foo", 1);
> > 	    if(fd == -1)
> 
> I recall many moons ago whilst browsing K&R and before really learning C that I
> swore up and down that I would forego such (what I thought at the time to be)
> unwarranted over-abbreviation as:
> 
> 	while ((c = getchar()) != EOF) {
> 
> It didn't take me long once I was actually using C on a regular basis to
> realize that forgoing constructs such as the one above is not only ridiculous,
> a case can be made that it is actually LESS CLEAR stylistically.  Yes, it is
> more difficult to read for novices to C.  But consider the alternative:
> 
> 	for (;;) {
> 		c = getchar();
> 		if (c == EOF)
> 			break;
> 
> I would argue that a strong case can be made that the for loop is actually
> LESS CLEAR than the while loop.  By announcing the for loop as a forever loop,

I guess this is why some programmers make more than others.  I find the
open() example to be perfectly readable.  The while() example is also
my preferred method.  Some of the other code I preferred to use:

	struct something {
		int	whoknows;
		char	whatever;
	};

	func (member)
	struct	something	*member;
	{
		if (member && member->whatever == ILLEGALVALUE)

relying on the left-to-right order of && evaluation.  I hated PASCAL in
college because the above had to be coded

		if member <> nil then
			if member^.whatever = ILLEGALVALUE then
				. . . .

Also, I like to perform if-then-else's in expressions some times so
the value inside a block of code is more uniform -

	if (isupper (c) || (islower (c) && (c = toupper (c)))) { ...

This is a lousy example for me since I almost never use ctype ... but
you get the point.  Inside the then part 'c' is an UPPER case letter -
if there is an else-part then c is not a letter of any case.

My favorite this year is

	switch (fork ()) {
		case -1:	/* fork() failed */
			perror ("some message");
			break;
		case 0:		/* in child process */
			exec ("my_prog", 0);
			perror ("some other message");
			exit (-1);
		default:	/* in parent process */
			who_knows_what ();
	}

I got tired of declaring int fork(); on one line, int child on another and
then having to write all of those stupid if-then-else's.  This example seems
to show the _exact_ nature of the fork() system call.  My big beef with
the for(;;) example the poster gave is that it is a perfect example of a
middle tested loop - and in this case the code to make it a top tested
loop is very simple.  If you want to use a for() loop, at least use a
good one:

	for (c = getchar ();c != EOF;c = getchar ()) { ...

I don't care for this much because of the two getchar calls on the same
line - looks wasteful. And besides,

	while ((c = getchar ()) != EOF) { ...

has the exact same behavior and may well optimize to the exact same code.
(It can be optimized to exactly the same code as the for() loop).  But
then I never assume anything about my optimizer.

I ask the group, thich do you prefer?

	if (access (file, 0)) {
		fd = open (file, 0);
		if (fd >= 0)
			good_stuff ();
		else
			error_handler ();
	} else
		error_handler ();

	- or -

	if (access (file, 0) && (fd = open (file, 0)) >= 0)
		good_stuff ()
	else
		error_handler ();

When I see the first example, I read it in English as

	If I can access the file, then
		get a file descriptor for the file from open ();
		if the file descriptor is legal, then
			do good_stuff();
		else
			do error_handler();
	else
		do error_handler();

and by the time I am down to the second call to error_handler I have
forgotten why the test failed in the first place.

The second example I read as

	If I can access the file and get a legal file descriptor from open, then
		do good_stuff();
	else
		do error_handler();

I might even be inclined to throw in a || creat (file, 0666) someplace in there
just in case I might want to have the file no matter what.

This to me is consice code.  Anything more is a waste of my time.

- John.