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.