TEACH C.EX1 A. Sloman, May 1988 Updated by Steve Easterbrook, Nov 1992 C PROGRAMMING - FIRST SET OF EXERCISES -------------------------------------- Note - most of the exercises in this file are for you to try on your own. CONTENTS - (Use g to access required sections) -- Reading -- Possible things to learn -- Use a special directory -- Compilers & ANSI C -- A first exercise -- Things to note in examples.c -- Play with the examples.c file -- Change some of the arguments to printf -- Conditional expressions and statements -- Simple and compound statements -- Exercise, define test5(x,y) -- Loops in C -- Exercise define addup(start,incr,fin) -- Assignment: define test6(start,incr,fin) -- Assignment: fix printslope and slope -- Using a cast to transform a type -- Using macros -- SEE ALSO -- Reading ------------------------------------------------------------ Main text henceforth referred to as K&R: B.W. Kernighan & D.M. Ritchie The C Programming Language Prentice Hall 1978 There are other books listed in TEACH * C.refs, and more will no doubt keep turning up in bookshops. If you use other books you should still be able to do these exercises. Please let your course tutor know which you find specially good or bad. -- Possible things to learn ------------------------------------------- Here is a list of possible things to learn about C. You'll only manage a subset in the time available. In general, there is no RIGHT subset, but the items marked with an asterisk form a kind of 'core' syllabus for an introductory C course. *- The syntax of C (See summary in HELP * C_SYNTAX) *- Conditionals, loops, switch, procedure calls, arguments, results *- Arrays (including strings) and structures *- Pointer manipulation and accessing fields of structures *- Data-types available and ways of defining new data types *- Identifier declarations *- Logical and arithmetic operations *- The use of printf for formatted printing and scanf for input see "man printf" "man scanf" *- Allocation of store in C - local (automatic) and global (static, and external) *- The use of the standard input/output routines getc, putc, getchar, putchar. See "man getchar" "man putchar" Also "man 3s intro" *- File manipulation routines close, creat, open, read, write. See their man files - Store management malloc and other things described in "man malloc" - C Library facilities available and described in man 2 intro (system calls, file handling, etc.) man 3 intro (mathematical and other libraries) - How to write portable C programs: see "man stdio" - More advanced facilities man 3m intro - mathematical functions man 3n intro - network facilities man 3r intro - remote procedure calls (RPC) man 3x intro - other facilities, e.g. screen management *- How to run C programs (See HELP * CCOMP, and "man cc" - available as HELP * CC on TSUNA) *- The use of 'cc -c' to produce .o files (object files) - The use of 'ld' to link object files *- The use of header files (.h files) and #include Standard header files available in /usr/include/*.h E.g. #include gets /usr/include/math.h -- Use a special directory -------------------------------------------- You are advised to make a sub-drectory for your C programming exercises. E.g. use the command "mkdir c". If you do all your C work in that directory you will not risk files produced by the C compiler overwriting other files that happen to have the same name. -- Compilers & ANSI C ------------------------------------------------- You should be aware that there are two versions of the C programming language in common use. The version used throughout this teach file is the 'original' version, as described in the first edition of K&R. The second edition of K&R describes ANSI C, which is a (relatively) new standard definition of the language. This latter version should eventually supercede the original, but it will take a long time for the old version to die out completely. There are many changes in the ANSI version, but most of them are trivial or rather esoteric, and not to be worried about at this stage. There is a full list of changes in an appendix of the second edition of K&R. The most important change is the syntax of function definitions. In ANSI C the types of parameters are defined within the brackets, rather than separately. Hence the definition for the max function described below would be written: max (int x, int y) Compilers for both versions are available on tsuna & tsunb. For the original C, use the compiler 'cc' as normal. If you want to use ANSI C, there is a compiler called 'gcc' available. Beware that the UNIX 'make' command and the ved command 'CCOMP' both use 'cc' by default. -- A first exercise --------------------------------------------------- Make a copy of the file ~steveea/examples.c Compile it with the unix command % make examples This will look for a file callex examples.c, compile it and produce a new executable file called "examples". Use "ls -l" to see that it exists, that (for you) it has mode "rwx" i.e. is readable, writeable, and executable. You can then run it and see what output it produces, i.e. % examples Now compile and run the program inside VED using the ccomp command. Read HELP * CCOMP to find out what it does. I.e. edit the file, examples.c, then do ccomp The first time you use ccomp in any session there will be a delay while it is autoloaded. The output (if any) is read into a new VED file. Compare the contexts of examples.c and the output produced and try to understand how the program produces the output. -- Things to note in examples.c --------------------------------------- 1. A line starting with "#define" defines a macro, ie. something to be substituted in the text during the pre-processing stage. So "max" is defined as a function (i.e. a procedure, in POP-11 terminology), whereas "MAX" is defined as a macro. The lecture explained the difference. 2. There is no special keyword telling you that a function is being defined. The mere occurrence of max(x,y) outside of any pre-existing context tells the compiler that a function called "max" which takes two arguments, is being defined. For a function of no arguments, empty parentheses are used, e.g. foo() Later you'll learn that the type of the result produced by the function can be specified before the name, e.g. int max(x,y) says that the function max produces a result of type int. In fact type int is assumed as the default for all functions. Try making it explicit. I.e. put "int" before "max" and check that the file can still be compiled and run successfully. 3. The arguments to the function must have their type specified in the function definition. This is done immediately after the function header, e.g. int x,y; This is a declaration. In the definition of "max" try changing "int" to "long" and see if it affects anything in the program. It shouldn't since nothing in examples.c uses the difference between short and long integers. 4. The various functions defined in the file are invoked via the function called "main" defined at the end of the file. When a C program starts running it invokes the function called "main". So every program must have one function with that name. You can't, as in Pop-11 just define a function called "fred" then write "fred();" and expect it to run. So a C program consists of a collection of declarations and function definitions including a function called "main" which is automatically run when the program starts. -- Play with the examples.c file -------------------------------------- Try changing examples.c in various ways, e.g. with different numerical arguments or different things to be printed out in the printf commands. Then recompile and re-run it, using either "make" or ccomp. Also try changing the function -main- at the end of the file so that it invokes the other functions with different arguments, and check that it works as expected. Try to generate compile time error messages, as follows (do only one of these at a time then compile the file): Insert or delete a semi-colon or comma Insert or delete one or more of { } ( ) N.B. If you have any problems and cannot understand what is happening, try the following a. ask another student for help b. try the demonstrator (announced in a mail message) c. make a note of the problem and bring it to the tutorial -- Change some of the arguments to printf ----------------------------- -printf- is a very useful function. It takes a string (delimited by double quotes, unlike POP-11 strings), and possibly some additional arguments. It prints the string. If there are additional arguments they are printed in the middle of the string at locations indicated by the occurrence of percent symbols "%". The % is followed by one or more characters indicating how the thing is to be printed, e.g. as a decimal integer (%d), as a floating point number (%f) as an octal number (%o), and so on. The unix man file "man printf" tells you all about the options. Try adding some extra printf statements into -main-, e.g. filling in something appropriate into the arguments in the following: printf("\nThe sum of %d and %d is %d", ... , ... , ...); printf("\n%f divided by %f is %f, ..., ..., ...); (use "+" for addition and "/" as in POP-11). The "\n" represents a newline character, as in POP-11 strings. (See HELP *ASCII). Try out the field specification options, as in the definitions of -printslope- and -test3- in the examples.c file. E.g. %3d means print a decimal integer in 3 spaces %8.4f means print a float with 8 columns before and 4 figures after the decimal point. %-10d means print the integer left-justified within a 10 column field. %% means actually print a (single) percentage sign! There are many more options in printf! -- Conditional expressions and statements ----------------------------- Roughly, an expression denotes an object, the result of evaluating the expression, or else a location where something is to be stored, whereas a statement (or imperative) tells the computer to do something. An expression can be used as argument to a function, or on the right or left of an assignment operator. The notion of statement and expression are not totally distinct, since often the evaluation of an expression involves DOING something. In C an expression can be turned into a statement by following it with a semi-colon. For a definition of the syntax of expressions and statements in C look at TEACH C_SYNTAX, or the C reference manual at the back of K&R, which ends with a syntax summary (section 18). Note the peculiar syntax for conditional expressions in C. In the definition of function "max" in the examples.c file, the expression ? : has the value of expression1 if the condition is is not FALSE, otherwise expression2. In C, the integer 0 counts as FALSE, and everything else as TRUE. Unlike the conditional expression, the conditional statement in C is (a bit) more readable: if (expression) statement or if (expression) statement1 ELSE statement2 Note that there is no "then" or "endif" and that the parentheses around the condition expression are essential. -- Simple and compound statements ------------------------------------- If you want the "then" or "else" statements to be compound, i.e. made up of a sequence of statements (instructions) then you have to use curly braces around the compound statement, e.g. if (expression) {statement1; statement2; statement3;} This is a general rule in C. There are many places where you can have EITHER a simple statement like x = 3; or printf("my string"); OR a compound statement, such as { x = 3; printf("The value of x is %d",x); } No semi-colon is needed after the right brace "}" in a compound statement. However, you do need a semicolon before it, if the last sub-statement is not compound. -- Exercise, define test5(x,y) ---------------------------------------- Add to examples.c a function called "test5". It should take two arguments, both integers, x and y (remember to declare them). If x is bigger than y it should execute two printf statements saying so and two other statements if y is bigger. I.e. use the format if ( ... ) { printf( ...); printf( ...); } else { ...; ...; } (by filling in suitable bits of C in place of the dots). If x is bigger, then -test5- should first print out, on a line by itself: The first number is bigger followed by, on the next line, namely, ... with the bigger number instead of the dots. If y is bigger it should print out something similar, also using two separate printf statements. To make the program run -test5-, add instructions to do so with, different pairs of numbers, into the definition of "main" at the end. Compile and run it and make sure it does what you expect. -- Loops in C --------------------------------------------------------- In addition to the form for conditional statements, there are similar forms for various loop statements in C, e.g. while (expression) statement do statement whle (expression) for (expression-1; expression-2; expression3) statement In each case where a statement is specified you can use either a simple statement terminated by ";" or a compound statement in braces. There is an example of a for statement in examples.c, in the function -test4-. In "for" statements expression-1 is the first thing to be done. It is optional. It is often used to initialise some variable that will take different values each time round the loop. In the definition of -test4- it is n=p which in C is not an equality test but an assignment, equivalent to the Pop-11 p -> n. That is, in C assignments go from right to left. (For equality use n == p. In fact C has a whole variety of assignment operators which both do an assignment and also do something else, like increment or decrement the variable on the right.) Expression-2 is a condition, which is tested before the loop statement on each iteration. If it evaluates to anything other than 0 (false), the loop continues. Expression-2 is optional and defaults to 1, i.e. true. So if Expression-2 is missing that is equivalent to an infinite loop. Expression-3, which is also optional, indicates what is to be done after each execution of the loop statement to prepare for the next one. In examples.c, Expression-3 is "n++" which means "increment the value of n by 1". I.e. it is roughly equivalent to "n = n + 1", which, in POP-11 would be "n + 1 -> n". The n++ is not exactly equivalent to the assignent, since in C n++ is an expression that returns the value of n before the increment, whereas the value of "n = n + 1" is the new value of n. In the context of the "for" statement the difference does not matter, since the value of expression-3 is not used. Try changing test4 so that instead of n++ it has n = n + 1 for expression-3. Compile and run the program to make sure it still produces the same result. Then change it so that n is incremented by some other number, instead of 1. -- Exercise define addup(start,incr,fin) ------------------------------ In a file called add.c define a function called addup, which takes three arguments, start, incr, fin all of type "long" (i.e. long integer) and produce a result of type long, as follows. The result is the sum of all numbers less than or equal to fin, got by repeatedly adding incr to start. Include start in the total. So addup(1,1,3) = 1 + 2 + 3 = 6 addup(2,3,10)= 2 + 5 + 8 = 15 etc. The function heading would have to start thus (with an appropriate comment between /* ... */): /* Function -addup- returns result of type long, and takes arguments of type long */ long addup(start,incr,fin) long start, incr, fin; (You could use "int" instead of "long", and then you would be restricted to smaller numbers.) The function body is enclosed in braces { ... } You could declare a local variable of type total, initialised to 0. long total=0; Then in a loop, repeatedly add the value of start to total, after adding incr to start. Use a for loop, with the syntax for(; ; ); Note that any of , and can be empty. In this case, start, incr, fin and total all have values initially so no initialisation is needed. The continuation test is start <= fin The step is start = start + incr The loop body can be simple or complex. E.g. a simple version might be: total=start+total; A more complex version would print out the current values of start and total, in which case braces would have to be used, something like: { total=start+total; printf("start = %d, total=%d\n",start,total); } The definition would have to end with a command saying what result is to be returned, e.g. return(total); Put all that together in a definition of -addup- then in the same file put a definition of -main- that runs and prints out the result of a series of tests, e.g. something like: main() { printf("Total for 1,1,3 is %d\n",addup(1,1,3)); printf("Total for 2,2,8 is %d\n",addup(2,2,8)); printf("Total for 0,5,100 is %d\n",addup(0,5,100)); } -- Assignment: define test6(start,incr,fin) ----------------------------- You could do this one in a file called av.c as it is concerned with averages. Define a procedure called "test6" that returns a float as result and takes as arguments three floats (decimal numbers), declared as float start,incr,fin; It should cycle through all the numbers from start to fin got by repeatedly adding incr to start, and find their average. E.g. test6(3.0,1.0,5.0) should be 3.0 + 4.0 + 5.0 --------------- i.e. 4.0 3 It will have to add up the total set of numbers, e.g. using a variable of type float called "total", and it will have to divide the total by the number of values used. One way to do this is to use a counter of type int, which is incremented by 1 each time round the loop, then used to divide the total. To see how to define and initialise a local variable which is not one of the formal parameters of a function look at function -slope- in examples.c It has two local variables of type int, namely dx and dy. In your definition of -test6- use one of type int, and one of type float, in two separate local declarations. Use a "for" loop analogous to that in test4, but with a different increment, and instead of printing out intermediate values add up the total and increment the counter. The final statement in the function body could be return(result/count); Define the function and put it into a file called av.c, and in the -main- function put in several tests, including the following: printf("\nThe value of test6(3.0,1.0,10.0) is %f",test6(3.0,1.0,10.0)); printf("\nThe value of test6(0.0,2.5,10.0) is %f",test6(0.0,2.5,10.0)); printf("\nThe value of test6(0.0,3.5,10.0) is %f",test6(0.0,3.5,10.0)); Alternatively, use a function print_test6(start,incr,fin) which does the equivalent of one of the above printf statements, analogous to the function -printslope- in examples.c, then use it in -main- to test your definition of -test6-. Check that the results printed out are correct. ASSIGNMENT: hand in a report describing the contents of your av.c, showing a test run, and discussing any limitations you can think of. (See TEACH * REPORTS, ignoring the bits that are specific to POP-11.) -- Assignment: fix printslope and slope --------------------------------- Look at the definitions of -slope-, -printslope- and -test3- and check that the output produced is correct. Copy them into a file called slope.c. Now try changing some of the numbers in test3 so that so that the slopes are not whole numbers. e.g. printslope(0,0,2,3) should print a slope of 1.5 The problem is in -slope-. The value is calculated by dividing two integers, dy and dx. C is defined so that if you divide an integer by an integer the result is an integer, truncated downwards. E.g. 3/2 becomes 1 instead of 1.5. So you need to change slope so that it takes floats, and change the declarations of dx and dy so that they are floats. Change printslope so that it takes floats. Then inside the function main, test it with arguments like these: printslope(0.0, 0.0, 2.0, 3.0); printslope(-2.0, 0.0, 2.0, 3.0); and others. Make sure that it produces the right results. WARNING: in the course of your tests if you have made a mistake you may find that C gives you the unfriendly message "floating point exception core dumped" in which case it will have created creted a file called "core" in your directory. This mechanism is designed so that when programs are very complex and fail inexplicably you can use a debugging tool to analyse the state of the core dump. (See "man adb"). However, we shall not get into that in this course. So just remove the file called "core" and try again. If you get totally stuck ask for help. ASSIGNMENT: write a report on your modified versions of slope and printslope, explaining what you had to change to make it produce the right results, and why. -- Using a cast to transform a type ----------------------------------- It is possible to keep -printslope- and -slope- as functions that take ints, keep dx and dy as int variables, but inside the function -slope- convert dy to a float before doing the division, using (float) dy/dx Here "(float)" is called a "cast". A cast is used to transform an expression of one type (here int) to an expression of another type (here float). Casts are often used in C, but have to be used with great care. It is not necessary to cast both dx and dy as floats since the divide operator "/" will do that if given one float and one integer. So you could also try dy/(float)dx OPTIONAL assignment. Include versions of slope and printslope that take integers and produce floats as necessary. Test them on integer co-ordinates that correspond to slopes that are not whole numbers, and write a report on what you have done and how it works, including printout demonstrating the tests. -- Using macros ------------------------------------------------------- Notice the difference between the function "max" defined in examples.c and the "macro" MAX. The difference is that calls to the former produce a subroutine call, whereas the latter produces "in-line" code. I.e. the text is substituted at compile time. So the compiled code is bigger but should run faster. (Though too-much inline code can make the program so big that paging slows it down!) Try putting a new macro definition at the top of the file. #define PP(a,b) printf("\nThe bigger of a and b is: %d",max(a,b)) Then instead of typing the following in test1, you could type printf("\nThe bigger of 3 and 4 is: %d",max(3,4)); PP(3,4); Try defining a new function test1a, which is like test1, but uses PP. Insert an appropriate call of test1a into main, then recompile and run the program, checking that the use of PP produces the right output. Play with other changes, till you feel you understand everything in examples.c, before moving on. -- SEE ALSO ----------------------------------------------------------- TEACH * C.refs HELP * C_SYNTAX VED ~steveea/examples.c --- $poplocal/local/teach/c.ex1 --- Copyright University of Sussex 1992. All rights reserved. ----------