CPS 125 Alexander Ferworn Week 6 User Defined Functions What we will cover this week ----------------------------- o User Defined Functions o Function Scope o Modifying Function Arguments o Call by Value o Call by Reference o Pointers Functions in C --------------- o Almost all programming languages have some equivalent of the function. o You may have met them under the alternative names subroutine or procedure. o Some languages distinguish between functions which return variables and those which don't. C assumes that every function will return a value. o If the programmer wants a return value, this is achieved using the return statement. o If no return value is required, don't use the return statement. o Unless declared before the function name all functions are assumed by the compiler to return an int. This is a big source of bugs so watch out! o Functions can only return 1 thing. an example: Here is a function which raises a double to the power of long int, and returns the result as a double. double power(double val, long pow) { double ret_val = 1.0; long i; for(i = 0; i < pow; i++) ret_val = ret_val * val; return(ret_val); } o The function follows a simple algorithm, multiplying the value by itself pow times. o A for loop is used to control the number of multiplications, and variable ret_val stores the value to be returned. o Careful programming has ensured that the boundary condition is correct too. ie . x^0 = 1.0. Let us examine the details of this function. double power(double val, unsigned pow) o This line begins the function definition. o It tells us the type of the return value, the name of the function, and a list of arguments used by the function. o The arguments and their types are enclosed in brackets, each pair separated by commas. o The body of the function is bounded by a set of curly brackets. o Any variables declared here will be treated as local (can only be accssed inside the function) return(ret_val); o On reaching a return statement, control of the program returns to the calling function. o The bracketed value is the value which is returned from the function. o If the final closing curly bracket is reached before any return value, then the function will return automatically. The example function can be called by a line in another function which looks like this result = power(val, pow); o This calls the function power assigning the return value to variable result. another example: o Here is an example of a function which does not return a value. void error_line(int line) { fprintf(stderr, "Error in input data: line %d\n", line); } o The definition uses type void which is optional but recommended. o It shows that no return value is used. o Otherwise the function is much the same as the previous example, except that there is no return statement. o Some void type functions might use return, but only to force an early exit from the function, and not to return any value. o This is rather like using break to jump out of a loop. This function also demonstrates a new feature. fprintf(stderr, "Error in input data: line %d\n", line); o This is a variant on the printf statement, fprintf sends its output into a file. o In this case, the file is stderr. stderr is like standard input and output but for error messages. o It is usually connected to the console of the computer system, so this is a good way to display error messages from your programs. o Messages sent to stderr will appear on screen even if the normal output of the program has been redirected to a file or a printer. The function would be called as follows error_line(line_number); Things you need to know about functions Function can appear in programs in 3 forms. 1) Definition: A declaration that actually defines what a function does and returns as well as the number and type of arguments. 2) Allusion: Declares a function that is defined elsewhere but you still define what it returns and the number and type of arguments--this is done to aid the compiler in doing type checking. 3) Call: Invokes the function causing execution to jump to that function We have seen definition and call in the previoius examples. Allusions are used most as prototypes as will be seen below an allusion example; char fred(long); /* a prototype (allusion) */ void main() { .... c = fred(10); /* a call */ ... } char fred(long val) /* a definition */ { .... } notes: The prototype allows us to defer the actual definition of fred() until after main yet still gives the compiler enough information to spot syntax errors for us. Scope of Function Variables ---------------------------- o Only a limited amount of information is available within each function. o Variables declared within the calling function can't be accessed unless they are passed to the called function as arguments. o The only other contact a function might have with the outside world is through global variables (discussed later). o Local variables are declared within a function. They are created anew each time the function is called, and destroyed on return from the function. o Values passed to the function as arguments can also be treated like local variables. o Static variables are slightly different as they persist even when the function is exited. o The value of a static variable can be accessed when the function is called again. (it remembers what it had in it.) an example: ... void example_func() { static long temp; ... An exercise: o Write a program to allow the user to input two numbers x and y. o The program should print x! + y!. Suggested Solution: #include <stdio.h> int factor(int num); void main() { int x,y,i; printf("Enter x and y.\n"); scanf("%d%d",&x,&y); printf("The answer is: %d\n",factor(x) + factor(y)); } int factor(int num) { int total = 1; while (num) { total = total * num; num --; } return total; } note: o We can user factor() in the printf() call so we can actually defer the definition of factor() since we know what it does but not necessarily how (yet). o This is call "abstraction"--knowing what and not worrying about how and is one of the fundamental principals of computer science. Another Example: /* Let's illustrate a nested loop and a function call this program call a function which prints out n rows by m columns of stars, where the user inputs n and m */ #include <stdio.h> void stars(int n, int m) /* note that no values are changed in stars */ { int j,k; for(j=0;j<n;j++) /* loop for rows */ { for(k=0;k<m;k++) /* loop for cols */ printf("*"); printf("\n"); } } void main() { int rows, cols; scanf(%d%d",&rows,&cols); stars(rows,cols); } A first exercise ------------------ exercise: Write a program which asks the user to input a single c or f. If they input a 'c' this means that they want to convert degrees C to degrees F. If they input a 'f' they want to convert degrees F to degrees C. In each case print out a table going from -40 to 40 degrees. Make sure the user can repeat the process if they want. suggested solution: /* Table temp converter */ #include <stdio.h> #define TRUE 1 #define FALSE 0 /* This is a user define function which validates their input it takes one character argument and returns TRUE if that character is F,f,c or C otherwise it returns false */ int test(char tester) { switch(tester) { case 'f': return TRUE; case 'F': return TRUE; case 'c': return TRUE; case 'C': return TRUE; default: return FALSE; } } /* Another user defined function which prints a table */ void ftoc() { float f; printf("F degrees\tC degrees\n"); printf("---------\t---------\n"); for(f= -40;f<=40; f=f + 5.0) printf("%f\t%f\n",f,/*****put f to c equation here*****/); } /* Another user defined function which prints a table */ void ctof() { float c; printf("C degrees\tF degrees\n"); printf("---------\t---------\n"); for(c= -40;c<=40; c=c + 5.0) printf("%f\t%f\n",c,/*****put c to f equation here*****/); } void main() { char inchar; printf("Enter c or f (or anything else to stop)\n"); scanf("%c",&inchar); fflush(stdin); while(test(inchar)) { /* note you don't have to test for f...why? */ if(inchar == 'c' || inchar == 'C') ftoc(); else ctof(); printf("Enter c or f (or anything else to stop)\n"); scanf("%c",&inchar); fflush(stdin); } } Modifying Function Arguments ---------------------------- An exercise: Write a main() function and one other function. main() should pass as arguments 2 long variables. The function should then interchange their values. Their values should then be printed in main(). Suggested solution (wrong) #include <stdio.h> /* this will not work */ void swap(long a, long b) { long temp; temp = a; a = b; b = temp; } void main() { long first=1,second=2; swap(first,second); printf("%d %d\n",first,second); } o Some functions work by modifying the values of their arguments. o This may be done to pass more than one value back to the calling routine, or because the return value is already being used in some way. o C requires special arrangements for arguments whose values will be changed. The concept of "Call by Value" ------------------------------- o You can treat the arguments of a function as variables, however direct manipulation of these arguments won't change the values of the arguments in the calling function. o The value passed to the function is a copy of the calling value. o This value is stored like a local variable, it disappears on return from the function. o This is why the above example will not work. The concept of "Call by Reference" ----------------------------------- o There is a way to change the values of variables declared outside the function. o It is done by passing the addresses of variables to the function. o These addresses, or pointers, behave a bit like integer types, except that only a limited number of arithmetic operators can be applied to them. o They are declared differently to normal types, and we are rarely interested in the value of a pointer. o It is what lies at the address which the pointer references which interests us. o The function must now be written to use the value at that address (or at the end of the pointer). o On return from the function, the desired value will have changed. o We manipulate the actual value using a copy of the pointer. Pointers in C -------------- Pointers are not exclusive to functions, but this seems a good place to introduce the pointer type. Imagine that we have an int called i. Its address could be represented by the symbol &I (recall & is the "address of" operator). If the pointer is to be stored as a variable, it should be stored like this. long *pi = &i; int * is the notation for a pointer to an int. & is the operator which returns the address of its argument. When it is used, as in &i we say it is referencing i. The opposite operator, which gives the value at the end of the pointer is *. An example of use, known as de-referencing pi, would be i = *pi; note: Take care not to confuse the many uses of the * sign; Multiplication, pointer declaration and pointer de-referencing. This is a very confusing subject, so let us illustrate it with an example. The following function fiddle takes two arguments, x is an int while y is a pointer to int. It changes both values. an example: fiddle(int x, int *y) { printf(" Starting fiddle: x = %d, y = %d\n", x, *y); x ++; (*y)++; printf("Finishing fiddle: x = %d, y = %d\n", x, *y); } since y is a pointer, we must de-reference it before incrementing its value. A very simple program to call this function might be as follows. main() { int i = 0; int j = 0; printf(" Starting main : i = %d, j = %d\n", x, y); printf("Calling fiddle now\n");. fiddle(i, &j); printf("Returned from fiddle\n"); printf("Finishing main : i = %d, j = %d\n", x, y); } Note here how a pointer to int is created using the & operator within the call fiddle(i, &j);. The result of running the program will look like this. Starting main : i = 0 ,j = 0 Calling fiddle now Starting fiddle: x = 0, y = 0 Finishing fiddle: x = 1, y = 1 Returned from fiddle Finishing main : i = 0, j = 1 After the return from fiddle the value of i is unchanged while j, which was passed as a pointer, has changed. To summarise: -------------- If you wish to use arguments to modify the value of variables from a function, these arguments must be passed as pointers, and de-referenced within the function. Where the value of an argument isn't modified, the value can be passed without any worries about pointers. An exercise: Write a main() function and one other function. main() should pass as arguments 2 long variables. The function should then interchange their values. Their values should then be printed in main(). This time do it right!!!! Suggested solution (right!) #include <stdio.h> /* this will not work */ void swap(long *a, long *b) { long temp; temp = *a; *a = *b; *b = temp; } void main() { long first=1,second=2; swap(&first,&second); printf("%d %d\n",first,second); }