Copyright 1984 by ABComputing July 15, 1984 ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º Introduction to C º º º º by º º º º Ron Watson º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Introduction ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Welcome to the second installment of our C tutorial. It occurred to me that you may not yet own a C compiler, or want to replace the one you own. So, before starting the technical part of this article, I offer some suggestions to aid you in the selection process. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Choosing a Compiler ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ First and foremost, the compiler should adhere strictly to the standards defined in Appendix A of K&R. ("The C Reference Manual.") Every compiler will deviate to some extent, so check the documentation to see if the deviations are thoroughly explained. You should be suspicious if the documentation states only that the compiler complies with the K&R standards, without offering detailed explanations. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Available Functions ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Next, look at the list of functions provided with the compiler. These should be comparable to the functions described in chapters 7 and 8 in K&R. Again, the compiler documentation should not only list the functions provided, but offer some description as to how they are used, and point out the differences from the definitions in K&R. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Enhancements ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ These will include variations in syntax rules, additional functions, etc. Study these "enhancements" carefully as their use will affect the portability of your programs. Most compiler packages will include functions in their libraries to provide access to the operating system. There is no K&R or UNIX standard for this, so each package supports this feature differently. Some use a "general DOS gate" which provides access to the DOS function-call 21H routines. This is the minimum help that should be provided. A more helpful set would include function calls for the function 21H interface, a cursor-movement interface, a screen interface, access to the communication port, etc. In any case, be prepared to buy or write some assembly-language routines if you want complete access to the operating system. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Popularity ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Don't underestimate the importance of popularity. Surely that played a part in your decision to purchase the IBM-PC or one of its clones. The same reasoning that led you to buy a computer is equally valid when considering a compiler. Popularity greatly affects the availability of add-on products for the compiler just as it affects the availability of add-ons for your computer. You might review the advertisements for C add-on libraries in magazines such as PC Age, Softalk, PC Tech Journal, etc., before purchasing a compiler. For many of these products, the target compiler must be specified as part of the purchase information, and some products are not available for every compiler. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Price ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Lastly, consider price. The most expensive compilers available for the PC cost about $500. This is a minimal amount in comparison to the value of the time you will invest while using these compilers. Of course, if you are only interested in dabbling in C, consider buying one of the less-expensive compilers. But, if serious development work is your primary goal, it is foolish to place overwhelming importance on the price. I have used the Lattice compiler (available from Microsoft or Lifeboat) and the c-systems compiler, from c-systems in Fullerton, CA. Rather than recommend either, I will say that both are excellent in many respects, and both have a few weaknesses. Lattice will generate the fastest object code in the least amount of time, but the c-systems program has some very powerful debugging tools that are particularly useful to the novice. There are many others, of course, and you can find reviews of several in the PC Tech Journal, Volume 1, Nos. 3 and 4. Enough of this small talk. Let's move on to the C tutorial. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The "If" Approach ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ C contains a very complete lexicon of verbs for looping, including the ever popular "if-then-else", the more modern "while" and "for", the classic "do" and the dreaded "goto". I will demonstrate each of these, using a simple program that squares the integers from one to ten and displays the results using "printf". First, with "if" and "goto": main() { int i, isquared; i = 10; loop: isquared = i * i; printf(%d squared = %d\n",i,isquared); i = i - 1; if (i > 0) then goto loop; } Most of the elements in this program, with the exception of the label "loop:", were discussed in my last article. The other new item that may not be obvious is the relational expression "(i > 0)" used in the "if" statement. (Note that it must be enclosed in parentheses.) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Relational Operators ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The relational operators in C are: > "greater than" < "less than" >= "greater than or equal to" <= "less than or equal to" != "symbol for inequality" == "symbol for equality" They operate as expected. Notice the equality symbol "=="; two equal signs are used for a relational expression, but only one for an assignment statement. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The "Do" Approach ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ In practice, I doubt you will find a C program written like the one above. There isn't a thing wrong with it and it violates no rules of good structured programming. It's just old-fashioned, and as shown below requires needless keystrokes to enter and excessive diskette space to store. The same program written using the C version of the classic "do" loop is: main() { int i, isquared; i = 10; do { isquared = i * i; printf("%d squared = %d\n",i,isquared); i = i - 1; } while (i > 0); } This construction puts the relational expression at the end of the loop to emphasize that the test is done after the loop is executed, guaranteeing at least one cycle through the code. The range of the "do" is delimited by the beginning and ending braces following the "do" verb. This program requires fewer keystrokes than the "if-goto" form, but by C standards this is still too many. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The "While" Approach ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The "while" construct is used more often than the "do" and is similar in performance to the conventional "while" structure. main() { int i, isquared; i = 10; while ( i > 0 ){ isquared = i * i; printf("%d squared = %d\n",i,isquared); i = i - 1; } } As for the "do", the braces delimit the range of the "while" instruction, but the relational expression is coded at the top of the loop, emphasizing the fact that the test will be performed first and the code executed only if the test condition is true. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The "For" Approach ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ All of the above constructions require the programmer to code specific lines to initialize and decrement the control variable, "i." The "for" command allows these instructions to be combined on the same line. main() int i, isquared; for(i=10; i>0; i=i-1) { isquared = i * i; printf("%d squared is %d\n",i,isquared); } } The "for" command has three arguments. The first, initializes i to 10, and the initialization is performed once at the beginning of the loop. The second argument, i>0, is a test performed before each iteration, and the loop terminates if the condition becomes false. The third argument, i = i-1, is used once after each iteration of the loop to decrement the value of i. The braces are used to delimit the range of commands to be executed with each iteration. The "for" command is a very powerful command, more so than may be readily apparent: the three arguments are optional, each can contain any expression, not just those that are directly related to the control variable, and, with the use of the "comma operator" described later each argument can contain several expressions. Further examples of the ubiquitous "for" command will be provided as we continue in this column. The "for" command is a good example of C's freedom of expression. The definition of the command only states that the first argument will be executed once when the loop is initialized, the second argument once at the beginning of each iteration, and the third argument once at the end of each iteration. The actual content of the argument is left completely to the discretion of the programmer. Whether the expressions contained in the arguments are related to the content of the loop is entirely up to the programmer. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Squeezing Bytes ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ While the previous version of our program has become much shorter than the first version in terms of required keystrokes, it still is not as brief as it could be. Note the many occurrences of the variable "i"; and is the "isquared" variable necessary? The following version reduces the overall size further by eliminating some of these redundancies. main() { int i; for(i=11; i=i-1;) printf("d% squared = %d",i,i*i); } How about that! Down to one variable and one line of executable code. Note that the braces are not needed to delimit the range of the "for" when it contains only one statement; the same is true for the "while" construct above. Replacing the "isquared" variable with the expression i*i in the printf statement should be intuitively obvious, and serves to demonstrate the freedom of C's syntax. Generally, an expression can be used in any context allowed for a variable name. The above "for" command has only two arguments, as I have combined the decrementing and testing of the control variable into one expression. This is possible because the definitions of "true" and "false" are simply non-zero and zero, respectively, and the result of any assignment statement is implicitly available for testing after the assignment is complete: the value assigned is tested for "true" or "false" as defined. The expression "i=i-1" in the second argument position decrements the variable and then, because it is in the second argument, the result of the assignment is tested for zero to determine if the loop is complete. Note that the initialization expression becomes "i=11" in this case, and as the second argument is performed at the beginning of each iteration "i" will be decremented once, before the printf is executed the first time. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Smaller Code Yet! ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Well, you might say, that is certainly a very terse way to code a solution to this problem. True, it is fairly terse, but there is one last way we shorten it. There is the small matter of mentioning the variable "i" twice in the same expression ("i=i-1"). Surely, there is some way to relieve the incredible burden of typing that "i" twice. Attend: main() { int i; for(i=11;i-=1;) printf("%d squared = %d\n",i,i*i); } There, now isn't that better? This is a special construction allowed by C to abbreviate expressions that perform arithmetic or boolean operations on a variable and store the result in the same variable. The expression "i-=1" is exactly equivalent to "i=i-1", and this type of notation is fully integrated into C. For example: conventional notation shorthand a = a + b; a += b; (addition) b = b / 10; a /= 10; (division) x = x * x; x *= x; (multiplication) An excellent convenience, I'm sure you'll agree. It is even more useful when the variable to be operated on is a complex one, as we shall see in later columns. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ The Smallest Code? ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ One last method of abbreviation. The thoughtful authors of C, themselves being programmers, noticed that often it was necessary to increment or decrement a variable by 1. Why, they thought, shouldn't the language contain a special operator for this purpose? And so it does, as this last example demonstrates: main() { int i; for(i=11; --i;) printf("%d squared = %d\n",i,i*i); } The double minus signs preceding the "i" yields the same result as the "i-=1" in the previous example. Double plus signs "++i" can be used to increment a variable by one. The plus or minus signs can also follow the variable as in "i--" or "i++", with slightly different results in some cases, which we will discuss later. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Conclusion ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ The text file that accompanies this article contains the above examples in a form suitable for compilation. Review these examples between now and the next issue, and experiment. Try the last example with "i--" to see what difference it makes, or modify "i" within the body of the "for" loop. Some compilers will generate different object code for "i=i-1" and "--i". Does yours? Can you prove it? My next column covers control-flow structures. We shower the "if" statement with the attention it deserves, provide a complete description of relational and conditional expressions, and introduce the switch-case-break statement. C you then. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ File Name: ÛÛ c1.txt ÛÛ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ