-------------------------------------------------------------------------- __________________________________________________________________________ @BEGIN_FILE_ID.DIZAmiga C For Beginners part 1 A Complete C book for the Amiga @END_FILE_ID.DIZ -------------------------------------------------------------------------- * * * AMIGA 'C' FOR BEGINNERS * * * * TYPED IN BY RAZOR BLADE OF ALLIANCE * * * * GREETS TO :- VIPER , BLACKBEARD , ARAMIS , CHAOS , MIT , * * SHADOWFAX AND ALCHEMIST. * * * *------------------------------------------------------------------------- NOTE ** :- DON'T WORRY ABOUT THE MISSING PAGE NUMBERS. THERE ARE A LOT A BLANK PAGES IN THE ORIGINAL BOOK AND THE TITLE OF EACH CHAPTER IS PRINTED IN LARGE CHARATERS ON A WHOLE PAGE. THERE WILL THEREFORE USUALLY BE TWO OR THREE PAGE NUMBERS MISSING BETWEEN EACH CHAPTER (RIP-OFF OR WHAT !!!). The notation ** has been used to represent exponentiation. I.e 10 squared woudl be shown as 10**2. TABLE OF CONTENTS. 1. Introduction to C ...................... 1 1.1 Program Execution .................... 4 1.2 Compiler Vs Interpreter .............. 5 2 Beginning C ............................ 7 2.1 The Editor ........................... 10 2.2 The Compiler ......................... 11 2.3 The Linker ........................... 12 2.4 Putting it all together .............. 13 3. The First Program ...................... 15 3.1 Using ED ............................. 18 3.2 Compiling ............................ 19 3.3 Error Messages ....................... 21 4. Theory and Practise .................... 23 4.1 Program Format ....................... 27 4.2 Defining a Function .................. 28 4.3 PRINTF and Escape Sequences .......... 29 4.4 Comments ............................. 30 4.5 Varaibles and Arithmetic ............. 31 4.5.1 Integers ........................... 31 4.5.2 The IF statement ................... 32 4.5.3 Calculating with C ................. 35 4.5.4 Floating Point Numbers ............. 37 4.5.5 Characters and Character Strings ... 39 5. Loops .................................. 43 5.1 WHILE Loops .......................... 45 5.2 FOR Loops ............................ 48 5.3 DO...WHILE Loop ...................... 49 5.3.1 More Error Checking ................ 49 5.4 AND and OR ........................... 52 6. Strings ................................ 55 6.1 Backtracking ......................... 57 7. Calculating in C ....................... 59 8. Variables .............................. 63 8.1 Variable Names ....................... 65 8.2 Data Types ........................... 67 8.3 Type Conversion ...................... 70 8.4 The Cast Operation ................... 71 9. PRINT and SCANF ........................ 73 9.1 More escape sequences................. 75 9.2 Format Specification ................. 77 9.3 Octal and Hexadecimal ................ 80 9.3.1 Conversion Program ................. 82 9.4 Character codes ...................... 84 9.4.1 About the backslash ................ 85 9.4.2 Going the other Direction .......... 86 10. The Pre-processor ...................... 87 10.1 #define .............................. 89 10.2 #include ............................. 91 11. Abbreviations .......................... 93 11.1 Increment and Decrement .............. 97 11.2 Definition, Declaration, Initialising. 99 11.3 Multiple Assignments in C ........... 101 12. Functions ............................. 103 12.1 Functions with Arguments ............ 106 12.2 Functions without return values ..... 108 12.3 Other Functions ..................... 109 12.3.1 STRCPY - Version 1 ................ 109 12.3.2 STRLEN ............................ 111 13. Arrays ................................ 113 13.1 Multi-dimensional Arrays ............ 116 14. More about loops ...................... 121 14.1 More about the FOR loop ............. 123 14.2 BREAK ............................... 124 14.3 CONTINUE ............................ 125 14.4 The SWITCH directive ................ 126 15. Pointers and Addresses ................ 129 15.1 Addresses............................ 131 15.2 Pointers ............................ 133 15.2.1 The EXCHANGE function with pointer. 134 15.2.2 STRCPY - version 2 ................ 135 15.2.3 STRCPY - version 3 ................ 135 15.3 Pointer without storage ............. 138 16. Storage Classes ....................... 141 16.1 Auto ................................ 143 16.2 Static .............................. 144 16.3 External ............................ 145 16.4 Register ............................ 146 16.4.1 Fast STRCPY routine ............... 147 16.5 Local ............................... 149 17. User defined Libraries ................ 151 17.1 The STRCMP function ................. 154 17.2 ITOA ................................ 157 17.3 Reverse ............................. 159 18. C features ............................ 161 18.1 The ?: Operator ..................... 163 18.2 The SIZEOF function ................. 164 18.3 Bit manipulation .................... 165 18.3.1 AND ............................... 165 18.3.2 OR ................................ 166 18.3.3 Bitwise Shift Operators ........... 167 18.3.4 EXCLUSIVE OR ...................... 168 18.3.5 One's Complement .................. 168 18.4 GOTO ................................ 169 19. Complex Data Types .................... 171 19.1 STRUCT .............................. 173 19.2 Bit fields .......................... 175 19.3 Unions .............................. 176 19.4 ENUM ................................ 177 19.5 TYPEDEF ............................. 178 20. Important Concepts .................... 179 20.1 Declarations ........................ 181 20.2 Initialisation ...................... 184 21. Pointer Arrays ........................ 187 22. Useful Macros ......................... 193 22.1 Macro Error Sources ................. 196 22.2 Library Macros ...................... 198 23. Communication ......................... 201 23.1 Passing data with CLI ............... 203 23.2 Buffered Input / Output ............. 206 23.3 More buffered Input / Output ........ 211 23.4 Unbuffered Input / Output ........... 213 23.5 Direct Access ....................... 216 23.6 Reading a Character ................. 218 23.6.1 Standard Input / Output ........... 218 23.7 A User Window ....................... 219 23.7.1 The Three Windows ................. 219 23.8 Redirection ......................... 222 24. Tricks and Tips ....................... 225 24.1 Starting from the Workbench ......... 227 24.2 Other Preprocessor Directives ....... 229 24.3 Finding and Removing Errors ......... 231 25. System Programming .................... 233 25.1 The Intuition Principle ............. 235 25.2 A Window under Intuition ............ 236 25.2.1 The Window Flags .................. 237 25.2.2 Opening a Window .................. 238 25.2.3 A window program .................. 239 25.3 Screens ............................. 242 25.3.1 A Screen program .................. 243 25.4 Text / Grpahic Window Display ....... 246 25.4.1 TEXT .............................. 246 25.4.2 MOVE .............................. 246 25.4.3 DRAW .............................. 247 25.4.4 Small Drawing Program ............. 248 25.4.5 Low Resolution and Interlace Modes. 252 25.4.6 Pixel Processing .................. 253 25.5 DOS ................................. 257 25.6 SETCOMMENT .......................... 258 25.7 Read Directory ...................... 259 25.8 Conclusion .......................... 262 Appendices ................................ 263 A. Functions ............................. 265 B. The History of C ...................... 270 C. The Lattice C Compiler ................ 272 D. The Aztec C Compiler .................. 273 E. Reserved C Words ...................... 276 F. Operator Precedence ................... 277 G. Storage Classes ....................... 278 H. Type Conversions ...................... 278 I. Modes for FOPEN ....................... 279 Index ..................................... 281 ========================================================================== CHAPTER 1 - INTRODUCTION TO C So you want to learn C. That's obvious or you wouldn't be reading this book. The goal of this book is to help you learn to program in C on the Amiga in as little time as possible. AMIGA C FOR BEGINNERS is written as a short course in the C language for anyone who wants to learn about the C language from scratch, without a lot of technical jargon. This book is divided roughly into two parts. The first part introduces the reader to the basic structures of C programming through operating the editor, compiler and linker. It also contains many sample programs. You'll be able to write your own short programs in C in only a few hours. This first part also takes the beginning C programmer through the essentials of C - calculations, string handling, loops and more. It even helps you find the errors made most often by most new C programmers. The second part, which begins at chapter 10, explains the background and peculiarities of each C statement and function. These include preprocessor commands, arrays, loops, pointers, addresses and memory classes. If these words don't mean anything to you now, they will when you start reaidng the second section. The second part also discusses macros, interfacing your Amiga to the outside world, tricks and tips for the C language and graphic programming. The appendices describe the history and development of the C language, as well as operating instructions for a number of popular C compilers (see your compiler's instruction manual since many compilers are constantly being upgraded). In addition, you'll find a number of practical C functions, mathematical precedence, reserved C keywords, memory classses and type conversions. This chapter described the general nature of C. You'll see it's advantages and disadvantages compared to interpreted languages, as well as advantages and disadvantages compared to other compiler languages. You'll also see why C stands out above so many other computer languages. PAGE 3 -------------------------------------------------------------------------- 1.1 PROGRAM EXECUTION. Before starting we need to ask the big question WHAT IS C? There are two kinds of computer languages. First, there are interpreted languages such as BASIC and LOGO. Second there are compiled languages such as C, Pascal or Modula 2. Section 1.2 contains detailed information about the advantages and disadvantages of interpreters and compilers. Compilers are programs which translate the language statements into a form understandable to the computer. This form consists of the numbers 0 and 1 (the numbers used in the binary system). Since people can't remember long strings of zeros and ones, the computer can be told to interprete words and other number systems as binary numbers. For example, a typical machine language instruction LDA means "Load the Accumulator", which is easier to remember than the binary number 10011101. The computer must contain a Central Processing Unit (CPU) to respond to the machine language instruction available in response to every keyword. Programming with the binary machines is called machine language programming. Using machine language mnemonics or intructions is called assembly language. The instruction which executes on one computer may not even exist on another computer. It would be easier for the programmer if the computer could be told in plain English what to do. Needless to say, computer languages haven't reached the level of a DWIM (Do What I Mean) interface. This type of language may exist a few years from now, when technology produces more helpful developments using artificial intelligence. High level languages act as a compromise between machine language and the human language. These languages contain a limited number of statements or keywords which in turn execute specific tasks. Unlike assembly language, many of these languages aren't tailored to a specific computer. The computer must execute several hundred machine instructions for every single statement in the high level language. An example would be the basic command "Load filename", which tells the computer to load a file. PAGE 4 -------------------------------------------------------------------------- 1.2 COMPILER VS INTERPRETER. As previously mentioned, two types of higher level languages exist: interpreted and compiled. An interpreter searches for keywords in the program text (or source), verifies that the command is a legal one and executes the equivalent machine language instructions. Then the interpreter searches for the next command, tests it and executes the machine instruction etc.. This translation process performed by the interpreter can be compared to the work of a human foreign language interpreter. An interpreter is a translator which translates the words of the higher level language into machine level instructions as needed. A compiler translates the source program ONCE into executable form. This is similar to someone who translates foreign language literature into his/her own language. This translator takes the time to select the proper choice of words for the text, unlike the interpreter who mediates a conversation between two people from different language backgrounds. The interpreter executes programs immediately. The interpreter can also be stopped by the user to check on certain values stores in certain variables. Interpreted program execution can usually be continued without causing problems. The main advantages of interpreters are flexibility and spontaneity. The compiler translates the program source only once. The compiler may spend a few minutes compiling the program before it can be executed. Afterwards, the compiled program executes much faster than an interpreted program, since the compiler doesn't have to re-translate the source program. This advantage become most evident in program loops in which a command can be executed several thousand times. The interpreter translates the command into machine language, repeating this process a few thousand times. The compiled program already knows what to do without repeating the translation. The advantages of compiled languages lie in shorter execution times for compiled programs. Interpreters, because of easy access to their programs, let the programmer enter corrections and modifications on the fly. They also let the programmer enter and execute program code until an error occurs. This "run-until-it-breaks-then-fix-it" attitude causes lazy, unstructured programming style. PAGE 5 -------------------------------------------------------------------------- Compilers don't usually allow easy error correction. When an error occurs you must re-load the program source editor, fix the errors in the source code and recompile the program. In addition, compiled languages require a specific language structure, or the program won't compile. The source code must be correct from beginning to end or the compiler will not compile it. The biggest advantages that C has over other languages is it's transportability. This means that you should be able to take a C source code from an Amiga, transfer it to a PC, make changes to fit the PC's File handling and other machine-specific tasks, and compile the C source code on the PC with no problem. There you have a general overview of the advantages and disadvantages of compiled languages and interpreted languages. The next chapter spends some time talking about these languages as well, while adding specifics about the subject of the book - the C language. PAGE 6 ========================================================================== CHAPTER 2 - BEGINNING C Let's take a look at the process involved in developing a C program. Although it may seem long and involved, don't panic. Once you learn the basics of developing a high-level language program it get's easier. One word of warning: Don't skip this chapter, even if you have previous experience with C. You might learn something you didn't know before about the language. Programs begin with an idea. The user has a task that he wants to accomplish using a program - and the program should perform the task easier, faster and more accurately than a human could do it. This idea could be for a drawing program; a spreadsheet for calculating payroll and figures; a fast disk copier; or just a simple text display on the screen. Once the general idea is developed, it helps if the programmer sits down and writes out the goals of the idea, and how the program can do this. This writing stage can be in plain English, since it should be as readable for you as possible. This written documentation of the program execution is sometimes called pseudo-code, since it tells what the program should do without actually writing which statements the program needs to perform the task. When writing the pseudo-code this program, keep it broken down into smaller modules whenever possible. A data flowchart and program flowchart should be developed from the written documentation of the idea, just as in any other computer language. After it has been determined how the program flow should appear, the user can proceed to the computer to program the idea. The C language uses three different programs which work together in generating programs; . The EDITOR, in which the user enteres and corrects the source program. . The COMPILER, which compiles the program. . The LINKER, which joins the main program with other compiled programs and functions to make a fully executable program. The rest of this chapter dicusses the use of each program in the development of C programs. PAGE 9 -------------------------------------------------------------------------- 2.1 THE EDITOR You need some sort of text editor to enter a C program from the keyboard. An editor is nothing more than a simple word processor. It usually only contains minimal text processing capabilities. The program only usually allows you to type in, load, save and edit the text of the source code: Nothing fancy like block functions or save and replace capabilities. Most word processor programs can be used for typing in the text of a C program. If you use a word processor, you may not enter any special control characters (e.g. bold fonts and text formatting) because the C compiler would not recognise them. Many word processors provide the option of letting you save a file as an ASCII file. If you use a word processor as an editor, it must allow you to enter the special control characters needed by C source codes. C programd use braces ({}), brackets ([]), the backslash (\), the number sign (#), the pipe character (|) and the tilde character (~). Maybe you can't afford a word processor, or maybe your word processor doesn't have the necessary characters. The workbench disk which comes with your Amiga contains an editor named ED. Ed is a basic text editor, which you can find on the workbench disk of the Amiga from the CLI. Invoke the editor by entering the CLI, typing ED and the name of the file you want to load/edit, and press the key. The editor is loaded and then the C program is typed in. This text, called the C source code, is stored on the disk under a filename. The characteristics that set this apart from normal word processing files appears in the file entension: C source codes must contain a file extension of .c. Examples of names can be SORT.c or ARCHIVE.c; note that these names indicate the contents of each file. Try to use meaningful names like this instead of cryptic names like a.c or this.c. The extension of .C is especially important, since several files with the same name but different extensions are generated by a C compiler. After a file is created and saved, you can then call the C compiler to compile the C source code. PAGE 10 -------------------------------------------------------------------------- 2.2 THE COMPILER Calling the compiler loads the C compiler into memory. The compiler reads the source code and begins to convert the C source code into compiled machine language. Most compilers read the source code twice (two pass compiler). During the first pass, if it encounters an invalid expression or keyword, it stops compiling and displays an error message on the screen or writes the error to a disk message file. Whenever an error is found, you must reload the editor, and correct the source code. Once you've fixed the errors, you must save the source code file, exit the editor and restart the compiler. If the compiler finds errors again, you must repeat the above procedures. Before going any farther, you should realise one thing. Writing the C source code requires proper preparation. If you just enter the source code without giving any thought to what you're typing in, you'll spend more time learning about error messages and the editor than you will spend learning about the C language. In the beginning, prepare to see plenty of errors, and be patient with yourself as you go from compiler to editor to compiler. When you've corrected all of the errors in the source code, the compiler can finish the first pass without stopping and performs the second pass. This second pass does the final transformation into OBJECT (compiled) code, The compiler saves the object code to disk under the same name as the source code, but with an extension of .O. For example, a source code named source.c generates an object file names source.o. The object file needs one more step before it can become executable program code- this is the linker. PAGE 11 -------------------------------------------------------------------------- 2.3 THE LINKER. We're not quite done yet. The object file must still be run through the linker. The linker searches for all functions used by the program from the C libraries, and links the necessary functions into one program. A function is a subroutine similar to a procedure in Pascal. Functions are capable of solving small tasks such as drawing a line or displaying a character on the screen. The libraries contained in most C compiler packages include frequently used functions stored in object (compiled) form. These functions could be input/output functions, graphic routines, sound routines or even trigonometric functions. The linker identifies the functions required for a complete program and adds them to the main program. This saves a programmer a lot of work. The functions simply require the passing of values instead of retyping the source code for each function from scratch. The C linker permits the development of large programs in modular form. This means the several parts can be (and often are) developed separately. The uer can compile and test every module separately. This has the advantage that the complete C program doesn't have to be reloaded anfd recompiled every time the compiler detects an error. The linker eventually links all the compiled modules into one complete, executable C program. The linker cannot handle non-compiled source code. It can only link compiled functions together. Source code can load other source codes during compilation, but we'll see more on this later. PAGE 12 -------------------------------------------------------------------------- 2.4 PUTTING IT ALL TOGETHER. The Amiga's CLI (command line interface) is used to specifiy the parameters for the editor, the C compiler and the C linker. For example, entering the following line in CLI calls the Lattice C linker; ALINK file1.o file2.o TO complete You already know that a program may have to be compilerd several times before it is free of errors (syntax errors, not logical errors). You must enter the above line exactly as written. Entering the line incorrectly can cause errors in itself. The developers of most C compilers took this into consideration, and added a special feature to allow easy linker access from text files. THE MAKE FILE. The inputs required to compile and link a C source code can be written to a file called a MAKE file. This MAKE file calls all the necessary programs such as the compiler or linker. The C system reads the file just as if the user had input the text direct from the keyboard. It's easy to create a MAKE file. Instead of executing, the C compiler calls directly. You invoke the editor and write the calls to a script file. Once you save this file to disk you now have a MAKE file. AmigaDOS' EXECUTE command reads this file and passes the infornation to the C compiler sections needed to make the final executable program. See Appendix C for one example of a MAKE file and it's contents. PAGE 13 ========================================================================== CHAPTER 3 - THE FIRST PROGRAM. The following code is our first C language program. Don't enter it yet - you'll type it in a few minutes starting at section 3.1. Here's the source code so you can see what it looks like: #include void main() { printf("Hello, I am here!"); } To see what the program produces, the text must be entered using an editor. When you use the editor please enter the text exactly as it is printed here and in section 3.1. This avoids error message which will cause problems Once you've become comfortable working in C, you can change programs around to suit your own needs. But don't change anything here until after the program compiles and links exactly as you see here. A step by step procedure follows. If you're on the workbench you must start the CLI before anything else. The CLI can be found in the system drawer located on the workbench disk. Workbench 1.3 users can use the shell program which is an enhanced version of CLI. Workbench 1.2 users who can't find the CLI may have it switched off with the settings in preferences 1.2. Using preferences 1.2 you must click on the ON gadget next to the word CLI. Once you close and reopen the workbench disk icon, the CLI should appear in the window. After you invoke the CLI or shell a new window appears. This window prompts for an input with the message: 1> For a super computer such as the Amiga this program is something unusual. No icons appear, the mouse can only be used to move and size the window, and the CLI only accepts input from the keyboard. Use the mouse to enlarge the CLI window to it's maximum size. Everything that occurs from now on will be displayed on the CLI window. Now put the mouse aside - you won't be needing it for a while. PAGE 17 -------------------------------------------------------------------------- 3.1 USING ED. First you'll need an editor to enter the program. The Ed editor can be called from the C: directory on your Workbench disk. The following executes ED and creates a new file named HELLO.C: 1> ED HELLO.C The computer places the 1> prompt at the beginning of the line. Some versions of CLI may also display the current directory (e.g. The shell from workbench 1.3). The name HELLO.C will be the name of our first C language program. It doesn't matter whether you type the name in upper case or lower case characters. If you made a typing error, press the key to delete the last character typed. The key has an arrow pointing to the left on some versions of the Amiga. If you prefer you can press the key combination to delete the entire line of text. Press the key to execute the command and invoke the editor. A window appears and displays the text "Creating new file". The user now enters the program. The editor allows you to move the cursor around the file using the cursor keys to make corrections and changes. Type the following text: #include void main() { printf("Hello, I am Here!"); } Once you've finished typing the text, press the key, then press the and keys. Pressing the key saves the text (this combination will be called from here on). Press to save the text and quit the editor. The system returns you to the CLI. Pressing returns you to the CLI without saving the file. See book AMIGADOS INSIDE AND OUT from Abacus for more information about ED. Pressing returns the user to the CLI window immediately. You now have your first C source code ready to compile. PAGE 18 -------------------------------------------------------------------------- 3.2 COMPILING LATTICE 4.0 Start the Lattice 4.0 compiler with the following: 1> lc -L hello For Lattice C 4.0, the following should now appear on the screen. Your screen may differ slighly. If you get error messages see section 3.3 ...................................................................... 1> lc -L hello Lattice Amiga Dos C Compiler Version 4.0 Copyright (c) 1987 SAS Institute Inc. All rights reserved. Compiling Hello.c Module Size P=00000014 D=00000012 U=00000000 Total Files: 1, Compiled OK: 1 Linking Hello BLink - Version 7.2 Copyright (C) 1986 The Software Distillery. Copyright (C) 1987 SAS Institute Inc. All rights reserved. Box 8000 SAS Circle, Cary NC 27511-8000 - Telex 802505 (919) 467-8000 Blink Complete - Maximum code size = 5488 ($00001570) bytes. Final Output file size = 5312 (000014c0) bytes. 1> ...................................................................... AZTEC C To compile the program using the Aztec C compiler requires two steps. The Aztec system first compiles the source code then assembles and links it. A MAKE file can be quite useful. Enter the following for the Aztec C compiler: 1> cc +L hello The above sequence may not function in some cases. If not enter the same line but omit the +L. Aztec C should display the following on the screen. Your screen may differ slightly. If you get error message see section 3.3 ...................................................................... 1> cc +L hello Aztec C68K 3.6a 12-18-87 (C) 1982-1987 by Manx software systems. Inc. Aztec 68000 Assembler 3.6a 12-18-87 1> ...................................................................... Now Enter : 1> ln hello.o -lm -lc PAGE 19 ---------------------------------------------------------------------- Aztec C should display the following on the screen. You're screen may differ slightly. ...................................................................... 1> ln hello.o -lm -lc Aztec C68K linker 3.6a 12-18-87 Base: 000000 Code: 001444 Data: 0002a0 Udata: 000050 Total: 001734 1> ...................................................................... Did everything work as expected? If you didn't get an error, type the following DIR command in the CLI to see the current disk's directory. The executable program is stored there under the name HELLO. Notice that this file has no extension. There may also be other files with extensions of .MAP, .O, .LNK and of course .C. This shows that the extension helps identify the file. Call the executable program by entering the following line : hello One the screen appears the text: Hello, I am here! The program isn't earth-shattering but this is only the beginning. PAGE 20 -------------------------------------------------------------------------- 3.3 ERROR MESSAGES The most common mistake a new C programmer makes is omitting the semicolon following the closing parenthesis of the PRINTF function. This semicolon is one of the most widely used characters in C programs since it indicates the end of a statement. For this reason almost every C statement or function ends with a semi-colon. If the user omits it, the compiler reports many error messages. You might like to edit the hello.c file again and remove the semicolon. Save the file and try compiling the file again. LATTICE :- If you forgot to type in the semicolon following the PRINTF function, the Lattice C compiler displays the following message: hello.c 5 Error 57: semi-colon expected. Compiler returncode 1 AZTEC :- The Aztec C compiler displays the following message if the semicolon is missing: hello.c: 5 ERROR 69: missing semi-colon 1 errors Let's try to determine from this compiler message what is wrong with the file. The first line states the filename in which the error appeared: HELLO.C. That filename specification is useful later on (more on this later). Then follows the line number (5), error number (57) and the error description in English. This indicates that a semi-colon was expected in line 5. The user must now reload the editor to correct the error: ED HELLO.C You must include the extension of .C to edit the C source code. To reach line 5, you can count down the lines (the fastest method for a progam this short). You can also press <5> to get to line 5. The editor then moves the cursor to the line indicated. Line 5 consists only of the closing brace (}), but this is expected since the error actually occured in the previous line. The semicolon in the previous line is missing. The error messages of the C compiler should never be taken to literally, since the search for the error may have to take the surrounding code lines into consideration. PAGE 21 -------------------------------------------------------------------------- After placing the semicolon where it belongs (following the PRINTF function), save the file again and try to compile the source code again. It should work. PAGE 22 ========================================================================== CHAPTER 4 - THEORY AND PRACTISE. Now you've had some practical experience entering and compiling a program, let's look at the theory of how the program in chapter 3 works. You have two different types of keywords involved in C programming: functions and statements. The first line contains the function name VOID MAIN. The MAIN function is the most important element of a C program. The VOID means this program will not return a value and is inserted so that the compiler does not display a warning message. Without this function literally NOTHING runs. A C program usually consists of up to a hundred functions. A function handles a part of the complete program, and is marked by parenthesis (). Braces ({}) mark the beginning and end of the function. These braces surround the statements and functions which the computer should execute. The following lines call functions; printf("hello"); Value(10); music(); end(); and the following lines don't: value = old; music; end; If you haven't compiled and linked the program in chapter 3, do so now. When you execute the program from CLI, a jump occurs first to the MAIN() function. This always happens, regardless of where the function appears in the listing. It can be at the beginning, end or middle of the program, but the main() function always executes first. The MAIN function calls the PRINTF function. The PRINTF function is stored in a library. All the user needs to know is the function name (printf), what it does (displays text on the screen) and what information it requires (text). ARGUMENTS: The information passed to a function during the call are arguments. The arguments to be passed are placed within the calling function's parenthesis to ensure proper delivery. It is important to enclose any character strings within quotation marks (e.g printf("Here I am!");). After calling the PRINTF function the text appears on the screen. The PRINTF function ends and the program continues at the point where it was interrupted by the function call. PAGE 25 -------------------------------------------------------------------------- No other statements follow the line after PRINTF. This means that the main function has also reached it's end, and the program ends. The computer returns to the CLI, and additional commands can be entered. The end of the main program and the return to CLI represent the termination of the MAIN function. The user should note how important this function is. It represents the C program itself; program execution starts and ends with the main function. PAGE 26 -------------------------------------------------------------------------- 4.1 PROGRAM FORMAT Let's discuss the format of the program. The C compiler ignores any spaces, linefeeds and paragraph marks added to the listing by the user. Indenting lines or adding blank lines helps make source codes more readable to the user. Formatting has no effect on the execution speed or length of the final program. The program you entered in chapter 3 could have been in one of the following formats: void main () { printf("Hello, Here I am!"); } void main () { printf("Hello, Here I am!"); } void main() {printf("Hello, I am Here!");} It's up to the user to select the version which appears to be most readable. Once you've selected a style stick with it. However, the last version above illustrates how even a small program can be made unreadable by 'formatting' it. NOTE: All statements and functions must be entered in lowercase since C is case sensitive (it differentiates between upper and lower case letters). If you entered Printf or PRINTF instead of printf the linker reports an error since it can't find this function anywhere. PAGE 27 -------------------------------------------------------------------------- 4.2 DEFINING A FUNCTION A function only executes the statements contained within the braces. If nothing is written there the computer does nothing. A program that does nothing is not very exiciting, but it's a good example. The floowing C source code compiles without problems (and does nothing).: void main() {} Don't expect miracles from the above program. When you compile, links and start it, it loads, runs and does nothing. The computer returns to the CLI. DEFINITION: The function arguments enclosed in the braces are it's definition. It defines what the computer should do when it executes a certain function. A function can contain several statements or other function calls. Longer lines of text can be displayed on the screen with this program: void main() { printf("Hello, I have a question!\n"); printf("Do you believe in life without electricity?\n"); printf("Not me!\n"); } The screen output will appear as follows: Hello, I have a question! Do you believe in life without electricity? Not me! This program has two differences from the program in chapter 3. First, there are 3 printf instructions instead of one; second the \n character appears at the end of each string within quotation marks. See the next chapter for details on \n and other escape sequences. PAGE 28 -------------------------------------------------------------------------- 4.3 PRINTF AND ESCAPE SEQUENCES The text as the end of section 4.2 appears on the screen as it appeared in the source code, with 3 exceptions. The three \n characters do not appear. The \n character is called the newline character, one of the many escape sequences used to control the formatting of text. The \n character tells the compiler to insert a linefeed at that place in the text, just ass if you pressed the key. Any escapre sequences can be recognised by the backslash (\) preceding it. The character following indicates which escapte sequence should be executed. For example, the N signifies a linefeed: The computer starts the text after the \n at the beginning of the next line. This newline (\n) escape sequence is important, since the printf function doesn't automatically add linefeeds after it displays text. You'll remember that when you ran the program in chapter 3, the prompt appeared right after the text. Basically C writes all characters sequentially on the screen, even if they are written with different function calls. The user must program the function to advance the line. The user is not obligated place the escape sequences at the end of the character string. They can be placed between other "normal" characters or even at the beginning of the text. If desired, all three line can be writtein within one printf function: printf("Hello, I have a Question!\nDo you believe in life without electricity?\nNot me!\n"); This line is rather difficult to read. If you consistently place a newline (\n) character at the end of strings, programs will be much easier to read. The newline character is not the only method of formatting text. The printf is comparable to the PRINT statment in BASIC or the WRITE statement in Pascal. The f in printf indicates that the text can be output in a specified format. This function can process and display character strings and other values as specified by the programmer. Since these capabilities are quite extensive, we'll introduce them to the reader as needed. PAGE 29 -------------------------------------------------------------------------- 4.4 COMMENTS The C language lets the programmer insert comments in the source code. This can be used to tell the reader what the source code is supposed to do. Comments have no effect on the speed or size of the compiled program. Comments start with the /* characters and end with the */ characters. The compiler skips over everything between the comment delimeters. Use comments liberally, since they never affect the final program and add to the readability of the source code. void main() { /* This program outputs a text which starts */ printf("---Comments -- desired -- stop --\n"); /* here ^ and ends over at the opposite end^ */ } Now think of your first program from chapter 3. If you added comments to tell a future reader exactly what this program did and when, the end result could look something like this: /* Program from chapter 3 of Schaun's book */ /* Amiga C for beginners from Abacus (C) 1988 */ /* This program prints the words "hello, Here */ /* I am " to the screen. Nothin else. */ #include /* Call standard i/o header file */ void main() /* main function */ { /* start of function */ printf("Hello, Here I am!"); /* show text on screen */ } /* end of function */ This is a very exaggerated example. It doesn't matter if you comment a program this clearly though, the comments are ignored by the compiler. PAGE 30 -------------------------------------------------------------------------- 4.5 VARAIBLES AND ARITHMETIC Our knowledge of the C language is still rather small. Right now you can display text on the screen and insert comments into source code. It would be nice to be able to have a program accept input from the keyboard. The SCANF function is the opposite of the PRINTF function - it reads input instead of displaying output (more about scanf later). Consider how a program would execute for questions and answers. First the printf function would display the question on the screen. Next the SCANF function reads the user's input. For example, pretend that a number will be input as a response. The following question asks for a number 1 to 2; Are you well? (1) = Yes, (2) = No Number: The number must be stored somewhere. C provided a series of variable types which can be used. Variables allow certain types of information to be stored in a computer. The variable type depends on the type of variable you want stored. There are variables for characters, variables for strings , variables for different kinds and sizes of numbers; and variables for combinations of numbers. 4.5.1 INTEGERS The INT variable type represents integers (whole numbers). Variables of type INT can accept whole numbers from approximately -32,768 to +32,767. The following program uses integer variables, and introduces a practical application of the SCANF function: /* scan1.c section 4.5 */ void main() { int input; printf("Are you Well?\n"); printf("(1) = Yes, (2) = No\n\n"); printf("Number: "); scanf("%d",&input); printf("\n\nYour input was: %d\n",input); } PAGE 31 The first line of the maint function INT input;, tells the compiler what to do with the variables. This line assigns a name to the variable. The program uses this variable name for access to the variable's contents. The variable name (input) indicates it's purpose. Variable definition ends with a semicolon. The following lines are printf functions. Next, the SCANF function asks for the user's input in response to the text "number : ". Since a large number of data types exist, the input routine must be told what data can be expected. The format specification %d specifies the data type. Format specifications are similar to escape sequences (the characters preceeded by a backslash). The percent sign indicates a format specification; the d tells the SCANF that the value to be read in must be of type int. The name of the desired variable follows the string in quotes, separated from the string by a comma. An ampersand (&) preceeds the variable name. This is important, if the system crashes the user should first examine the SCANF function parameters to make sure they are correct. If SCANF contains the correct information, the user can enter the input in the running program. Enter a number and press the key. This value can be found in the variable 'input'. The program can now use this number. This program accepts the input and then displays the variable using the PRINTF function. The PRINTF function must also be told what kind of data it must process. A format specification identical to the once which appears in SCANF serves this purpose. Because of this, PRINTF knows that an integer number will be passed, which must be placed at the location occupied by the format specification in the text. The variable name INPUT follows the string in quotes, separated from the string by a comma. The input appears on the screen if the input was a 1 or 2 and not text (text is not allowed here). If you enter text the input becomes a large random number (in Lattice C) even though this number never appeared in the input line. If you enter no input SCANF scrolls the screen one line upward and waits for a new (more useful) input. 4.5.2 THE IF STATEMENT. It is rather boring just to let the computer repeat the input. It would be better to respond to the input. For example, have the computer respond to an entry of "1" with the message, "Thats very good", or an entry of "2" where the message could be "I am sorry to hear that!". PAGE 32 -------------------------------------------------------------------------- The computer must be capable of comparing the value stored in input with other numbers. depending on the results of this test, it must select one text or the other text. In programming this is known as an If..THEN construct. As in many other languages C also has this capability (C doesn't require the THEN). The variable is compared with 1. If the condition is found to be true, the statement following the IF command executes. Here is an example: if(input==1) printf("That is very good!\n"); if(input==2) printf("I am sorry to hear that!\n"); The conditions appear inside the parenthesis so that the first printf statement only occurs when input equals 1. The same is true of the following if statement, with the difference that the text executes if input equals 2. Semicolons never follow the IF. The user who wants to experiment can try a semicolon after the second if. If you do so, the program acts as if line if(input==2); doesn't exist. After each input "I am sorry to hear that!" appears. Adding the four lines above makes the program run properly, but it could be improved. Some users may be familiar with the BASIC statement: IF A = 1 THEN PRINT "THAT IS VERY GOOD!": ELSE PRINT "I AM SORRY TO HEAR THAT" ELSE :- The C language also has an ELSE statment which can only be used together with the IF statement. The ELSE statement executes only when the condition has not been met. This eliminates the second test: if(input==1) printf("That is very good!\n"); else printf("I am sorry to hear that!\n); The second test is now improved. A modified version of the C program now appears as follows; /* scan2.c 4.5.2 */ void main() { int input; printf("Are you Well?\n"); printf("(1) = Yes, (2) = No\n\n"); printf("Input Number: "); scanf("%d",&input); if(input == 1 ) printf("That is very good!\n"); elese printf("I am sorry to hear that!\n"); } PAGE 33 If several commands should be executed after the If (e.g. two printf functions), the second line cannot be written immediately after it. The following example wouldn't work: if(input==1) printf("That is very good!\n"); printf("Hope you stay healthy!\n); /* not like this */ else printf("I am sorry to hear that!\n"); STATEMENT BLOCK Since only one line is executed after the IF, something else must be done. Up to now only one statement has been described. It is time to describe a statement block. Placing several statements inside braces creates a statement block which is valid as a unit. This block can be placed following the If statement without problems; if(input==1) { printf("That is very good!\n"); printf("Hope you stay healthy!\n"); } else printf("I am sorry to hear that!\n"); The Amiga can do a lot of things, but what it does best is calculate (and fast). Let's start with addition. To add two values together use the plus sign (+): sum = number1 + number2 The result for this example is stored in the variable sum. This variable must be defined at the beginning of the function, just like all variables. Using type INT, the following definition results; int sum int number1 int number2 C is a language for lazy people. Most everything can be changed to abbreviations to cut down on keyboard use. Those who wnat to become good C programmers should use this capability. These three data values are defined as the same variable type. You only need to enter INT once, all integer variables can be listed following the single INT: int sum, number1, number2; All variables are separated by commas and the list is terminated with the semicolon. PAGE 34 With this information it is possible to write a program that adds two numbers. The SCANF function permits data input, but this time two numbers will be read in. This will not be done with two separate function calls (this would also be possible), but a second format specification is written into the string of the SCANF call. If contains %d%d (notice no spaces) which reads the second parameter. And now the listing: /* scan3.c .4.5.2 */ void main() { int sum, number1, number2; printf("Please input two numbers!\n"); scanf("d%d",&number1,&number2); sum = number1 + number2; printf("%d + %d = %d\n", number1, number2, sum); } The user familiar with other langauges such as BASIC can compare the listings to other language implementations of the same program. The last PRINTF function with the three format specifications can appear confusing. The following is a BASIC equivalent: PRINT "please input two numbers!" INPUT N1,N2 SUM = N1 + N2 PRINT N1;" + ";N2;" = ";SUM 4.5.3 CALCULATING WITH C The reader can guess what would have to be changed to perform multiplication instead of addition. The plus sign is replaced by an asterisk (*). A hyphen (-) is used for subtraction and a slash (/) performs division. Calculations may be performed with constant values as well as variables. Both constants and variables can be mixed. Some examples follow to illustrate. It is assumed that all variables are defined and contain meaningful values: result = number * 4; sum = var + 2 + var2 + 3 + var4; result = 4 * 5 - 7 / var; value = 2 * (number - 7); result = 4 + 5 * 3 - 2; counter = counter + 1; Evaluating a formula is as simple as entering it into a pocket calculator. C recognises the laws of mathematical precedence: PAGE 35 result = 4 + 5 * 3 - 2; The result is 17, not 25. The product of 5 * 3 is calculated first, after which 4 is added and two subtracted. The counter = counter + 1; example above deserves special attention. This equation is unsolvable for the normal person but no problem for the computer. It takes the content of the variable COUNTER, adds one to it and stored the result in counter. This operation increments the content of the variable by one for every call. Let's write a comprehensive program for performing math equations. The assumptions are that all basic four mathematical functions are performed with two variables. A SCANF function reads the numbers. Then the numbers are tested with several IF statements to determine which mathematical function should be performed. The result is calculated accordingly. If an invalid code is input, a message appears; /* math1.c 4.5.3 */ void main() { int number1, number2, result, operator, error; printf("Please input Two Numbers! \n"); scanf("%d%d",&number1,&number2); printf("And now the code for the Operation!\n"); printf("1=Add, 2=Subtract, 3=Multiply, 4=Divide\n"); scanf("%d", &operator); error = 1; if(operator==1) /* add */ { result = number1 + number2; error = 0; } if(operator==2) /* subtract */ { result = number1 - number2; error = 0; } if(operator==3) /* multiply */ { result = number1 * number2; error = 0; } if(operator==4) /* divide */ { result = number1 / number2; error = 0; } if(error==1) /* none of the above */ printf("Wrong Code! Input only numbers 1-4!\n"); else printf("The result is %d\n",result); } PAGE 36 The above program is very easy to read. After all the values have been read the variable ERROR is assigned the value of 1. During every operation that follows, be it addition, subtraction etc.. the ERROR value is set to zero. This makes it possible to determine whether one of the four operations was performed. If this was not true the value in OPERATOR is illegal. Before the output, the result it tested to determine if it was calculated. This can only be seen in the variable ERROR. Test this program thoroughly with various values. Please note that the integer variables are only permitted to store values between +32767 and -32768. Furthermore, division by zero should be avoided. This would cause a system crash and a Guru Meditation. 4.5.4 FLOATING POINT NUMBERS Perhaps you have noticed something else. Try dividing 9 by 2. The result displayed by the computer is 4, which is incorrect (4.5 would be right). Isn't this expensive computer capable of performing correct division?. The error can be traced from the variable type. The INT variable type is only capable of processing whole numbers between +- 32000. The value 4.5 is a floating point number not a whole number. If during a division sa remainder (the fraction after the decimal point) occurs, it is ignored. This does not mean that the division 9/2 cannot be performed on the Amiga computer. The only thing required is that the variable type can be capable of storing floating point numbers. No problem since C is equipped for this. To convert the current program for this new data type, the variables number1, number2 and result must be changed. This process consists only of replacing INT with FLOAT. The first lines appear as follows; main() { float number1, number2, result; int operator, error; } This alone is not sufficient since SCANF and PRINTF use the format specification %d which expects an integer value. This is no longer the case. The %d must be replaced with %f. The F means a floating point value is passed just like the d used for integer values. PAGE 37 The first SCANF function now appears as follows: scanf("%f%f",&number1,&number2); The last PRINTF function must also be changed. The variable RESULT is now a floating point value. The d is replaced with f. Newly compiled and linked, this version makes error free computations of floating point numbers possible. Numbers stored as type float are practically unlimited in size. Millions, and even billions and billions, can be calculated. Here is the complete program: /* math1.c 4.5.4 */ void main() { float number1, number2, result; int operator, error; printf("Please input Two Numbers! \n"); scanf("%f%f",&number1,&number2); printf("And now the code for the Operation!\n"); printf("1=Add, 2=Subtract, 3=Multiply, 4=Divide\n"); scanf("%d", &operator); error = 1; if(operator==1) /* add */ { result = number1 + number2; error = 0; } if(operator==2) /* subtract */ { result = number1 - number2; error = 0; } if(operator==3) /* multiply */ { result = number1 * number2; error = 0; } if(operator==4) /* divide */ { result = number1 / number2; error = 0; } if(error==1) /* none of the above */ printf("Wrong Code! Input only numbers 1-4!\n"); else printf("The result is %f\n",result); } LATTICE: The library for mathematical and floating point numbers must be linked with the standard library. Example : lc -Lm math2 PAGE 38 AZTEC: If you work with the Aztec C compiler, the library for mathematical functions and floating point numbers must be linked with the standard library c.lib. Example : cc +L math2.c ln math2.o -lm -lc 4.5 CHARACTERS AND CHARACTER STRINGS. Besides the INT and FLOAT variable types which accept numbers, you need another category of variable to store characters. It would be better if the program above could accept a plus sign instead of the number 1 to indicate addition. The data type CHAR allows variables to be defined which can accept characters. The syntax for definition of a CHAR variable is exactly as described in the FLOAT and INT variables; char character; This type of variable has it's own format specification for the PRINTF and SCANF functions. A c is used for type CHAR. To give the calculation program a few extras, the operator is entered as a character. This means that instead of entering a number as you had to before, you can enter a math operator instead. Another improvement can be made at this point. The format for entering equations should be similar to that of a pocket calculator (i.e first number, operator, second number). You should be able to press the or key instead of the <=> key. Since the SCANF function is flexible, the following change is sufficient to make this possible; scanf("%f%c%f", &number1, &operator, &number2); The %c between the two format specifications indicates character input. The tests which formerly checked the code now have to test the characters for the operators. Nothing easier than that! Only characters must be placed in apostrophes (single quotes): if(operator=='+') ....... PAGE 39 After all the small changes, compare this version to the final program below in which some other cosmetic changes were made. The reader should now be able to understand the additions made. During the inpput a small item has changed. Until now the key had to be pressed (but not required) after inputting each number. Now the entire input must be in one line. The reason for this is the fact that a single character is read in with %c. This could be a return or a space. For this reason the first number is followed immediately by the operator after which the key may be pressed, if desired. Finally the second numberr appears as in this line: 15.50000* 12.50000 = 193.750000 Here is the final version of the program : /* math3.c 4.5.5 */ void main() { float number1, number2, result; char operator; int error; printf("Input format : Number1, Operator, Number2 (No spaces)!\n"); scanf("%f%c%f",&number1,&operator,&number2); error = 1; if(operator=='+') /* addition */ { result = number1 + number2; error = 0; } if(operator=='-') /* subtraction */ { result = number1 - number2; error = 0; } if(operator=='*') /* Multiplication */ { result = number1 * number2; error = 0; } if(operator=='/') /* division */ { result = number1 / number2; error = 0; } if(error==1) /* none of the above */ printf("Wrong Operator %c !\n",operator); else printf("%f %c %f = %f\n",number1, operator, number2, result); } PAGE 40 LATTICE : The library for mathematical functions and floating point numbers must be linked to the standard library. Example : lc -Lm math3 AZTEC : If you work with the Aztec C compiler, the library for mathematical functions and floating point numbers must be linked with the standard library c.lib. For example : cc +L math3.c ln math3.o -lm -lc PAGE 41 ========================================================================== CHAPTER 5 - LOOPS. The programs presented up until now execute straight from beginning to end. An if statment may skip over some spots but we havn't jumped to earlier statements. Loops branch to earlier sections of the program. 5.1 WHILE LOOPS The while statement is followed by two parenthesis which surround the desired arguments. An example makes this clear; void main() { int counter; counter = 15; while(counter>0) { printf("counter is %d\n",counter); counter = counter - 1; } } The block which appears after the WHILE statement executes until the conditions inside the parenthesis are true. In the beginning, the variable COUNTER is set to 15. While the Condition COUNTER>0 has been met, the following block is executed which outputs the current value of COUNTER and then decrements it by one. In this case the PRINTF function is called 15 times until COUNTER has been reduced to zero. The conditional statements can be formed by using tests for equality (==), greater than (>), less than (<), greater than or equal to (>=), or less than or equal to (<=). The comparison here is for COUNTER to be greater than zero. The comparison operators for C are similar to those found in most programming languages. PAGE 45 < less than <= less than or equal to > greater than >= greater than or equal to == equal to != unequal to Instead of while(counter>0) the following could be written: while(counter>=1) The latter is preferred since the limit is explicitly provided. To construct a loop which counts up to to value 100 it is recommended to use this value in the test: while(counter<=100) In the comparison operators <= and >=, the equal sign (=) always appears at the end. FACTORIALS. A practical example is the calculation of a factorial number through constant multiplication. A factorial in mathematics is the multiplication of all whole numbers up to a set value. The factorial of 4 therefore is: 4! = 1 * 2 * 3 * 4 = 24 Here is our example program: /* factorial.c 5.1 */ void main() { int num, i; float factorial; printf("Please input a number : "); scanf("%d",&num); i = num; factorial = 1; /* initialise */ while(i>= 1) { factorial = factorial * i; i = i - 1; } prinf("%d! = %f\n",num,factorial); } PAGE 46 Now for a few hints in helping you compile this program with your compiler. LATTICE : The library for mathematical functions and floating point numbers must be linked with the standard library. Example: lc -Lm factorial AZTEC: If you work with the Aztec C compiler, the library for mathematical functions and floating point numbers must be linked with the standard library c.lib. Example: cc +L factorial.c ln factorial.o -lm -lc PAGE 47 -------------------------------------------------------------------------- 5.2 FOR LOOPS Another loop can be constructed using the FOR statement. In BASIC the command is used as follows; FOR I = 1 TO 100 STEP 2 ........ NEXT I In C this appears as follows: for(i=0; i<=100; i=i+2) ...... C doesn't require a NEXT as in BASIC, since only the statement block following the loop header is executed. Within the parenthesis are some interesting items. There are three individual statements separated from each other by semicolons. A semicolon does not follow the last entry. The loop body ends here with the closing parenthesis. The first entry i=0 assigns a starting value to the variable which is modified within the loop. The statement i<=100 represents the ending condition. Until it is satisfied the loop executes. The last part of the loop body increments or decrements the control variables which were previously initialised with a starting value. PAGE 48 -------------------------------------------------------------------------- 5.3 DO WHILE LOOPS The last type of loop is the DO..WHILE loop. The reader has already read about the WHILE loopl this loop is quite similar. Please compare the two program sections below: /* first program section */ while(i > 0) { i = i - 1; printf("i is %d\n",i); { /* second program section */ do { i = i - 1; printf("i is %d\n",i); } while(i>0); The DO begins the DO..WHILE loop, and WHILE ends the loop. This leads to a small but significant difference in program execution. In the first example the program checks if variable i is greater than 0 and then executes the loop only if the conditions are met. In the second example the test is executed only after the loop has already been executed once. If i contains the value zero, the WHILE loop is skipped, unlike the DO..WHILE loop which executes at least once. Please note the semicolon which must follow the WHILE. It is often forgotten since normal WHILE loops don't use semicolons. If this material is not clear without further example programs, the user is encouraged to write some short programs (e.g. which output the values of the variables used). 5.3.1 MORE ERROR CHECKING. The next section deals with error detection. Up to now it was difficult to make mistakes, except for error in typing. When the program suddenly reports errors, there's no need to panic. Study the message the C compiler returns. You may have to do a little thinking to detect cleverly hidden errors. PAGE 49 Below is a new program. Based on what you know so far you should be able to determine where the errors are hidden, and which lines could cause problems. The listing contains errors which result in a long series of error messages. Try to find the hidden errors on your own first. Fix these errors then try compiling the source code to see what you missed. We've included the solution directly after the listing. The program should add all numbers from 1 to 100 and display the sub-totals and final total on the screen: main(); { printf("I add all numbers from 1 to 100/n"); i=1; do printf("Subtotal for %d. Value : %d/n",i,sum); sum = sum + 1; i = i + 1; while(i < 100) printf("Sum of all numbers to 100 is %d/n",sum); } Did you find all the errors? You should have found most of them since the program is almost completely wrong! Even if you found no errors you can follow the remaining material without problems. Let's start with the first line which contains an error (of course). The semicolon following MAIN shouldn't be there. The missing VOID only results in a warning not an error. The next line with the brace is correct (an exception in this program). The compiler accepts the first printf function without problems. It doesn't contain a syntax error. The line would even be right if you wanted to display the slash (/) and an N. The slash (/) should have been a backslash (\). To be consistent this error occured in all the PRINTF functions in this program. THe assignment i=1; is correct. The DO..WHILE loop which should execute the following three lines, has no braces (the braces make the three lines into a statement block). The semicolon is also missing after the WHILE( i<100) line. Within the loop the values for i and SUM should be displayed. Except for the error with the /n everything is correct here. Finally the program increments the contents of sum and i are incremented (NOTE :- the bad formation of this text is in the original and I have just copied it exactly as in the book - RAZOR BLADE.). Trouble is, the SUM variable was never defined. The C compiler doesn't know what is meant by i and sum. A line must be added before the PRINTF function: int sum, i; Before or after i=1; a sum=0; must be added. After these changes the C compiler is happy but the program will not display the right output. There are two logical errors in the program. PAGE 50 The first occurs as the sub-total is displayed. The value of SUM is displayed before it has been calculated. The line sum = sum + 1 must be placed before the line with : printf("sub-total for %d. Value : %d\n",i, sum); The WHILE test remains which terminates the program after the number 99. The change is simple: } while(i <= 100); The error free version of the program appears below; /* errorfree.c 5.3.1 */ void main() { int sum, i; printf("I add all numbers from 1 to 100\n"); sum = 0; i = 1; do { sum = sum + i; printf("Subtotal for %d. Value : %d\n", i,sum); i = i +1; } while( i<=100 ) printf("Sum of all numbers to 100 is %d\n", sum); } PAGE 51 -------------------------------------------------------------------------- 5.4 AND and OR Up to this point only one condition can be check in your loop. This changes with the introduction of the && and || operators. The <|> key can be found on the right side of the keyboard above the key. The && represents the logical AND and the || the logical OR. Why these operators are called logical will be revealed later since other logical operators also exist in C. From BASIC the commands AND and OR are familiar and they are the same as the operators in C. AND: Connecting two conditions with AND: while(i <= 10 && i >= 5) ....... The loop is now executed when . i is less than or equal to 10 . i is greater than or equal to 5 If one of the two criteria is not met (e.g i=4 , the entire condition is false and therefore not satisfied. Only if both tests are true can the loop be executed. OR: Or is used as it is in daily conversation. If one of the two conditions is true the entire expression is true. The next example assumes that a character variable should be tested for a certain comment. Since the logical connections can be used with other conditional tests, they can be used together with IF: if(operator=='+' || operator=='-' || operator=='*' || operator =='/') printf("The operator is valid!\n"); Four tests were made of which only one must be true. If several tests can be positive then this is no problem since only one true condition is sufficient. That the IF statement could be written in two lines should be nothing new. Rememeber that the formatting of the C listing is of no interest to the C compiler. NEGATION: There is another operator to be discussed. This is the negation operator !, mentioned as part of the inequality operator !=. With this character all tests and returns can be made into the opposite. If a test should be made to determine if a character is not an arithmetic operator, the following test can be devised: PAGE 52 if !(operator=='+' || operator=='-' || operator=='*' || operator =='/') printf("Not a valid operator!\n"); All tests are made within the parenthesis. If the expression inside the parenthesis is true, a valid character is present, the negation operator goes into action. It simply reverses the matter. From the true test it makes a false one so that the PRINTF command is not executed. This is similar to the false test result within the parenthesis, when none of the signs +-*/ are stored in the variable. In this case the ! operator makes it a true test. That is the same procedure as inserting a NOT into a sentence. In everyday English double negatives can be used in one sentence, but not many people will understand it. PAGE 53 ========================================================================== CHAPTER 6 - STRINGS. You've entered strings and displayed them on the screen in previous chapters. What else can you do with them? The following code shows string variable definition. char name[number_of_fields]; Strings consist of groups of individual characters of type CHAR. The above variable definition tells the compiler the maximum number of characters the string can have. If you want to process a single character belonging to the string, you can't just call the variable - you'll get the entire string. In addition, you must know the exact location in the string at which you can find the specific character. You'd enter the number of the character in brackets, just as you did in the definition. Let's take the first character in a string. This first character appears in the first position of the string, and is assigned position 0 (computers always start counting with zero). All locations then shift by one. The second character can be reached using the value 2, due to the index, which acts as a position indicator. Every position contains a character. All characters are arranged sequentially in a large or small string. 6.1 BACKTRACKING. Let's write a program which displays the text backwards on the screen. Before starting you must assume that a string can be any length. The string will always end with the value 0 (null). The last character of the string must be processed first if you want the text displayed backwards. The program needs a small FOR loop to find the last character of the string (0) : ..... char input[80]; for(index=0;input[index] !=0; index = index + 1) ; ...... The loop body (the statements executed during every passs through the loop) is empty. A single semicolon follows the FOR loop. Since a block of statements follow every loop, this semicolon ends a block that does nothing. PAGE 57 This is the empty statement. The data in parenthesis perform all of the required operations. First the index is set to 0. Then the test follows which determines whether a character is not equal to 0. If the condition is satisfied to index is increased by one. The last element of the string contains a zero and the loop ends. The result in index is the length of the string. The last character of the string is located one position before the null value. Therefore the index variable must be reduced by one before being used. The index counts down to zero one step at a time and the program displays a character at every step. do { index = index - 1; printf("%c",input[index]); } while(index > 0); You now have the information you need to write the entire program. One other item before you enter and compile the program; If you have the Aztec C compiler, this program will not compile using the +L (longwords) option. Omit this option when compiling the program with Aztec C. Here's the source code : /* backwards 6.1 */ void main() { char input[81]; int index; printf("Please input some text!\n"); scanf("%s",input); /* Strings do not require & */ for(index = 0; input[index] !=0; index = index + 1) ; /* search for end mark */ printf("Your input >%s< has %d characters \n",input, index); do { index = index - 1; printf("%c",input[index]); }while(index > 0); printf("\n\n"); /* Blank lines before prompt */ PAGE 58 ========================================================================== CHAPTER 7 - CALCULATING IN C You have already seen how fast the Amiga computes; addition(+), subtraction(-), multiplication(*) and division(/) are familiar to you. The modulo operator (%) performs another mathematical operation - modulo division, which calculates the remainder of an integer division. THe result is assigned to a variable using the equal sign, where the variable must be to the left of the assignment operator. The general format is as follows: variable = operand1 <> operand2 (<> represents the operator) Frist the program calculates the expression to the right of the equal sign and places the result in the variable to the left of the equal sign. Because of this statements such as the following are possible: variable = variable + 1; The expression is impossible in normal math, but poses no problem for the computer. The computer reads the variable content, adds one and stores the result in the same variable. Combinations of math operators are possible as the following examples show: number = 3 * 32 number = 2 + 6 * 7 number = 5 * (180 / 3 + 9 ) * ( 5 - 2) number = number - 1 number = number % 2 PRECEDENCE. Division and multiplication have precedence over addition and subtraction. This rule is also observed by the C compiler. Therefore, the expression 2 + 6 * 7 needs no parenthesis to achieve the correct result. The modulo operator has precedence equal to division and therefore precedence over addition and subtraction. Example: 5 % 3 = 2 since 5/3 = 1 and remainder is 2. Simple calculations don't need to use a variable. The following program will illustrate: void main() { int number; number = 3 * 12; printf("Result : %d\n",number); } PAGE 61 -------------------------------------------------------------------------- A variable does not have to be used in the program since the %d characters tell printf that it can expect an integer value. The term 3 * 21 can be passed directly to the function as a parameter. The calculation of the result occurs before the result is passed so no variable is required. The following program is faster and shorter: main() { printf("Result: %d\n",3*12); } An integer number can only contain a whole number, so the statement 3 / 2 places a value of one in the variable NUMBER. The correct result would have been 1.5, but the result will be rounded to the next whole number. NOTE :- The number -2.25 is rounded to -2 and not -3 since -2 is larger than -3. Lattice C rounds numbers toward 0 but this can differ with other C compilers. Only a text run helps to explain what happens to -5 / 2 in which either -2 (toward zero) or -3 (rounded) appears as a result. PAGE 62 ========================================================================== CHAPTER 7 - CALCULATING IN C You have already seen how fast the Amiga computes; addition(+), subtraction(-), multiplication(*) and division(/) are familiar to you. The modulo operator (%) performs another mathematical operation - modulo division, which calculates the remainder of an integer division. THe result is assigned to a variable using the equal sign, where the variable must be to the left of the assignment operator. The general format is as follows: variable = operand1 <> operand2 (<> represents the operator) Frist the program calculates the expression to the right of the equal sign and places the result in the variable to the left of the equal sign. Because of this statements such as the following are possible: variable = variable + 1; The expression is impossible in normal math, but poses no problem for the computer. The computer reads the variable content, adds one and stores the result in the same variable. Combinations of math operators are possible as the following examples show: number = 3 * 32 number = 2 + 6 * 7 number = 5 * (180 / 3 + 9 ) * ( 5 - 2) number = number - 1 number = number % 2 PRECEDENCE. Division and multiplication have precedence over addition and subtraction. This rule is also observed by the C compiler. Therefore, the expression 2 + 6 * 7 needs no parenthesis to achieve the correct result. The modulo operator has precedence equal to division and therefore precedence over addition and subtraction. Example: 5 % 3 = 2 since 5/3 = 1 and remainder is 2. Simple calculations don't need to use a variable. The following program will illustrate: void main() { int number; number = 3 * 12; printf("Result : %d\n",number); } PAGE 61 -------------------------------------------------------------------------- A variable does not have to be used in the program since the %d characters tell printf that it can expect an integer value. The term 3 * 21 can be passed directly to the function as a parameter. The calculation of the result occurs before the result is passed so no variable is required. The following program is faster and shorter: main() { printf("Result: %d\n",3*12); } An integer number can only contain a whole number, so the statement 3 / 2 places a value of one in the variable NUMBER. The correct result would have been 1.5, but the result will be rounded to the next whole number. NOTE :- The number -2.25 is rounded to -2 and not -3 since -2 is larger than -3. Lattice C rounds numbers toward 0 but this can differ with other C compilers. Only a text run helps to explain what happens to -5 / 2 in which either -2 (toward zero) or -3 (rounded) appears as a result. PAGE 62 ========================================================================== CHAPTER 8 - VARIABLES. The earlier chapters used variables. These are areas of memory used for storing mathematical results, as well as different kinds of data. Variables are subject to certain rules and regulations. They must be assigned specific data types and unique names. As you'll see in this chapter, there are many types, and the names can be almost anything you want them to be. 8.1 VARIABLE NAMES. The names given to variables must follow some rules. The following is a list which describes these rules: 1.) The first character must be a letter (the underscore _ counts as a letter. After the first character any legal character can be used. 2.) Characters within variable names can be letters, numbers or underscore characters (the shifted minus sign). 3.) No control characters or foreign characters allowed. 4.) Variable names can be of any length, but many C language compilers only use the first 8 characters of the variable name. (The Lattice C compilers permits up to 30 characters, Aztec up to 31.) 5.) Reserved C keywords may not be used as variable names. 6.) Variable names are case sensitive. (i.e. The compiler sees a difference between upper and lowercase). Some examples are shown to make these rules more understandable. Some are correct some aren't. Can you find the bad ones? PAGE 65 -------------------------------------------------------------------------- a - Number_1 b - 2_pi c - first-var d - Book_No_1 e - Book_No_2 f - int g - _flag h - int_valu i - number_1 j - secret_password The following variable names are correct: a), d), e), g), h), i) and j). It should be noted that a) and i) are different values since upper and lower case letters are differentiated. d) and e) may refer to the same value on some C compilers, since the names are the same for the first 8 letters. Errors would occur on some C compilers since these variable names are longer than eight characters: c), d), e) and j). h) uses a C keyword as a variable name, but this is permitted since the rest of it doesn't match the keyword. Look at examples b), c) and f). The variable in b) starts with a number (not allowed). A hyphen appears in c) (the hyphen is considered a special character). Finally f) uses a variable name which is a reserved keyword of C. The following list shows the reserved words used in C: auto enum short break extern sizeof case float static char for struct continue goto switch default if typedef do int union double long unsigned else register void entry return while PAGE 66 -------------------------------------------------------------------------- 8.2 DATA TYPES. Until now three data types have been described: INT, FLOAT and CHAR (strings). INT : Integer values are type INT. The 16-bit INT type represents whole numbers between -32768 and +32767. The INT type works well for general use. The Amiga libraries use 32-bit integers, but for portability of your source code to other computers you may want to use 16-bit integers. FLOAT: Floating point numbers are assigned the data type FLOAT. A FLOAT variable can store extremely large or small numbers, and numbers with decimal places. In this type of variable the value can be represented in scientific notation. Very small numbers such as 0.000000015 can be written as 15E-7 (the use of E is an abbreviation). 15E-7 is scientific notation for: 15 * 10 (to the power of) -7 Numbers with as many as 15 places can be written. The "e" which separates the exponent (here-7) from the mantissa (in this case 15), can be written in upper or lowercase letters. In both cases the compiler will translate it without problems. Even the FLOAT variable type has it's limitations. The largest number permitted is 10**38. Any number less than 10**-38 converts to a 0. The value 10**-40 when written out is a number which has a decimal point, 39 zeros and a one, in that order. The computer views it as 0. Floating point variable remain accurate up to seven decimal places. Try the MATH.C program from section 4.5.5; enter the number 16.8. The program converts the number to 16.799999. DOUBLE: If you need more accuracy use the DOUBLE variable type. Variable of type DOUBLE are about twice as accurate (11-14 decimal places). However, DOUBLE variable require more memory. There are times when FLOAT and DOUBLE variables don't have the accuracy of integer values. On the other hand, rounding numbers off can cause incorrect results in multiple calculations. The result becomes more inaccurate with every additional operation. Avoid comparing a fixed value during a test. For example: if(value==1.0) /* not like this */ PAGE 67 -------------------------------------------------------------------------- It would be better to test if it is larger or smaller so that the value tested is not skipped through a rounding error. Otherwise an infinite loop could result. Special conditions must be considered when using floating point numbers. To determine if a constant without fractions following the decimal point (for example 2) is a floating point number another digit must be added after the decimal point. This error occurs during the PRINTF call in the following example: printf("Result of 2 / 3 = %f.\n", 2/3); The example computes 2/3 as an integer value and passes 0 as a result. The function waits for a floating point number which was indicated by %f. The example below is the correct version printf("Result of 2 / 3 = %f.\n",2.0/3.0); NOTE: You may crash the system as well as get the wrong answer with the example above. If you want to try to two examples above, save any important data that you might have in RAM disk to a floppy disk before continuing. CHAR: Other data types can be derived from the basic types INT and FLOAT. For example, the type char which can accept a character is really a variable for whole numbers between -128 and 127. This small relative of INT respresents the ASCII values of the characters. LONG: The LONG type is another type derived from INT. Long accepts integers between -2,147,483,648 and 2,147,483,647. If you must define contants as long values, place an l or L behind the floating point number instead of .0. For example: 1l SHORT: The C keywords UNSIGNED and SHORT can be used a adjectives to the basic types. These specify integer values. The combinations of unsigned and short cannot be used with FLOAT values. UNSIGNED defines an integer number which has no sign; SHORT accepts only 16-bit numbers. If the indication of INT or FLOAT is missing, C defaults to INT. The following combinations are valid: unsigned = unsigned int short = short int = [compiler dependant] char long = long int unsigned long int long float = double PAGE 68 The advantage of UNSIGNED is limited to positive values and extends the limit of the normal type. For example, UNSIGNED INT, accepts numbers between 0 and 65535 . The normal INT type only allows positive values up to 32767. Unsigned numbers also allow operations which cannot be performed with other data types. More on this later. The value assignments to CHAR variables proceeds in the following manner, as in INT values; char character; char = 65; ChAR stores characters. This is done with the following assignment which leads to the same result as the line above. character = 'a'; The combination of integer value with the attributes SHORT, LONG and UNSIGNED returns different results from one compiler to another. For this reason no general value or memory reuqirement can be provided. The following relaionship exists between the length of variables used by all C compilers. char <= short <= int <= long. The table below describes the length of the different numeric variable types. Lattice Aztec. char 1 byte 1 byte short 2 bytes 2 bytes int 4 bytes 2 bytes long 4 bytes 4 bytes PAGE 69 -------------------------------------------------------------------------- 8.3 TYPE CONVERSION. Type conversion sometimes becomes necessary during computation because of the use of various data types. The following rules govern type conversion; 1.) CHAR and SHORT always convert to INT; FLOAT always converts to DOUBLE. 2.) If, after these conversions, one of the operators should have the type DOUBLE, the second operand and the result also convert to DOUBLE. 3.) If a data type is now LONG, all participating values also convert to LONG. 4.) If an UNSIGNED value exists among the operands, all values convert to UNSIGNED. PAGE 70 -------------------------------------------------------------------------- 8.4 THE CAST OPERATOR. Constants, function values and variables can be converted into a specific data type. The parameter to be converted is placed in parenthesis and is preceeded by a data type in parenthesis. This is the CAST operator. The parenthesis are not always required, but are recommended because of the high precedence of the type conversion. For example; LONG number; number = 123 / (long)('a' / 1.5); In a general format, the expression is: type (parameter) Any data type can be substituted for the word TYPE. PAGE 71 ========================================================================== CHAPTER 9 - PRINTF AND SCANF. The most powerful output function in C is PRINTF. Youve seen a little of what it can do with screen output using the examples printed earlier in this book. The SCANF function gives the user the option of input to the computer. You have had a chance to work with this option also. Both SCANF and PRINTF use a number of format specifications and escape sequences for controlling the format and type of input and output. 9.1 MORE ESCAPE SEQEUNCES. You'll remember reading about the \n escape seqeunce in chapter 3. The following list shows other escape sequences, which youll find useful for controlling text output. \t places output at next tab stop. \b moves current write position one place to the left. \r inserts carriage return at first position of the current line. \n inserts carriage return and linefeed. \f inserts form feed. \\ prints the backslash character. \" prints quotation mark within the string. \' prints apostrophe within the string. \nn prints any character with the octal value NN. NOTE :- An escape sequence uses two characters in the text, but represents only one character. Keep this in mind when calculating memory usage. The following program uses the \t escape sequence for tab stops: main() { printf("An\tExample,\ttwo,\ttabs,\tspacing"); printf("\tSpacing\tText!\n"); } For our next assignment we wish to display the following text : Use the control characters : "\n", "\t"! PAGE 75 -------------------------------------------------------------------------- It is not possible to simply place the text in quotation marks since they already occur in the text. Escape sequences are necessary. The program prints the quotation mark using the \" escape sequence, the backslash with \\. The necessary PrintF call appears as follows: printf("Use the Control Characters : \"\\n\",\"\\t\"!"); Here is an example Program : main() { printf("small"); printf"\"T e s t P r o g r a m "); printf("\"\n\nWhere\nis the \ntext now?\n"); printf("\t Everything OK ? \n"); } The output is : Small "T e s t P r o g r a m " Where is the text now? Everything OK ? PAGE 76 ------------------------------------------------------------------------- 9.2 FORMAT SPECIFICATION. Strings can accomodate format specifications as well as text and escape sequences. Every format specification has a corresponding varaible attached to the string and separated from the string by a comma. The format specification always starts with a percent character (%) and can be used in the printf and scanf functions. A difference from the format string of the printf function is important. The scanf function reads in data. For this reason the escape sequences \n (linefeed), \t (tab) and space divide the input into separate fields. Here is a table with format specifications for printf and scanf: Format Specification Data Type. -------------------- ---------------------------------------- %c Char (one character) %d integer value. %s string. %f Float and double. %o Integer value as octal (base 8) %x Integer value as hex (base 16) %u Unsigned integer value (printf only) %e Float or Double (printf only) in scientific notation. %g shortest form of %e and %f (printf only) %h short (scanf only) %% represents the % character (printf only). ADDITIONS :- The integer elements d,u,o and x can be preceeded by the letter 1 to indicate that long values instead of integers are used. Long values are integers of double length. An L preceeding floating point numbers containing e,f and g indicates that double values are expected. The field width of the input or output of a field can also be indicated with a format element. After the percent sign, the size of the individual field can be indicated. If the first character of this number is a minus sign , the text is left justified. Spaces fill the remaining positions in the field. Without indication of the field width, the standard setting for %f in the printf function is %.6f. The output therefore always has six decimal places and any field size. PRINTF and and elements for FLOAT values: %.F PAGE 77 ------------------------------------------------------------------------- indicates the minimum width of the output field. is the maximum number of numbers after the decimal point. F is one of the format specifications e,f or g. The indication of 0 for fractional positions truncates all numbers after the decimal point (e.g., %.0f). For example : printf("Number %5.21f\n", 12.345); creates the output: Number 12.35 In this case the program expects a DOUBLE number (1f) at least 5 characters wide, but with only two decimal places. Since rounding is performed to the second number after the decimal point, the number 5 appears at the last position. If less numbers are available than the number indicated for the positions after the decimal point, zeros are attached. Printf and elements for integers: %F indicates the minimum width of the output field. F represents one of the format instructions d,u,o or x. Example : printf(">%4d<",12); Output : > 12< printf & %s: %s indicates the minimum width of the output field, while describes the actual number of characters displayed. The following examples show the effects on the string "SampleText": Format Specification Output -------------------- ------------------------------- >%6s< >sampletext< >%-6s< >sampletext< >%12s< > sampletext< >%-12s< >sampletext < >%12.6s< > sample< >%-12.6s< >sample < >%.6s< >sample< The SCANF function is much simpler. Only one number exists which indicates the maximum input length possible. As soon as a character no longer fits into the format of a data type, or a control or balnk character appears, the input for the current field ends. This means that during input only characters representing an integer number are used. PAGE 78 ----------------------------------------------------------------------------- If other characters are input, the integer number input ends. In addition "*" can be used which preceeds the instruction for data type and suppresses the assignment. The field is simply skipped in this case. int i; float f; char string[50]; scanf("%3d %f %*d %s", &i, &f, string); Input : 1234567.89 12345all clear? Value Assignment : i contains 123 since the field should have 3 places and only numbers can appear. The value 4567.89 is in f, because the space after "9" prevents additional reading of input. The same happens after the storage of "all" in the string[]. The number sequence 12345. which normally is assigned to an integer value, was skipped because of the asterisk. The means that with the SCANF no spaces can be read. This makes SCANF less than ideal for string input. If the reader cant remember all of this material, dont worry. It is used intensively during the course of the format specifications. PAGE 79 ----------------------------------------------------------------------------- 9.3 OCTAL AND HECADECIMAL. Two number systems are often used in C. To discuss these systems well start by looking at the decimal system. Lets take a decimal number and disect it into its component parts :- 5279 = = 5000 + 200 + 70 +9 = 5*1000+ 2*100 + 7*10 +9 = 5* 10**3 + 2 * 10**2 + 7 * 10**2 + 9 * 10**0 This make the origin of the term decimal = 10 in our number system clear. Every number position has a certain value. There are ones, tens, hundreds etc.. The value of these positions is multiplied with the number at that location. For example 7 * 10. The factors 1, 10, 100, 1000 can be traced again to the base 10. The exponent in the base 10 depends on the position of the digit in the number. The first position corresponds to exponent 0, the second exponent 1, the third 2 and so on.. Numbers between 0 to 9 can be used, which makes 10 different numbers available. This is the reason the system is called base 10. OCTAL SYSTEM : If you used 8 different numbers (0-7) instead of ten the base in the calculation would be 8. This base 8 system is better known as the octal system. The follwing example shows the process of calculation of a number in the octal system. To differentiate the number systems, the base number appears in parenthesis following the number: 6204 = 6 * 8**3 + 2 * 8**2 + 0 * 8**1 + 4 * 8**0 = 6 * 512 + 2 * 64 + 0 * 8 + 4 * 1 = 3072 + 128 + 0 + 4 = 3204 (10) This brings us back to format specifications. To write a variable in octal on the screen use %o. printf("3204 dec = %o octal\n",3204); PAGE 80 ---------------------------------------------------------------------------- HEXADECIMAL The format specification %x converts a number into the hexadecimal system. As the name suggests, hexadecimal is base 16. This produces a small problem. The decimal system uses numbers (0-9), but hexadecimal system need 16. Hexadecimal uses the first 6 numbers of the alphabet as the top six numbers. The letter A represents the number 10; B has the value 11; C 12; D 13; E 14 and F 15. The following hexadecimal number can be converted as follows: 5DA9 = 5 * 16**3 + 13 * 16**2 + 10 * 16**1 + 9 * 16**0 = 5 * 4096 + 13 * 256 + 10 * 16 + 9 * 1 = 20,480 + 3,328 + 160 + 9 = 23,997 (10). Hexadecimal and octal numbers can be used in C exactly like decimal numbers. Hex numbers use the format specification 0X to indicate that they are base 16. For the octal system only a leading zero is required. Some examples : 0x5DA9 0xFFFF 0612 0x5da9 0x123 0815 0X5da9 06543 One of the above combinations is wrong. Example the numbers again carefully. The error is hidden in the innocent number combination 0815. With the leading zero it should represent an octal number. There is no digit with the value 8 in the octal system. The response depends on the compiler. The compiler can issue a message that a wrong number was entered, or accept it as a decimal number. The lattice C compiler converts the number from octal into decimal notation. This produces something entirely different, namely 525(10) which is equal to 1015(8). The user would soon get tired of entering every number for conversion. A good C implementation does that for you. To write a program to convert numbers from various bases into the decimal system the procedure must differ slightly. Nothing is simpler than constructing a loop to save typing time. Starting with the last position ( 9 in the example), multiply it with the value of the position. The value at the last position is then 9 * 1 = 9. The next position has the value 16 , and the variable containing this value with the base (16). The next position is therefore 10 * 16 = 160. PAGE 81 -------------------------------------------------------------------------- All computed intermediate results are added in a separate sum. This is the same path as the manual procedure with the exception that every step again is divided into smaller steps. The user doesnt have to understand how the calculation works, since you are trying to learn C and not mathematics. 9.3.1 CONVERSION PROGRAM. Analyse the following listing in the basis of the explanations already provided. If it isnt clear a few PRINTF functions inserted in the program could print out the current value of one or more variables. This makes the most important variables visible. /* base-con.c 9.3.1 */ viod main() { long base,collect,value; int index,help; char test[100]; printf("Please input Base of numbering system!\n"); scanf("%ld", &base); printf("Input number for conversion in base %ld system!\n",base); scanf("%80s",test); collect = 0; value = 1; index = strlen(test) - 1; /* new function */ while (index >= 0) { help = test[index]; if(help >= 'a') /* lowercase letter */ help = help - 'a' + 10; else if (help >= 'A') /* upper case letter */ help = help - 'A' + 10; else /* probably a number */ help = help - '0'; collect = collect + value * help; index = index - 1; value = value * base; } printf("%s(%ld) = %ld(10)\n", test, base, collect); } The program uses the LONG data type a lot. This can also be noted in the format specification %ld for the input and output of these variables. The scanf function which reads a number as a string has something new. After the percent sign appears an 80, followed by the format specification %s for string. This value between the percent sign and the format specification tells the function the maximum number of characters permitted. PAGE 82 --------------------------------------------------------------------------- In this case the string cannot be longer than 80 characters (+1 end of line = 81). In reality this does not work out quite that way. As a maximum only 80 characters are processed, but the user can write several lines. Only the first 80 characters are used. The indication if a maximum number of places is also permitted for other data types (see the example in SCANF). A new function STRLEN will be introduced next. It delivers the characters in a string. The concluding zero byte is not included. The result is assigned with an equal sign. The only parameter required by STRLEN is the string to be investigated. One of the examples already calculated the length of the string. The strlen function is therfore not very large. In the following WHILE loop the string which was input is processed. To avoid the use of the expression test[index] for every calculation, the character at that location is copied into the variable HELP. The user should have noticed that help was defined as an integer variable. Yet an attempt is made to store a character at that location. Computers view characters as numbers. Every letter has a numeric code, just like a number. The CHAR variables are nothing more than small integer memory areas which, depending on the compiler, accept a value between -128 to 127, or , from 0 to 255. These peculiarities of calculating numbers with characters and their codes will be discussed later. PAGE 83 --------------------------------------------------------------------------- 9.4 CHARACTER CODES. After the character was made available in HELP, it is tested to see wether it was an uppercase or lowercase letter. These can be used as auxillary numbers in a system whose base is 10. In the haxadecimal systems the letters A-F are used. When it has been determined what type of character (uppercase, lowercase or number)is available its actual value is calculated. 9 is not 9 here. Confused ? You remember how a character was assigned to the CHAR variable: character = '9'; The 9 is a character which represents the number 9. This character also has a special ASCII character code. This ASCII code for the number 9 is the value 57. The assignment that follows gives the same result as the example above : character = 57; Caclulation with the variable requires the value 9 and not the stored code 57. First subtract 48 from 57 (48 is the character code for 0). This is practical since all numbers are in sequential order with the following codes : CODE CHARACTER ---------- ---------- 48 0 49 1 50 2 51 3 .. .. .. .. 57 9 The letters of the alphabet also follow this order. The table starts with A (code 65), B (code 66) etc.. The lowercase letters are in a separate list. The first value there is 97 for 'a'. Lets look at a program section : char test; test = 'B'; test = test - 'A' + 10; What is contained in the value TEST after the execution of this sequence ? An equivalent part appears in the conversion program. The right result is 11. In the last line, test contains the letter B with the value 66. Subtracting A from this results in 1; plus 10 is 11. That is the value that the letter B represents in the hexadecimal system. PAGE 84 --------------------------------------------------------------------------- The following line converts an uppercase letter into a lowercase letter : test = test - 'A' + 'a'; That is much more readable than : test = test - 65 + 97; or test = test + 32; The codes are nearly identical on almost all computers thanks to the ASCII standard. ASCII assigns a specific code to each character. To obtain an overview of the ASCII codes, the program below displays every code and its character (32-127, 160-255). The codes 0-31 and 128-159 were left out because they either have special functions (e.g. 13 is equal to \n) or do not produce anything on the screen. /* ASCII.c 9.4 */ main() { int i; printf("\n\n"); for(i=32; i<=127; i = i + 1) printf("\t%-3d %c",i,i); for(i=160; i<=255; i = i + 1) printf("%-3d %c",i ,i); printf("\n"); } These are all of the characters that can be printed with the PRINTF function. 9.4.1 ABOUT THE BACKSLASH. Output using a character code displays a character which cannot be accessed with the keyboard or with the backslash. The backslash must preceed the code. The compiler replaces the combination of backslash and the individual digits of the code with a single character. One hitch - the number must be entered in octal notation instead of decimal notation. The following command displays the +/- character (character code 177): printf("\261"); The number 177 decimal corresponds to 261 octal. The conversion can be avoided using a format specification as shown below: PAGE 85 --------------------------------------------------------------------------- printf("%c", 177); This character is not used directly in the string, but goes directly to the printf function in the form of a character code, with the %c. printf("The result is \261.\n"); printf("The result is %c.\n", 177); The control character %c permits the output of a single character by indicating the character code, even if an integer value was passed. 9.4.2 GOING THE OTHER DIRECTION. The following program converts decimal numbers into octal numbers. It is almost the reverse of the previous conversion program which converted numbers into the decimal system. /* dec_conv.c 9.4.2 */ void main() { long base,test,help,rest; int index; char result[260]; printf("Please input number Base!\n"); scanf("%ld",&base); printf("Input number in decimal system!"); scanf("%ld", &test); index = 0; for (rest = test, rest >= 0; rest = rest / base) { help = rest % base; /* remainder of division */ if(help > 9) result[index] = help + 'A' - 10; else result[index] = help + '0'; index = index + 1; } printf("%ld(10) = ",test); index = index - 1; /* last entry is still unused. */ while(index>=0) { printf("%c",resut[index]); index = index - 1; } printf("(%ld)\n"), base); } PAGE 86 -------------------------------------------------------------------------- CHAPTER 10 - THE PREPROCESSOR. The preprocessor is part of the compiler program which first processes the source code. It accepts source text as written. There are some special directives which force the preprocessor to make changes in the program source text. After the preprocessor has done its work, the part of the compiler responsible for the translation uses this 'processed' version of the source code text. This version can appear quite different from the listing. To differentiate the pre-processor directives from other C statements and functions there are two important guidelines: 1.) All directives begin with the # character. 2.) All directives begin in the first column. 10.1 DEFINE Lets first consider the most important and most used preprocessor directive; #DEFINE. #DEFINE replaces a certain character string with another string. The preprocessor exchanges the two text strings. Lets think about what the text replacement could be used for. Assume that a constant is used during calcualtions. For example during the calculation of a sales tax, a certain percentage (4% perhaps) appears regularly. If this percentage is used 10 to 20 times in a program and the sales tax percentage changes, a change in the program can become difficult. It can also lead to errors. Perhaps a wrong value would be returned if the number 4 appeared elsewhere in the program. An entry can be missed during the changes. It is simpler to use the #Define directive. An application would appear as follows: #define TAX 4 Up to this line the text TAX can be used which is then replaced by the preprocessor with the text 4. Also the following line could be used : printf("TAX-rate %d",TAX); The preprocessor passes to the compiler the following substitute line: printf("TAX-rate %d",4); PAGE 89 -------------------------------------------------------------------------- Nothing has changed within the parenthesis. This is good since it would be impossible to output a string such as TAX on the screen. Nothing inside the quotation marks can be touched by the preprocessor. Defines are always used in a large program. The following program explains the usage of #defines. What the program produces can be seen readily in the listing : /* DEFINE.c 10 */ #define BEGIN 1 #define END 100 #define STEPS 2 void main() { int i; printf("\n"); for(i=BEGIN; i<=END; i = i + STEPS) printf("%5d", i); for(i=END; i >= BEGIN;i = i - STEPS) printf("%5d",i); printf("\n"); { Even in a small program the DEFINE directives can enhance the readability of a program. An example is marking the end of a string with a null byte. This null byte is also called end of string. With the abbreviation EOS its an often used define. The definition appears as follows : #define EOS '\0' That is more correct than simply indicating a 0. The entries of a string are considered individual characters. It is therefore good C style to use data type assignments. The single quote mark informs the compiler that a single character is used. The backslash followed by the octal value indicates the character code (see section 9.3). The number zero in the octal system, the decimal system and any other systems is always zero. A conversion in this case isnt difficult. Using the character with the code zero, or the code directly (zero) in the assignment is of no consequence. In future programs which use strings, the definition of EOS should appear in one of the first lines. If the reader thinks that the subject of #define is now finished, he is wrong. The many capabilities which are provided with the #define directive will be discussed in more detail in a separate chapter. PAGE 90 -------------------------------------------------------------------------- 10.2 INCLUDE. Another important preprocessor directive is #include. A file can be combined with the source file during compilation with this directive. This is similar to appending a file to the current file ( from ED) and then saving the appended file. The compiler does not differentiate where the definitions originated because for the compiler only one file exists. This preprocessor directive is ideally suited to include multiple #define directives into the program. Assume that the following #defines were stored in a file with the name DEF_NEW.H : #define EOS '\0' #define MAXLEN 81 #define EOF -1 If you have a source code text that uses these #define directives you dont have to re-enter them. All you have to do is #include the file DEF_NEW.H: #include "def_new.h" The file extension .H stands for header file. This ensures that all #define directives are available throughout the listing. It is not a requirement but should be done anyway. Although this preprocessor directive can appear at any place in a file, it is better to include it at the head of the source code. The filename is always written between quotation marks. In this case the compiler searches in the directory where the source code is located. You can also enter the include file within the greater than and less than characters: #include STDIO.H The compiler assumes that the file is now located in a subdirectory in which all .H files can be found. The path to this subdirectory passes to the compiler during the start. There is a series of these files which are waiting to be used. One of the most popular of these files can be found under the name STDIO.H. This stands for STanDard Input Output Header file. In Lattice C it is included in the include directory. This file can be examined using the ED editor. PAGE 91 -------------------------------------------------------------------------- CHAPTER 11 - ABBREVIATIONS. We said earlier that C is an ideal language for lazy people who don't lije to type. This is still true since C lets you compress many functions into smaller packages using abbreviations. This chapter describes the art of abbreviating code in C. C abbreviations help save time typing. Lets start with the simplest abbreviations - those used in arithmetic operations. The equation below may look fairly similar to you. Believe it or not, this can be converted to a shortened form of the same equation: number = number * 4; What could be saved here? The variable number appears twice. This doesnt have to be so. The C language allows you to abbreviate the equation to the point where you only need to use the variable once instead of twice: number *= 4; Every time you use the same variable during calculation and for storing the result of the equation, you can use the short form instead. The multiplier gets moved to the left side of the equal sign. The above abbreviation becomes most effective when using long variable names. In addition it helps decrease the number of typing errors (the less you type, the fewer mistakes you make). For example, look at the following abbreviation : the_user_input[index] += '0'; The above abbreviation corresponds to : the_user_input[index] = the_user_index[index] + '0'; Another advantage is the speed difference between the long version of the code and the abbreviated version of the code. The execution speed of each compiled code is different; the abbreviated version executes in less time than the original. The compiler knows what values are used and where to store the result. This can save a lot of unneccesary calculation time. PAGE 95 --------------------------------------------------------------------------- Implementing operator abbreviations is fairly easy to do. All arithmetic operators can be changed into abbreviated form as seen in the following list: += -= *= /= %= etc..... Consider the following expression. Can you see any possibilities for abbreviating the code? value = value * (5 + number); The line is already written in such a way that the operator to abbreviate becomes immediately obvious. It is the multiplication operator. So, if you change the equation into abbreviated form, the source code looks like this: value *= 5 + (number); Now for the same thing in reverse. The operator and the named variable can be attached to the terms using parenthesis. The following equation also has potential for becoming an abbreviated version : var *= number1 - number2; Corresponds to : var = (number1 - number2) * var; PAGE 96 --------------------------------------------------------------------------- 11.1 INCREMENT AND DECREMENT. Shorthand notation can be carried a step beyond arithmetic operators. The operators ++ and -- increment and decrement a variables contents by one. The ++ operator increments the specified varaible by one, and is therefore called the increment operator. The -- operator (called the decrement operator) acts in the opposite manner and decrements (decreases) the specified variable by one. These operators appear as follows: main() { int i; i = 1; while(i++ < 100) printf("%d",i); } This short program is deceptive. Up to the WHILE loop everything is clear. The i variable contains the value 0. Now comes the expression: i++ < 100 First the computer sees if i is less than 100. Then it increments the value of i by one regardless of the results of the test. This corresponds to the following if/else/while: if( i<100) condition = 1; else condition = 0; i = i + 1; while(condition) ............... Here all four listed directives are executed within the parenthesis. That makes the increment operator very powerful. If gets better. The increment and decrement operators can be placed before or after the variable to serve different purposes. The location is important as youll see. A simple example will illustrate : i = j++; PAGE 97 ---------------------------------------------------------------------------- Assuming that J contains the value 3, i also contains 3. Then the value of J is incemented by one to 4. In contrast, the next line places the operator on the other side of the variable: i = ++j; With the same assumptions, the content of J is incremented to 4 and then the variable i is assigned that value. Both variables now contain 4. Remember, if you preceed a variable with an increment or decrement operator, the content of the variable changes before it is used for additional tests. If the operator follows the variable, first the current value is used and then the variable is incremented or decremented. It is important to remember this small but decisive difference. Examine the output of the two programs on the screen. The first number which appears there is two. That is clear since the starting value of i was one which was already incremented inside the loop head with WHILE. For this reason the I at the time the printf occurred already had the value 2. These operators help to write fast and compact programs. They are even more efficient than the abbreviations using the equal signs. PAGE 98 --------------------------------------------------------------------------- 11.2 INITIALISATION , DEFINITION AND DECLARATION INITIALISATION :- These 3 concepts are very important for the C programmer and should not be confused. Lets begin with INITIALISATION. It describes the first assignment of a value to a variable. After this point you know what the variable contains. Before the variable can be initialised it must be defined or declared. Definition takes forms similar to the following :- int index; char string[80]; When the compiler reaches this point, it knows the variables and sets aside the necessary memory area for them. An integer value generally requires two bytes. The variable STRING requires 80 bytes since every char element requires 1 byte. Functions can also be defined. Up to now only the definition of MAIN was mentioned. If you declare a function or variable, this only tells the program that such a variable or function was defined somewhere. For this reason no memory is allocated. DEFINITION : Heres a tip for saving line of code. Variables can be initialised during definition. That saves one program line : int index = 0; Any expression can be assigned to the newly defined variable. The string length which was determined with the STRLEN function can be used during initialisation as follows : int end = strlen(string) - 1; Of course, string must have previously been defined : Some coding can seem exaggerated, but there is no limit to your imagination. long middle = 4*((strlen(string)+1)/2+1)-strlen(string2)/3; DECLARATION : If you write a large program stored in several modules(files) a variable used by all modules only requires a single memory allocation. The definition is in one file and all the other files only contain the corresponding declaration. Declaration is made with the C word EXTERN. The compiler knows that the memory was reserved externally through another file. Otherwise the linker stops linking. Example : PAGE 99 --------------------------------------------------------------------------- extern char pass_word[80]; extern int error_nr; The example above shows that the data type must also be specified. This provides all the information necessary to the compiler about the variable. The function declaration is similar. extern long atoi(); If you define the function in the same file, the EXTERN can be omitted. The declaration is still required since the compiler knows the function names and their data types only at the end of the file. PAGE 100 --------------------------------------------------------------------------- 11.3 MULTIPLE ASSIGNMENTS AND DIRECTIVE VALUE IN C. Source code can also be abbreviated by using multiple assignments. If several variables are to be assigned the same value, individual assignments were previously required for every variable. The same value was given for each : begin = 0; sum = 0; However, the following linme performs the same function : begin = sum = 0; The assignment is from right to left. First 0 is assigned to sum and then begin gets the contents of sum which is zero. A term with more simultaneous assignments could be enclosed in parenthesis which would make the sequence more readable. Here's one version : a = b = c = d = 2; This version shows added parenthesis for readability : a = (b = (c = (d = 2)); Individually expressed, the two above lines correspond to the expression : d = 2; c = 2; b = 2; a = 2; Multiple assignment is possible since every expression has a value (the result of the last operation performed). For example, the value of (d=2) 2, of (index = strlen(string)) strlen (string). Except for large initialisations of variables, the value of an expression can be used almost everywhere. It also shows who knows C well. The shorter formulation will identify the professional. Examples can show this better. Here are some more values for expressions : (2) 2 (a) a (a *= 3) a*3 (a=(b=(a+2)-3)) a-1 PAGE 101 --------------------------------------------------------------------------- The last example must be dissected into its components to reach the same results : (a=(b=(a+2)-3)) (a=(b=a-1)) (a=(a-1)) (a-1) Of course, the advantages of the multiple assignment can be used during the definition and initialisation. The following line os permissible: int start = value = 0; The variable VALUE must be predefined and initialised (very important) which is the case here. PAGE 102 --------------------------------------------------------------------------- CHAPTER 12 - FUNCTIONS. You read in the introduction that a C program sometimes consists of many different functions. Up to now only one has been defined (the MAIN function). Its time to start writing programs which contain several functions developed by you. FUNCTION STRUCTURE First the formal structure of a function definition. You must specify the function name, preceeded by the data type returned by the function. The name must correspond to the usual rules for variable name. Parenthesis containing the arguments follow the name. If no such values exist (e.g. the MAIN function) none can be indicated. If such arguments are expected, these variables must be declared. The values are important since most functions get information from other functions which are then processed. Then follow the executable commands, also enclosed in parenthesis. Let's look again at a simple version of the MAIN function : main() { .. .. } The first item to be encountered according to the specification is the data type which the function returns. Since the MAIN function doesnt return any values to the calling program, the data type is omitted. The word VOID usually appears preceeding a function that returns nothing. Next the function name (main) is specified, followed by a pair of parenthesis. Since no values are passed to the main program, no data appears between the parenthesis. The variable declaration is also omitted, since nothing is passed. Then follow the other executable instructions within the braces, which up to now was the complete executable program. PAGE 105 -------------------------------------------------------------------------- 12.1 FUNCTIONS WITH ARGUMENTS. The next step is to dissect the program into individual tasks. You can write a short function for every partial task. For example, a function to compute the square of a value requires no great mathematical training : double square(x) float x; { double q_number; printf("The square of %f\n is ", x); q_number = x * x; return q_number; } THE SQUARE FUNCTION : The above routine defines a function named SQUARE which in turn returns a double value to the calling program. As a parameter to be passed, a FLOAT value called x is required. At the end of the routine a new C keyword appears, the RETURN keyword. It delivers the required result of the specified data type to the caller and also ends the function. It is important that no semi-colon follows the function name. There must be a semi-colon after each parameter declaration. This differentiates a function definition (without semicolon) from a function call (with semicolon). The following line identifies that a function named square is to be used by the main program : double square(); main() { float value = 3.0; double result; ............ ............ result = square(value) } The names of the parameters passed by the calling function need not be identical to those of the called function. However, the data types must be the same. Notice that the line in which the square function is declared as a function which returns a DOUBLE value. PAGE 106 --------------------------------------------------------------------------- The declaration can be omitted if integer values are returned. The same is true for the definition of a function. If the function returns integer values, a data type need not preceed the function name. This is only possible with the data type INT. All other types must be declared and supplied with the proper data type during the definition. If one of these data types is contradictory (perhaps because the declaration forgot a DOUBLE function) the resulting values will be wrong. While the C language permits much freedom to the programmer, this can cause much trouble. PAGE 107 --------------------------------------------------------------------------- 12.2 FUNCTIONS WITHOUT RETURN VALUES. Some functions return no values. These functions can be declared as VOID, if the compiler has implemented this keyword. This can improve the speed somewhat since the parameters need not be prepared for the calling function. Even that may be omitted, which is the reason why some C compilers dont define the VOID type. /* key.c 12.2 */ void key(string) /* without return value -- void */ char string[80]; { int i; for (i=0; string[i] > 0;i++) printf("%c", string[i]+1); /* from 'a' makes 'b'*/ } void main() { char text[81]; void key(); printf("please input some text\n"); scanf("%80s",text); key(text); printf("in the original it was %s\n",text); } The new defined functions are called exactly like the routines from the libraries. In this example the MAIN function stands at the end of the file. The routine names KEY is declared as a function which returns nothing or VOID. That is important since the definitions would contradict themselves during usage in MAIN. If the function had not been declared the compiler would assume that it should return INT objects. It returns nothing. PAGE 108 --------------------------------------------------------------------------- 12.3 OTHER FUNCTIONS. Another function which does not require a result is STRCPY. This routine copies strings, and performs general string handling. Even though its included in every C compilers library file, it is interesting to see how it can be programmed. 12.3.1 STRCPY - VERSION 1 This copies one string to another. Unlike the previous example, you dont know how many entries are in each string. This can be omitted. It is enough for the compiler to know that it will get a string. In the routine itself, a counter tests all entries. They are copied until the routine reaches the EOS character (the end character which must also be transmitted). #define EOS '\0' strcpy(to,from) char to[], from[]; { int i = 0; while((to[i] = from[i]) != EOS) i++; } The function is indifferent to the memory requirements of the array, since it doesnt have to set aside any memory. The STRCPY works directly with the strings passed to it from the calling function. The strings may be of different lengths. What do you think of the terminiation conditions in the WHILE loop?. The position of the parenthesis makes the processing clear. First it is the assignments of from[i] to to[i]. The expression in the parenthesis also has the value from[i], and also the character which was copied. This is now compared with the end code character. If you copy the EOS, the condition is no longer true and the loop terminates. Otherwise it increments the current counter and remains in the loop. PAGE 109 -------------------------------------------------------------------------- The actual loop body has only a peripheral role. The main action occurs in the ending conditions. Experiment with this function. Notice that the string into which the copy is stored appears first. Here is a complete example program : /* copysrt.c 12.3.1 */ #define EOS '\0' #define MAXLEN 81 strcpy(to,from) char to[], from[]; { int i = 0; while((to[i] = from[i]) != EOS) i++; } void main() { char s1[MAXLEN], s2[MAXLEN], s3[MAXLEN]; printf("Your name please\n"); scanf("%40s",s1); strcpy(s3,s1); strcpy(s2, "TEXT in s2"); printf("Therefore %s, in s2 is \"%s\".", s1,s2); printf("I hope %s, that everything is clear!\n",s3); } The STRCPY function can be used to initialise strings since the following expression is not permitted in C : WRONG: main() { char text[20] = "This_is_text!"; . . . . . . } RIGHT: main() { char text[20]; strcpy(text,"This_is_text!"); . . . . . } This copies the complete string into the variable text. PAGE 110 -------------------------------------------------------------------------- 12.3.2 STRLEN. You used the STRLEN function earlier in this book. It is simple to write and return a value. The passed length of the string is a whole number and should be an integer value. strlen(string) char string[]; { int i = 0; while(string[i]) i++; return(i); } A nice short function! The expression string[i] is always the content of this element. This means that the expression is only 0 (false) when the end character \0 (EOS) has been reached. The counter which corresponds to the length of the string passes to the calling function as an integer value through a return directive. This function doesnt have to be declared in the calling function because it returns an INT value. If the return directive passes data, it must be assured that the value has the proper data type. If the function definition states that the routine returns a CHAR element, there should be a variable or constant of the CHAR type. Some compilers will not tolerate such mistakes and will issue an errror message. Others are indifferent and convert the result into the data type indicated in the definition. Its better to do it right in the first place. PAGE 111 ------------------------------------------------------------------------- CHAPTER 13 - ARRAYS. Up to now strings have been used as if they were a special data type. A string is actually multiple char entries. A string of similar objects is called an array. Arrays can also be made using INT or FLOAT data types as well as CHAR types. Any elementary data type can be stored in an array. Several similar variables can be accessed through a single identifier. A single element is accessed by using a subscript called the index (counter). The definitionn of a long array differs little from string definition: long value[20]; This line reserves 20 elements of type long for the variable value. To indicate the end of a string, the last entry contains the value 0, i.e. assigns the escape sequence \0. For this reason the definition of a string (character array) requires one element more than needed for the actual string. No such requirements exist for other array types: Only as many entries are defined as required by the data. A value assignment of one element is possible only by providing the index. For example : value[0] = 'O'; value[1] = 707; value[2] = 31415; The index value of the first element always starts with 0. Using this method you can create a string using one character at a time; char string[80]; string[0] = 'o'; string[1] = 'k'; string[2] = '\0'; This tedium can be avoided by using the STRCPY function. The assignment sequence above would store the string value 'ok' into the variable STRING, and is terminated with the usual end code \0. Again the difference between a single character and a string of characters should be emphasized. The difference between "K" and 'K' is that "K" is a string, while 'K' is a character. If a letter is enclosed in "Quotation marks" like a character string it is a string. It is also terminated with a \0 so that "K" consists of two characters, the K and \0. However,'K' is only a single character. This condition must always be observed since all operating system routines assume that the string terminates with \0. The last element that may be accessed has a value of 79, according to the declaration above of STRING[80] (counting starts with 0) PAGE 115 -------------------------------------------------------------------------- 13.1 MULTI-DIMENSIONAL ARRAYS. Up to now we've been using one-dimensional arrays, i.e., variables which use a single subscript. Multi-dimensional arrays have elements like a one-dimensional array. However, multi-dimensional arrays have multiple elements. For example, if you were designing a chess game in C you might use an 8 * 8 array for the chess board data: int field[8][8]; Both elements are of course between 0 and 7. You need two subscripts to access a single field: printf("Contents of line 2 column 4 %d\n",field[1][3]); You can define an array with upto 5 sets of elements: long content[4][5][6][7][8]; Please observe that arrays can quickly occupy large amounts of memory. The array would require 4*5*6*7*8 * 4 (size of a single long element) bytes(26880 bytes or 26.25k). Data can only be stored sequentially in memory. The user must get away from the notion that a two-dimensional array is located in two tables which are one in front of the other. How would a five-dimensional array be stored? Since all elements are stored in a long series (One-dimensional) there is a rule which must be followed. The first index changes only when all elements which belong to it's group are stored. During the second index that occurs more frequently and the last index changes changes with every element. This concept is easier to understand in a listing which shows the position of the entries in menory. Assuming a definition of INT POS[4][3];, entries in memory are ; [0][0] [0][1] [0][2] [1][0] [1][1] [1][2] [2][0] .... .... [3][1] [3][2] PAGE 116 -------------------------------------------------------------------------- To conclude this chapter we want to present a program which operates with arrays, and touches on many topics previously discussed. The program tests a series of numbers, passes them to a routine which adds them and receives a sum back. Then it makes statistical evaluations to determine if it's worth storing the values. An array stores the data entered. There are also some tricks which should be examined closely; /* array.c 13 */ #define FALSE 0 #define TRUE 1 #define MAXENTRY 20 long total(); /* declaration of the function.*/ void main() { int i, number, end = FALSE; long sum, data[MAXENTRY]; for(i=0; i 0 ; i++) printf("value %d: %5.9lf%%\n", i+1 , data[i] * 100.0 / ( (double) sum / number ) - 100.0 ); } long total(array,cnt) long array[]; int cnt; { long sum = 0 while(cnt--) /* short and precise */ sum += array[cnt]; return sum; } LATTICE The library for mathematical functions and floating point numbers must be linked with the standard library. Example: LC -Lm array PAGE 117 -------------------------------------------------------------------------- AZTEC If you work with the Aztec compiler, the library for mathematical functions and floating point numbers must be linked with the standard c.lib. Example: cc +L array.c ln array.0 -lm -lc First some information on the program. To make it more secure, only 20 entries are permitted. The #define MAXENTRY allows you to adapt the program to larger input. The declaration of the ADD function is important. since this function uses LONG values, the compiler must be told this. The declaration can be performed within the MAIN function. The && operator ends the for loop which connects two tests logically with an AND. If not all entries are occupied and the varaible end is unequal to 0, the loop executes. NEGATION The negation operator converts END into the logical opposite. At the beginning the varaible contains the value 0 so that the expression becomes !end 1. The reverse occurs when end is set to 1 and the negation !END is used to leave the loop. This happens when the user enters the number 0 indicating the end of the input. The first entry in the array requires the index 0. Since the count usually starts at one, a 1 is added to the current index during text output. In the formulation of the SCANF function, the following is most important: &entry[i] If another array was input no & character appears. That was the big exception. Since entry[i] and not ENTRY was written, this is not an array but a perfectly normal LONG varaible. During the SCANF routine it is equipped with the & like all elementary data types. The fact that this variable is in a long string of elements doesn't concern the SCANF function. The following If test also merits closer examination. This is a typical case of C abbreviation. The test should pass the value 1 to the end varaiable if the current input was 0. The following shows this; if(entry[i]==0) ........... If entry[i] contains a zero this expression is also zero. With the help of the negation operator a true result is obtained. Especially for the test ==0 or !=0, the abbreviations are often placed in the location wherre you would expect to find an explicit value. It doesn't complicate the matter but the uer must know what is hidden there. PAGE 118 -------------------------------------------------------------------------- When the loop finishes, either because 20 entries have been made, or the last entry was 0, the total number of the stored data is calculated. The ADD function gets the necessary data (the array with the input and the number of values to be added). This routine returns the sum. With this information the deviation of each entry from the average can be calculated. If the task of the program was only to add a series of numbers, no arrays would be needed but all entries could be summed after their entry. PAGE 119 ========================================================================== CHAPTER 14. 14. MORE ABOUT LOOPS. This chapter takes you through some of the finer points of using loops in the C programming language. You've already seen FOR loops and WHILE loops. It also demonstrates some refinements to the FOR loop; statements which help control loop programming (BREAK and CONTINUE); and a function for switching around within a loop (SWITCH). --------------------------------------------------------------------------- 14,1 MORE ABOUT THE FOR LOOP We described the FOR loop earlier in this book. Now we'll look at the limitations and flexibility of the FOR loop. Individual components of the FOR loop are separated by semi-colons. Several statements can be placed within the initialisation and the increment expressions. They use commas as separators, instead of Semi-Colons. This is how the FOR loop can be used : for(sum = 0, i = 1; i<=20; i++) sum += 1; or for(i=1, j=0; i<10; i +=2, j+=3) This is the usual construction of a FOR loop. Since C permits other variations this example is presented; for(printf("now we start"); ; printf("Bang\n"), i++) if ((c = input()) == 'e' ) break; The text 'now we start!' appears at the beginning of the loop. A test is then made to determine if the condition located between the semicolons is true. This is always true since there is nothing there. You may recall that, under every condition, a null value is always considered a false condition. Everything else is considered logically true. The condition in the loop is always true. The only way to stop the program is to press the key, provided there is an input function. PAGE 123 -------------------------------------------------------------------------- 14.2 BREAK If the test for IF is true the BREAK statement is carried out. The break statement ends the currently executing loop immediately and forces the program to continue with the statement that follows the loop that just ended. The BREAK statement is the only way to break free of a loop at any time. Look at the program in the precedding section. The increment proceeds in an unusual fashion. a PRINTF call can be found there. This printf executes at the end of each loop execution (notice that not much remains of the original construction.). An endless loop, which doesn't have an initialisation, a test or incrementation would appear as follows; for(;;) { ...... } A FOR loop can always be replaced with a WHILE loop, and vice versa. The general format is; for(term1; term2; term3) { other directives } OR term1; while(term2) { other directives term3; } PAGE 124 --------------------------------------------------------------------------- 14.3 CONTINUE The CONTINUE statement does the opposite of the BREAK statement. Instead of leaving the loop immediately, the program jumps to the next directive in line for execution after the last line within the loop is processed. For the three types of loop this is; 1. The body of the WHILE loop (within the parentheses) 2. The incrementation of the FOR loop, therefore FOR(...; ...; continue) 3. The directive after DO in DO....WHILE. EXAMPLE: calculate(field) double field[]; { int i; for(i=0; i%s<\n",text_ptr); } Where in this program is the memory space for the string? The pointer does nothing in this direction. It is stored somewhere in the program text, just like in function calls (e.g. printf("Hello\n");). Also this string within the function must be stored somewhere. If the text should be changed for example with access to text_ptr the maximum length must be observed. In the string above this is only 28 characters with one character representing the end of the string. If 30 characters are written into this space anyway a system crash can be expected. It is possible that behind the string program code was stores which was overwritten. Should the computer encounter such data which it cannot understand it will go crazy. The name of array symbolises the first element in the chain. Now the question, what is expression field[3][2], if the following definition has been used; int field[5] [5] [10]; Is it an element of this array? If so which one, and if not what is the element? Examine the expression carefully. If only contains two indices but the definition contains 3. If follows so that it cannot be an extra element. If can only be a pointer which points to the first element. The first element isn't field[0][0][0], but the first field to which field[3][2] points. PAGE 138 -------------------------------------------------------------------------- Is it clear now what wonderful changes can result from forgetting an index? One element of the field becomes a pointer to a field in which the missing index is replaced with [0]. Therefore field 3 points to field [3] [0] [0]. If something like this is possible, it can be done with pointers. Later we will see some other tricks with pointers. PAGE 139. ========================================================================== CHAPTER 16 - STORAGE CLASSES This chapter discusses various groups of variables. These variables have different lifespans during program execution. There are four storage classes : AUTO (or LOCAL), GLOBAL, REGISTER and STATIC. Each of these storage classes helps your program identify which C functions recognize which variables, and determine how long the functions should use these variables. 16.1 AUTO Even though the name is unfamiliar to you, you've been using the Auto (LOCAL) variables all along. The AUTO variables represent the default storage class in C language. These variables belong to the AUTO class because they are automatically defined every time a function is called. On the function call, C allocates memory space for the AUTO variables. The lifetime of an AUTO variable is limited to the function in which the variable is declared. After the function is abandoned through return , or the lasr brace of this function is reached, the memory space allocated is released and can be used for other assignments. These AUTO variables can only be used in the function for which they were defined. The content of the variable is lost and the name is not known to the rest of the program. PAGE 143 -------------------------------------------------------------------------- 16.2 STATIC Unlike AUTO variables, STATIC variables are retained until the end of the program and are not deleted after leaving the function. They do not have to be created again during a new call of the function . Leaving, which means the terminating of the executing function, should not be confused with another function call within this function. Control may briefly pass to another routine, but the calling routine remains active (it's waiting for a result). Here's an application of a STATIC variable. The C word static appears in front of a definition. For example; function() { static int counter = 1; ............. } During the first call of the function, the variable is defined and initialised with a starting value as in the example above. If the function is left temporarily, a new variable isn't created during the new function call because the variable still exists. Even it's content remains and it does not have to be initialised again. For example, a counter in this routine could track how many times it has been called. PAGE 144 -------------------------------------------------------------------------- 16.3 EXTERN The next storage calls is the EXTERN or GLOBAL variable. These variables are defined outside the function and can be used by all functions. A section of a program would appear as follows; #define EOS '\0' int error,dummy; main() { .... } The variables which were defined can also be used by functions which are not within the source file. The linker is given a number of files for linking. These file contain functions which have already been compiled. They may need GLOBAL variables which must be assigned the right values in their program. Such variables must be declared before using them with the EXTERN function, but they don't have to be defined: extern int error; This permits the use of the variable in a file in which ERROR was not defined. Combinations such as GLOBAL STATIC variables are also permitted. Through this definition all functions can access the GLOBAL variable within the source file, but the situation just described of accessing this variable with a function from another file is prohibited. The variable is only known to the source file. Functions which first come in contact with the program through the linker, have no access to this variable. PAGE 145 -------------------------------------------------------------------------- 16.4 REGISTER The last storage class is REGISTER. Those of you who have some programming experience with assemblers know what this means. A processor, the most important part of a computer, has various internal memory locations. One such memory segment, which should not be confused with the RAM of the computer, is called a REGISTER. The number of registers which can be used depends on the type of processor used. a 6502/10 used in the C64 or in the Atari 600/800/130 has only three registers ( 2 registers and an accumulator ). The MC68000 used in the Amiga, Atari ST and Macintosh has 17 registers. Each 68000 register is four times the size of a register in the 6502. For this reason there are almost no compilers for 6502 computers which offer the capability of register storage for variables. Of the 17 registers in the computer only three to five (depending on the compiler) are made available for storage. The remaining registers are required for internal use. A variable defined as REGISTER must fit insude a register. a 68000 register is equal to 32bits (4 bytes), which only permits integer numbers. Even if the float value could occupy only 4 bytes it could not be stored in a register. Valid data types are; int char short unsigned long combinations of the above pointers Pointers are possible since they only represent the address of an object. In the Amiga they occupy only 4 bytes. There are other restrictions. The defined variable can only be an AUTO variable since it occupies a register of the central processor. They are rarely used and can be occupied only for a short period of time. After leaving the function in which it was defined the register is released again for other purposes. The REGISTER variable has a speed advantage over other variables. The program can only fully utilise this speed when these variable are used during during many loop repetitions or calculations. The variable does not have to be loaded from memory into a register for every use since it is already present. PAGE 146 -------------------------------------------------------------------------- 16.4.1 FAST STRCPY ROUTINE. Before we present the first example, we must discuss another limitation. It isn't possible to obtain the address of a register variable with an & operator, because a register doesn't have an address. It is not located in RAM. strcpy(to,from) / * last version */ register char *to, *from; { while(*to++ = *from++) ; } This definition of the CHAR pointer as REGISTER should receive the maximum speed that can be obtained in C. This could be faster only if it was written in machine language. To test the speed advantage obtainable through registers, compile the following program. To measure the speed of the program you must use the registers as often as possible and should not use other functions since they only extend the time required. For this reason the program does nothing more than count a variable down from 5,000,000 to 0. /* countdown.c 16.4.1 */ #include void main() { printf("Time comparison with and without registers\n"); printf("RETURN for START\n"); getchar(); printf("%c Start without ",7); without_register(); printf("%cSTOP!\nRegister routine\n",7); printf("RETURN for START\n"); getchar(); printf("%c Start With",7); with_register(); printf("%cSTOP!\n\n"); } with_register() { register long i=5000000; while(i--) ; } without_register() { long i = 5000000; while(i--) ; { PAGE 147 -------------------------------------------------------------------------- The pre-processor command #include include the STDIO.H file in this C program. This file is required because the GETCHAR routine is used to obtain a character from the keyboard. In the Lattice C compiler, the function is unusable because it waits for the key after every character. However, it is sufficient for the program above, so it can be used to wait for the key. Timed by hand the author counted 51.6 seconds without register variables and 24.1 seconds with register variables. That's impressive because it's twice as fast when the word register is used. It should be noted that the multi-tasking capability of the Amiga, which could have been performing a task in the background, was not used. This would have Provided a different result. PAGE 148 -------------------------------------------------------------------------- 16.5 LOCAL LOCAL variables are the reverse of global variables. Different variable groups such as REGISTER,AUTO or STATIC LOCAL can be defined. They are only valid in the block or function in which they were created. a LOCAL variable has precedence over a GLOBAL variable , which means that, if two variables were defined with the same name the LOCAL variable is used. The local variable gets preference while the GLOBAL variable disappears for the moment. An example; /* local.c 16.5 */ int 1 = 1; void main() { int i = 2; printf("%3d",i); { printf("%3d",i); { int i = 3; printf("%3d",i); } printf("%3d",i); } printf("%3d",i); test(); printf("%3d",i); } test() { printf("%3d",i); { int i = 4; printf("%3d",i); } } The numbers 2,2,3,2,2,1 and 4 are displayed sequentially. In the MAIN function a new local variable is declared so that the GLOBAL variable i is no longer addressable. The following blocks keeps this configuration and another 2 appears. Then another block follows in which another i variable is defined. Because of this, the previous block becomes invisible to the program and the current one prevails. The result of the output is 3. After the program leaves all blocks, the hidden variable appears again. The previous variable with the value 3 is erased by leaving this block then disappears. The test function is now called and proceeds to output i. Since no LOCAL variable is known as this point, the output of the GLOBAL variable, which is 1, is used. Finally a LOCAL integer variable is activated which overshadows the global variable again. This ensures that the last variable defined in a block is used, and that often used name (i,x or j etc..) are recognised as runtime variables in many loop with different values. PAGE 150 -------------------------------------------------------------------------- CHAPTER 17 - USER DEFINED LIBRARIES. One advantage of the programming in C is the modular construction of programs, which can accept existing functions used in other programs. The #include directive lets you add external files that have frequently used functions to the current program before compiling. The compiler processes one large file instead of several small files. Every C programmer writes his own functions at some time or another. You've already entered two functions (STRLEN and STRCPY); let's use these. Most compilers contain these functions. However, viewing them can give us an understanding of how user written functions work. Save these functions to your own file under the name STRING.C. You can include these functions in your own program using the #include directive. The following line searched the main directory for the INCLUDE file STRING.C #include "string.c" The following line also adds the STRING.C file to the main file; #include Of the two syntaxes, the second line is much more flexible than the first since it searches many different directories for the same file. Most C implementations have INCLUDE files as standard equipment. Files with .h extensions contain mostly #define directives. You can find these functions in a file such as AMIGA.LIB or LC.LIB. Include files can be included on demand. The syntax reads; #include Before starting with INCLUDE, first you need something that can be included. A useful function can be written to compare strings. Since strings are not elementary data types they cannot be compared with; if(string1 == string2) /* this is wrong ! */ If you wrote the variables string1 and string2 as character arrays, the name would correspond to the address of the first element (&string1[0]). Therefore the addresses of the two arrays always differ. Since both arrays have been assigned by the compiler to separate memory locations for their char entries the comparison is completely useless. The only case (theoretically) in which this IF test can be fulfilled is if one or the other variable was defined as a pointer and if the pointer pointed to the same string. This method doesn't work. PAGE 153 -------------------------------------------------------------------------- 17.1 THE STRCMP FUNCTION. You now have to write a program to compare each element of the first string one at a time with each element of the second string: strcmp(s,t) register char *s, *t; { while(*s==*t) { if (!*s) return(0); /* end reached (*s==0) */ s++; t++; } return(*s - *t); } The STRCMP function compares the characters of the S string with those of the T string. As long as the characters are equal (*s==*t), the WHILE loop executes. A test determines whether the last character matches the \0 marker EOS (end of string). If so, both strings must be identical, since S and T end with EOS. Otherwise the pointers move to the next element and the process repeats. If a character appears within S which differs from the T character, the WHILE loop terminates and the difference between the two characters (*s - *t) returns to the calling program. Negative values indicate that the S string was smaller than T. Positive values mean the opposite. A null returned after the IF test indicates that both strings are completely identical. Store this function and the other two files below as STRINGFUNC.C. Since the older STRLEN routine could be improved, we will use pointers this time. Instead of the indices, the pointer moves over all entries of the string up to the EOS character. The start value must be stored first so that the number of increments can be computed. That is faster than counting with an additional variable. The STRINGFUNC.C file therefore appears as follows: /* stringfunc.c 17.1 */ strcpy(to, from) register char *to,*from; { while(*to++ = *from++) ; } PAGE 154 -------------------------------------------------------------------------- strlen(s) /* Conversion to pointer */ register char *s; { register char *help = s; /* store initial position */ while(*s) s++; return (s-help); /* difference between pointers is element number */ } strcmp(s,t) register char *s, *t; { while(*s==*t) { if(!*s) return(0); /* end reached (*s==0) */ s++; t++; } return(*s - *t); /* difference between two strings */ } Now let's see if they function properly. For this you'll use two strings which are initialised in the program. /* stringtest.c 17.2 */ #include "stringfunc.c" /* global arrays can be initialised ! */ char string1[] = "hello!"; char string2[7] = {'h','e','l','l','o','!',0}; main() { printf("\nComparison of >%s< and >%s< is%d\n",string1,string2, strcmp(string1,string2)); printf("Now >%s< and >%s< result in %d\n\n",string1,"huhu!", strcmp(string1,"huhu!")); } First we will look at the expected results of the function call STRCMP. The first call returns 0 since both strings are equal. The second function call returns -16. This number is the result of the comparison of the E and U characters. This means that the first different character in the string ("hello!") is smaller than the first different character in the second string ("huhu!"). Array initialisation is new to this program. Until now each element was stored idividually. Automatic variables wouldn't allow storage in any other form. With GLOBAL variables, a string can be initialised directly or as in the second example, every character is initialised separately. PAGE 155 -------------------------------------------------------------------------- In the first example there wasn't even an indication of how many elements string1[] should have. This is another indication that the C language was intended for those who consider laziness a virtue. The compiler must determine the number of the character on it's own. It initialises string1 with 7 elements (don'f forget the null byte at the end.). Those who prefer can indicate the value as in the second example. If you assign elements individually (example 2) to the fields they must be contained in braces and separated by commas. For multiple dimensions, multiple braces must be used. int field[4][4] = { { 1, 2, 3, 4 } , ( 6, 3, 4, 9 } , { 3, 4, 5, 6 } , {12, 9, 0, 2 } , }; This formulation assigns field [4][4] the proper values where the first values {1, 2, 3, 4} are stored in the fields FIELd[0][0] to FIELD[0][3]. The inner braces are not required on some compilers and the directive could appear as follows: int field[4][4] = {1,2,3,4,6,3,4,9,3,4,5,6,12,9,0,2}; When some elements are not initialised, they don't have to be listed. All elements left out are automatically assigned a null. int field[3][3] = { {3, 2}, {4}, {3, 4, 5}, }; The fields FIELD[0][2], field[1][1] and field[1][2] contain nulls. A semicolon must follow the definition. After the inner braces and the last brace there must be commas. Remember that initialisation only affects GLOBAL or STATIC variables and not AUTO variables. PAGE 156 -------------------------------------------------------------------------- 17.2 ITOA Another routine seen frequently in connection with strings is ITOA (Integer to ASCII). It converts an integer value into the corresponding character string. When you pass the number 123 ITOA returns the string "123" in a character array. This is very important when preparing text that contains numbers. All convewrsions usually preformed by PRINTF can also be done with user functions. The ITOA function requires, as parameters, an integer value which it can convert and, and a string to store the result. The head of the function definition reads as follows; itoa(n, s) char s[]; int n; The modulo operator % performs the conversion. By dividing the number by 10 you obtain the last place. Then the code for the number '0' is added to get the first character. The number is then divided by 10 to shift it left one space and the last number drops off. The same procedure is performed on the new last position. The program section for this process appears as follows, if the index for the character array is called I; do s[i++] = n % 10 + '0'; while ((n/=10) > 0); The last place is converted and stored in S until the number which was stored in N has reached 0 through constant division. The sign should not be forgotten since it could cause problems for the loop (number larger than 0). The simplest process makes the number positive before the conversion and , if necessary, sets a flag for a negative value. After the completed conversion , the string returns the minus sign. The completed processing converts the number 123 into the string "321", but only the last place is processed and stored in the string. The solution to this problem is very simple. Write another function that reverses the string. Assuming that such a function already exists (see the next section for the function) the routine would appear as follows: PAGE 137 -------------------------------------------------------------------------- /**********************************/ /* Name : itoa */ /* Parameter: n(int), s(string) */ /* Function: Convert int to string*/ /* Comment :Requires Reverse() */ /**********************************/ #define EOS '0' #define FALSE 0 #define TRUE 1 itoa(n, s) register int n; register char *s; { register int i=0; register int sign = FALSE; if(n < 0) { sign = TRUE; n = -n; } do s[i++] = n % 10 + '0'; while( n/=10); if(sign) s[i++] = '-'; s[i] = EOS; reverse(s); } The large header contains important information. The function developed by the user should be ready for use when it is finished. After some time the function name and the parameters to be passed may have been forgotten. At that time you could consult the header with it's comments. The function can be compiled independant of other functions. If the compiler permits it, it can be stored in a library. Of course the source files can be included into the current file with: #include "itoa.c" This increases compiler time, of course. Since this function should be accepted in the library, it should be the latest state-of-art. This can be done with the ITOA function by defining all variables as REGISTER variables. The define required for this function sshould not be omitted, even it appears somewhat cumbersome to determine a define for a single application. It improves readability since larger programs usually access these macros. PAGE 158 -------------------------------------------------------------------------- 17.3 REVERSE. Now to the REVERSE function which can reverse a string passed to it. Construction of the routine doesn't present a problem. Two pointers, or indices, are needed for the beginning and end of the string. These pointers exchange their elements between themselves and are then moved toward each other. The pointer at the beginning is incremented and the one at the end is decremented. Exchange continues until the two pointers are equal, i.e., point to the same element. The routine is presented complete with a commented header. /***********************************/ /* Name: Reverse */ /* Parameter: s(string) */ /* Function: Reverse string */ /* Comment : Requires STRLEN() */ /***********************************/ reverse(s) register char *s; { register int c, i, j; for (i=0, j=strlen(s) - 1; ib) ? a : b; This would be similar to the if construction: if(a>b) c = a; else c = b; This term delivers the maximum of a and b. Since this is simple to formulate, this operation usually determines minimum and maximum quantity. The define : #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) Take a look at STDIO.H which youll find with your compiler. There youll find the definition. PAGE 163 ------------------------------------------------------------------------ 18.2 THE SIZEOF FUNCTION. The SIZEOF (size of) function returns the sizes of objects (variables) regardless of type. The unit returned by this operator is defined on the basis of CHAR elements. The following example followed by SIZEOF(character) returns 1: char character = 'a'; The result is always the number of occupies bytes for the object under investigation. The following short program determines how much memory is used by the various data types in your compiler. This will tell you if an INT variable occupies 2 bytes (most C compilers) or 4 bytes (Lattice). /* sizeof 18.2 */ main() /* inidicates memory requirement for data types*/ { printf("\nData type\tMemory in bytes\n"); printf("char\t\t&d\n",sizeof(char)); printf("short\t\t%d\n",sizeof(short)); printf("int\t\t%d\n",sizeof(int)); printf("long\t\t%d\n",sizeof(long)); printf("float\t\t%d\n",sizeof(float)); printf("double\t\t%d\n",sizeof(double)); printf("pointer\t\t%d\n",sizeof(* char)); } PAGE 164 --------------------------------------------------------------------------- 18.3 BIT MANIPULATION. This section describes the remaining operators, which deal with controlling individual bits. Operators for bit manipulations exist in addition to the logical combinations. A bit (binary digit) represents a position in a binary number and can therefore only assume one of two values (0 and 1). The conversion into the binary system is similar to the conversion into the octal or hexadecimal system. The bit is also the smallest unit which the computer can use. It acts as the basis for all other nunbers which can be used in the computer. For example, a byte consists of 8 bits, a word of 16bits and a long word of 32 bits. A character may be stored in a CHAR variable. Characters are stored in bytes; a byte can accept 256 different kinds of numbers. Depending on the compiler, an integer value contains 16 or 32 bits and a long value 32 bits. Individual bits of these data types can also be accessed. These operators cannot be used with FLOAT or DOUBLE variables. 18.3.1 THE "AND" COMMAND. The AND operator consists of the & character. Maybe you thought this is the address operator. This character can be used for both purposes but it's hard to explain why this is so. You must know the context in which it is used. If is is placed alone in front of a variable, it represents the address operator. If it is placed between two values in a normal arithmetic equation, then it is the binary AND. The concepts logical and binary help you distinguish between the two completely different operators. The logical and is different from &&. With AND (&) individual bits can be reset. A set bit has the value 1, a reset bit the value 0. The following table shows the connection between various bit combinations. AND OR EXOR ------------------------------------ & | ^ 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0 PAGE 165 --------------------------------------------------------------------------- According to the AND a bit is set (1) when both bits are set, otherwise the result is a zero. This is comparable with : if(bit1 == 1 && bit2 ==1) result_bit = 1; else result_bit = 0; 18.3.2 THE "OR" OPERATOR. With the (|) OR operator bits can be set. A glance at the table above will help you understand the various combinations of bits. In OR the resulting bit is set if one or both bits are set. Only if both bits are 0 is the result or OR also 0. The | sets individual bits and the & resets the bits. A mask acts as a storage area for the bits. A mask is represented by a number placed over the value to be processed. If a variable is ORed with this mask, all bits set in the mask are now also set in the variable. EXAMPLE : Bit number 2 should be set in the variable FLAGS (the count starts at zero).: #define MASK 4 int flags = 73; flags |= MASK; After this operation, the OR with the value 2**2 (number of the bit to be set) = 4 (sets the second bit in the variable FLAGS). A targeted resetting of certain bits sets the corresponding bits of the mask to 0. With the AND operation the desired zero bits are obtained. Example: bits 1 and 4 should be reset. int flags = 37; flags &= 0355; /* All bits expect 1 and 4 are set (0-7) */ Every bit has its own value according to priority. For example bit 3 has a value of 8 (2**3). The table below shows the individual bit values: bit nunber 0 1 2 3 4 5 6 7 Value 1 2 4 8 16 32 64 128 Some examples for bit operations: 1 & 2 = 0 2 & 6 = 2 7 & 8 = 0 9 & 12= 8 PAGE 166 ---------------------------------------------------------------------------- The last example should be examined closer in the binary system. 9(dec) = 1001 (binary), 12 (dec) = 1100 (binary) 1001 & 1100 -------- 1000(binary) = 8 (decimal) The same operation for OR: 1 | 2 = 3 2 | 6 = 6 7 | 8 = 15 9 | 12= 13 The last line expressed in binary: 1001 1100 ---- 1101 1101 (binary) = 13 (decimal) It is important that the characters & and && are kept separate from each other. & connects expressions bit by bit. && also makes a logical comparison from which either a 1 (true) or 0 (false) is returned. Therefore 2 & 1 = 0 but 2 && 1 = 1. There is also a distinction between the operators | and ||. Loops and conditions may result in strange behaviour if you confuse these operators. 18.3.3 BITWISE SHIFT OPERATORS. >> << Operators for bit shifting are >> and <<. They permit bit shifting to the left or right within a field. A shift to the left (<<) by one position is the same as multiplying by 2, only it is much faster. All of this is dependant on how data and numbers are stored and processed in the computer. A shift to the right equals a division by 2. Depending on the data type, either zero bits or set bits move into free locations. For unsigned values, zero bits are shifted in every case. For normal signed INT values it depends on the compiler used. For positive numbers, zero bits should be added to the left, and for negative numbers one bit. This is compiler dependant and there is no guarantee of how this works. PAGE 167 ---------------------------------------------------------------------------- The number of shifts is indicated behind the operator. 5 << 3 = 40 101 (binary) shifted left by 3 bits (zero bits are shifted in): 101000 (binary) = 40 (decimal). Use the program in the previous section for converting decimal numbers into binary numbers. 18.3.4 EXCLUSIVE OR The EXCLUSIVE OR operator ^ is related, as the name indicates, to the OR operator. The only difference lies in the fact that both bits are set. The OR operation results in a set bit, but the EXCLUSIVE OR resets a bit. The table for EXCLUSIVE OR is as follows: EXOR 0 1 --------------- 0 0 1 1 1 0 for example 2 ^ 1 = 3 Please do not confuse ^ with the up arrow for exponentiation. This does not exist in the C language. REMEMBER : The bit is set only if both bits are different. 18.3.5 ONE'S COMPLEMENT. The one's complement operator ~ requires only one parameter. All bits of the parameter are reversed. Set bits are unset and vice versa. It is recommended to use this operator only for variable which were defined as unsigned, or the sign is also affected. unsigned number = ~3; In the variable all bits except the first two are set (priority 0 and 1 = 1 + 2 = 3) so that the variable now recieves the following bit sequence (starting from a 16-bit integer): 1111 1111 1111 1100 = 65532 PAGE 168 ----------------------------------------------------------------------------- 18.4 GOTO Perhaps GOTO sounds familiar from BASIC, but the C implementation is a true curiosity. This statement has a bad reputation in C, since it can destroy a well structured program. Jumping in a function can defeat the purpose of clarity in programming. Nevertheless the GOTO statement is not totally useless. It can be used effectively in error trapping. If an error occurs within several loops, which makes progress impossible, only the GOTO statement offers escape. The usual break directive can only stop one loop, not several at once. Some tests and other break commands could also terminate all loops. It is preferable to use GOTO. The use of the GOTO statement of course assumes a label (a marked line to which the jump should be made). label: printf("This is where goto will Jump!\n"); . . . . . . . if(error) goto label; The labels can be defined in the program text anywhere, but must include a colon. They are only required for the GOTO statement and are formed exactly like variable names. NOTE : The label and jump commands must be used in the same function. It is not possible to jump across functions. PAGE 169 ---------------------------------------------------------------------------- See document part 2! ---------------------------------------------------------------------------- .. ..