C PROGRAMMING

Off and Running

Al Stevens

When the folks at DDJ called me about doing this column, they offered me the chance to repay a long overdue debt. As a loyal DDS reader since its earliest days, I have received much knowledge, advice, and good code from its pages. This column then is the first installment in what could become a long period of repayment. Please, now, indulge me in a bit of introduction.

I started programming 30 years ago when real pilots flew taildraggers, real beer cans were made of steel, and real computers had drum memory and vacuum-tube logic circuits. This statement probably says more about my age than my qualifications because little of what I learned during those early years applies in today's software-development profession. Programming wasn't considered a profession or even a career then, and no one worried much about whether programming was an art or a science. It was a job. There were no university courses, no structure or discipline, and no magazines. Nobody published or shared source code. MAD magazine was six years old, Playboy was five, and DDS was not yet a gleam. As my old pal Bill Chaney says, "Times was tough, Buddy." Times are better now, Bill.

My brother Fred introduced me to DDS when its title was much longer and more whimsical. Fred built one of the early Altair 8080 computers in about 1976. He built it to use in developing microprocessor based systems. Fred and I weren't hobbyists or hackers in the usual sense--our needs were more mundane, and although we had no association with hackers, we didn't lack the hacker's enthusiasm. Our combined consulting activities--his in hardware design and mine in software development--needed a test and development system. The Altair was just the ticket. It was fun to build and get running. DDS was a necessary adjunct to the lab in those days. Not much software was around, and we could always count on the good Doctor for a generous dosage of useful code. Fred has every issue of DDS.

Although Fred is a Forth practitioner now (I'm working on converting him) he got me into C. I was doing a lot of traveling, and one day he shoved a package at me on my way out the door-something to read on the plane, he said. It was the BDS C compiler for CP/M with a copy of Kernighan and Ritchie's The C Programming Language thrown in. I remember thinking that a language that only has a single letter for a name must not be much of a language. The names of real languages are healthy, technical acronyms like ALGOL, MACRO-11, and MUMPS. ("MUMPS" is healthy?) I was skeptical about a language with a wimpy name like "C." Nonetheless, I read K&R on the flight, and by the time I "deplaned," I was a C convert. Never had there been such elegance in a programming language. It seemed that all things programmers dislike in code syntaxes were masterfully corrected in the simple and powerful structure of C. Its loose typing created for a perfect match between C and systems programming. C looked like the ideal language. DDS readers don't need me to tell them that, but I like to say it, anyway.

At the first opportunity, I set about to hammer out my first C program, believing with Kernighan and Ritchie that you learn a programming language by writing programs. First, I needed a program to write. As it turned out, I already needed a personal program for one of my silly diversions, those cryptograms that the newspaper runs under the crossword puzzle. You've seen them. They encrypt a clever phrase with simple letter substitutions giving you one of the letters as a clue. You have to decrypt the phrase, usually revealing a groaner of a pun. The hardest part about a cryptogram is mustering the clerical effort to keep track of which letters you've used and where they line up in the message. My daughter Sharon has an eraser that has shredded more paper than Fawn Hall as she tore up the margins of the local daily news decrypting something like "hzsdmpdy nsvigotrf pm yjr hzsddmpdrf tiddosm npcrt."

So, the first C program I wrote had as its noble purpose the maintenance of letter substitutions and the display of partially decoded cryptograms. it's surprising how quickly you can decode a cryptogram when the tedium is eliminated. Example 1, on page 101, is a PC version of the program. The original version worked with whatever terminal I had at the time. You'll need the ANSI.SYS driver installed to run this version. Most C compilers and interpreters will handle it.

Let's conclude this trip down memory lane with a confession. Despite receiving this column as an assignment, I am not really an expert in the C language. In a moment, I'll describe a problem that took several days getting solved. I presume to write this column because I am probably just like your C programmer who can make programs run and who has a grass-roots understanding of what constitutes a useful, reliable, maintainable C program. If you are looking for the scholarly treatment of the language through an academic microscope, look elsewhere. Those forums exist and they have value. This column is about using the C language as a tool to build programs that people can use.

So much for introductions and the good old days. In the months to come, we will be indulging a favorite pastime, the publication and explanation of reusable C language tools. I've written four books and several articles on the subject, and the feedback from readers says that the end is well worth its pursuit. This column is a good medium for such material; the publication of free software tools is the honored tradition of DDS.

The little decrypting program in this issue is not necessarily meant to be an example of what you can expect from this column. As with the previous "C Chest" columns, most of these columns will have substantial examples of C code. However, some columns will digress to discuss other matters. I might review a book or a product or interview a C luminary. Occasionally, I will devote a column to the letters from you dear readers. Always, though, there will be at least a vignette of code. Thus the little game in Listing One.

One of the benefits of writing a column is that you get to plug some of your own stuff. In the past I operated a not-very-successful mailorder business selling software tools. Some of the products are still available, but I have no business interest in them any more. Occasionally this column might discuss one of them if it is relevant to the matter at hand. If that happens, I will tell you of the special relationship that I have with the item so that you might understand my unbridled enthusiasm and take it however you wish. All that aside, I do not hesitate to encourage you to read my books.

Crotchets

James J. Kilpatrick is a national political commentator with a wide readership on the editorial pages of many newspapers. Besides writing and appearing on television programs, Mr. Kilpatrick has appointed himself one of the guardians of good style and proper usage of the English language. (Self-appointed guardians of style and usage are the only kind we get-no one else cares enough to appoint them.) He has written a wonderful book called The Writer's Art in which he lists what he calls his crotchets about writing practices. Kilpatrick's crotchets are pet peeves such as the abuses of the words hopefully and only, and his list is cleverly presented and makes a lot of sense. If you love to read or write, I recommend that you read The Writer's Art. The foreword by William F. Buckley is itself worth the price. Don't be put off if the politics of either author differ from yours. There are no political statements in The Writer's Art, just good sense about the written word. Borrowing Kilpatrick's idea and transferring its application from English to C, each "C Programming" column will offer one or more of my own C programming crotchets when they occur to me and as I come across them. Here are some examples to start the list.

Example 1: PC version of crypto.c

     #include <stdio.h>
     #include <ctype.h>
     #include <string.h>

     char crypto [80];        /* encrypted message */
     char decoded [80]        /* decrypted message */
     char alphabet [] - "\nabcdefghijklmnopqrstuvwxyz"'
     char subs [] - "                                ";

     /* -----ANSI.SYS screen driver-----------*/
     #define cursor (x,y) printf("\033[%02dH",y,x)
     #define clear_screen() puts("\033[2]")

     main ()
     {
          int i, cp, a, b;
          char sb [3];

          clear_screen ();
          cursor (1, 6);
          puts("Enter the encrpted quote:\n");
          fget(crypto, 80, stdin);
          for (i = 0; i < strlen(crypto); i++)
               decoded[i] = ' ';        /*clear the decrypted msg */
          decoded[i] = '\0';
          while (1)  {
               cursor(1,9);
               puts(decoded);
               puts(alphabet);          /* display the alphabet */
               puts("\nEnter 2 letter substitution: (xy):");
               fgets(sb, "99", 2) == 0)
                    break;
               a = *sb, b = *(sb+1);
               if (isalpha(a) && isalpha(b)) {
                    for (cp = 0; cp < 26; cp++)
                         if (subs[cp] == a)
                         subs[cp] = ' ';
                    subs[b - 'a'] = a;
                    for (cp = 0; cp < strlen(crypto); cp++) {
                         if (decoded[cp] == b)
                              decoded[cp] = ' ';
                         if (tolower(crypto[cp]) == a)
                              decoded[cp] = b:
                    }
               }
          }
     }


Example 2: Several ways how to position braces and indent lines.

     /* --- then ... endif style (K&R, too) --- */
     if (a == b)    {
          foo();
          bar();
     }
     /* --- begin ... end style --- */
     if (a == b)
     {
          foo();
          bar();
     }
     /* --- do ... doend style --- */
     if (a == b)
          {
          foo();
          bar();
          }
     /* --- poised roadrunner style (ugh!) --- */
     if (a == b)
     {    foo();
          bar();    }
     <end>

C Crotchet Number 1

I am vexed with C language preprocessor programs or macro files that encourage you to code in some nonstandard syntax, which is then translated into C. The practice suggests that the C language needs improvement (beyond ANSI and C + +, of course). For example, some preprocessors or macros want you to use begin and end in place of the open and close braces, supposedly to make your code look more like Pascal or otherwise more readable. in my opinion, budding C programmers are better off without these so-called C enhancers. It's better to learn the real thing and come to love it a lot sooner.

C Crotchet Number 2

I do not like to read C programs that are inconsistent in their positioning of braces and indenting code. Example 2, this page, presents several ways that you might position braces and indent lines.

Whichever of these or other styles you prefer, be consistent or risk raising the hackles of the next reader of your code. The problem of inconsistency usually sneaks in when someone modifies the code of another and the two programmers prefer different styles. A program with such mixed conventions is a mess.

Two crotchets are enough to give you the idea and should be enough for a month's column. My crotchets are based in my opinions and do not reflect anybody's official sanction. Not all of the crotchets are about C usage. Some might be about compilers or libraries or writers or anything at all that gets the goat. If you wish to add to or subtract from the list--perhaps you grouse at writers who always use foo and bar in their examples--please send your contributions care of DDS or on BIX (alstevens) on CompuServe (71101,1262). I will include interesting, funny, or otherwise relevant crotchets in this column along with the names of the crotchety contributors.

The Books Department

The book of the month is Macintosh Programming Secrets by Scott Knaster. It has no C code, but the book is necessary reading for any programmer who is about to tackle the Macintosh. Don't let the dose of Pascal scare you off; those Pascal guys will come around soon enough, and there are several good C compilers for the Macintosh right now.

Besides being a good introduction to the insides of this fascinating machine, the book is funny just when it needs to be, and is never funny when it does not. I do not usually like computer books that try to be funny. More often than not, the author can't quite pull it off, using tiresome parody or weary satire. Knaster has successfully balanced entertaining humor and worthwhile technical information. Most writers wish they could do that and get away with it. Usually a wise old editor with green eyeshades, a mean red pencil, and a paling sense of humor crosses out all those witty gems. (if you are not reading these words, it has happened again.I That notwithstanding, you are urged to read Knaster's work. He held sway over that editor and managed to get a book to market that informs, entertains, and makes you laugh out loud in places.

Son of K&R

Ten years after its initial publication, The C Programming Language has been released in a second edition. It occurs that there might be a lot of C programmers who have never read the first edition. Shame. Plenty of other C books set out to do again what the "white book" does best: describe C to programmers. For my money, K&R is still the best introduction to C if you already know what programming is. The second edition catches up to the emerging ANSI standard (oh, so long it has been "emerging") although Prentice Hall might have been prudent to wait for the official standard rather than promote the book as "based on draft proposed ANSI C" in a banner on the cover. I suppose the draft is pretty close to the final standard but this early publication probably means I'll have to pop for another $21 when the "draft" tag is removed for the next printing of the second edition.

Seen this summer in Paris: a French automobile with a chrome emblem insignia identifying its model: "Turbo D." A new language and compiler from Philippe, perhaps?

Confusing but necessary C Constructs

Suppose that you have an array of structures and that each structure has an array of function pointers. Now, given a pointer to the structure array, an integer that subscripts to the desired structure element in the array, and an integer that subscripts to the function you want to call, how do you write the expression that calls the function?

While you think about it, here is some background. This exercise is not another C puzzle, but a real problem encountered in a general purpose menu driver. The driver uses arrays of structures and strings to describe a hierarchy of menus. This concept of a reusable menu tool is central to most C systems that I develop, and its format has occasionally changed to support different styles of menu presentation. You will not be surprised that the same technique figures significantly in a new "C Programming" project to be revealed soon.

The structure just mentioned describes a menu. The array of those structures represents all the menus in a program. The array of function pointers represents the functions that are to be called when the user chooses the corresponding menu commands. Example 3, this page, shows the pertinent data structures and items. In the problem, the mnn pointer points to the menu array, the curr_menu integer represents the current menu, and the selection integer represents the current selection integer. Imagine that you want to call the proper function, and pass it the menu number and selection number that caused it to be called.

Example 3: A structure pointing to an array of menus and the functions for controlling them.

     struct menus {
          char *menu_selections [MAX_SELECTIONS];
          int (*func[MAX_SELECTIONS]) (int, int);
     } mn{MAX_MENUS];

     struct menus *mnn = mn;
     int curr_menu;
     int selection;

A seasoned C programmer might intuitively come up with the correct expression without thinking about it. A new C programmer, however, is going to have to work on it for a while. You might argue that a new C programmer would never have this dilemma because the construct would not be obvious--the programmer would design a simpler data structure--perhaps distinct arrays for each menu. Nonetheless, these problems are real, and you get a better understanding of the C language after you solve them.

Does the answer seem obvious? If so, congratulations. If not, it will clear up once you understand it. Earlier, as a fledgling C programmer, I designed myself into this corner without realizing how perplexing it was going to be. The correct syntax of the calling statement is what eluded me. So, taking the components of the statement, I tried every variation of parentheses, subscripts, and pointer operators until the statement worked. A solution that worked with one compiler failed with several others. The one shown later was one that all the compilers accepted. To derive it, you can break down the different parts of the problem, a better approach than my stumbling block method. We start by knowing that a function call through a pointer looks like this:

     (*func)(curr_menu,selection);

The func pointer contains the address of some function. In the ANSI standard, you can leave out the asterisk and the binding parentheses so that the function call looks like a regular, nonpointer function call. I am not quite ready for this new convention. The older convention--which is still supported, thank goodness--tells you as you read the statement that the call is made through a pointer. This convention gives you a clue as to where to look for the function being called. Such clues are of value when you are searching through some old, perhaps not well commented code. I prefer it when the language helps to explain itself.

Here is the way that a function's address is assigned to a function pointer:

     func = funcname;

Simple, isn't it? You already knew that. Now suppose that the function pointer is in an array. If the user's menu selection subscripts the array, the assignment looks like this:

     func[selection] = funcname;

This assignment, too, is obvious. But to call through this pointer array, you need a subscripting integer to specify which of the pointers in the array contains the address of the function to be called. The subscripted call looks like this:

     (*func[selection]) (curr_menu,selection);

This call is not as apparent. I needed some experimenting to arrive at it. Now on to the structure: if the function pointer in the structure was not in an array, and mm pointed to the specific structure, the assignment would be like this:

     mm->func = funcname;

Then the call would look like this:

     (*mm->func)(curr_menu, selection);

The progression to this point is also not as obvious, but reasonable. It took me a while to figure out the proximity of the asterisk pointer operator and the parentheses. Now add the selection subscript to the statement and it looks like this:

     (*mm->func[selection]) (curr_menu, selection);

But in the problem, the mm pointer points to the array--the first structure element in the array--rather than the desired structure element, and the curr_menu integer indicates which element in the array represents the current menu. So the final answer is this:

     (*(mm + curr_menu)->func[selection])                     (curr_menu, selection);

The new ANSI rule would simplify it only a little as shown here.

     (mm + curr_menu) >func[selection]                (curr_menu, selection);

Because of the close relationship between pointers and arrays, there are variations on this expression that will work as well. All of them are equally as cryptic.

These kinds of problems will discourage all but the strongest of hearts. When C programmers gather around the cooler and discuss such matters, they tend to dismiss such solutions as trivial and obvious. It is a maddening characteristic of C that the answers to coding puzzles are elusive when you don't know them and are obvious once you do. This characteristic makes you ask yourself if you are the only one who doesn't understand. Take heart. Unless you are a computer scientist and language expert with a natural propensity for reflexive left-to-right parsing and precedence evaluation, you will have to work out a problem like this one every now and then. By taking the stepwise approach to figuring out a complex C idiom such as is the one illustrated here, you can avoid the trial-and-error method that I innocently used. Here's a tip. Sign on to BIX or CompuServe and ask someone. There are some bright folks there, and almost certainly one of them will have worked out a similar problem. If not, someone is usually willing to go off-line, work out a solution, and sign back on to post an answer. I've never been disappointed by these generous people.

C Programming Project

Over the next several months, I will be using the forum of the "C Programming" column to develop a C language utility program. The full purpose of the program will be revealed in a later column, but for now I will say that the program will be used for modem communications and will run on the IBM PC and compatible computers. Do not dismiss this as just another modem program; I will tell you its full reason for being soon and it will be of interest to most of you. Most of the source code will be published in this column; all of it will be readily available to any DDS reader in printed and magnetic media.

The project will be made up of a number of C tools that are adapted from C libraries, and there will be some custom applications code. I will begin by defining and developing the tools--among them a window library, a menu manager (with that elusive function pointer), a help library, data entry screens, communications functions, and a text editor. Each month, I will add new tools to the collection. You will be able to use the tools in other applications, but their primary purpose is to support the project. The tool set will evolve in a bottom-up fashion. As I build new capabilities, they will use the functions in the libraries that precede them. Those of you who read the books I write will recognize the style (and some of the functions).

The program will be written in C, of course, to be compiled with Borland's Turbo C, which I selected because of its popularity and its strong text-screen-function library. Turbo C provides PC screen-management functions that lift the burden of worry over display adapters and video retrace signals. Since only the screen-window-management tools will use these Turbo libraries, you can port the rest of the code to other C compilers with a small bit of modification.

This project will be an interactive one. Your comments and suggestions will contribute to its progress. Perhaps between us we'll write the next great American program.

C Programming Sign-off

I would like to close this, my maiden trip in the "C Programming" column, with a tip of the cap to my predecessor, Allen Holub, whose contributions to the advancement of the C language are substantial. We have not met, but I have gained a great deal from his work in his column and in his books. Allen received a vote of appreciation in the preface to the second edition of Kernighan and Ritchie's The C Programming Language. In this business, such an acknowledgment is the equivalent of an Academy Award. Fare well, Allen, in your new endeavors.

DDS