Week 10 - Chapter 9 Functions Review Chapter 8 Homework ------------------------- Functions --------- Functions are one of the main building blocks of the C language. We have been using functions, in various forms, since day 1. They are a series of commands or expressions, grouped together, and given a name. Some take parameters, or arguments, some do not. Some return useful values, some do not. The primary reason for using functions is to avoid code duplication. Reuse. Sample function: float average(float a, float b) { return((a + b) / 2.0); } parts list: return type (float) name (average) argument list (float a, float b) body (everything inside the {}) Note that many of our previous rules apply, such as our C naming conventions, our use of spacing and indents, and keywords. When we use a function (or "call" the function), we use the function name and the parameters. printf is a function that we have been using since the beginning. Also note that main is a function that we have been using since the beginning. The return value can be assigned to a variable or used by printf: float a = 2.0; float b = 4.0; float c; printf("Average is %f\n", average(a,b)); c = average(a,b); The body of the function can include many lines of code, just like our main has in the past. A function can return void, which means that it returns nothing. A function that returns void is typically one that does all that it needs to do internally, and has nothing to say about what it has done. void getEnter(void) { printf("Press ENTER to continue..."); while(getchar() != '\n'); return; } A void-return function can call return at any point, but must never return a value. Many programmers leave the final return out, but I always include it so that it is clear throughout the code that this is a void function. As you can see, a function might also take a void parameter list, meaning that it does not need any further information to do its job. Most of the time, though, functions do require some sort of input. long get_clock(void) { return(clock()); } Defining Functions ------------------ Like you do with variables, you should always define functions before they are used. We have also been doing this since day 1, since the include files are used to define functions like scanf and printf. To create a function definition, we can do one of two things. First, we can actually write the function before it is used: float area(float a, float b) { return(a * b); } main() { printf("Area is %f\n",area(2.5,3)); return(0); } Here there is no problem, because the function is declared before it is used. Such declaration, however, is rare, because most programmers like to have main be the first function in the source code file. So this is the more typical way of writing this code: main() { printf("Area is %f\n",area(2.5,3)); return(0); } float area(float a, float b) { return(a * b); } The problem now is that when the compiler encounters the call to area in main, it knows nothing about area. What does it return? What are its parameters, how many are there? Did we call it correctly? C compilers make some assumptions about undeclared functions, because it has to do something with them. First, it assumes they return an int. Next, they assume the first call has the right number of parameters, and that they have the correct types. So, here, the compiler assumes that area returns an int, and takes two parameters, a float and an int. When it gets to the actual declaration of area, it gets confused - area returns a float and takes two floats. Something's wrong, and the compiler will complain. What we need to do is offer the compiler a snapshot of the function, something called a "function prototype." A prototype consists of just the function name, its return value, and a list of the types of its parameters: float area(float, float); Prototypes are typically all bunched up in one section in the code before main. Now, when the compiler gets to the call to area, it knows all it needs to know about the function. This time, it might warn that the 3 is not a float, but other than that, it is all set. The parameter list can include the names of the parameters, but I always leave them off, because the compiler ignores them anyway. The prototype could be: float area(float height, float width); and it would still be OK, even though in the real function we use a and b. When there is no parameter list, again, use void: long get_clock(void); Stubs ----- A nice thing about writing large programs is the concept of the function stub. When you're writing a program, you often know that you'll need a function to do something, but you just can't write everything at once. So you can write a stub to provide a calling point in the program, that does nothing. Say we have a program that will ask the user for a line of data, but we have not written that function yet: main() { printf("Please type in your name: "); get_name(); ... return(0); } void get_name(void) { return; } Of course, you'll want to prototype stubs, too. One more detail --------------- Be sure to always use the () when calling a function even when the function takes a void parameter list. getchar is an example - you must call it as getchar() even though you pass it nothing. Reason? What is a function name without the () following it? A variable name. Arguments --------- We'll discuss now a very important concept in functions, the concept of how variables are passed to the function, and what can happen with them. Typically, parameters are passed by value. In other words, the value of the parameter is copied into the function's parameter list variables. float square(float a) { a *= a; return(a); } main() { float num; num = 3.0; square(num); } In square(), a receives the value of num and multiplies itself by itself. In square, a is equal to 3, then is equal to 9. It returns 9. In main, num is set to 3, and that value is passed to square. But when square returns, num still has a 3 in it. Its value was not changed by the function call. This is a good thing - our data is protected when it is passed by value. We will learn how to pass values by reference in a couple of weeks. You can also pass an array to a function, which can be particularly useful when you have lots of arrays that you need to do different things to. There is one problem, though, and that is that when you pass an array, the function that gets the array has no idea how large the array is. So we typically have to provide a second parameter to tell the function how large the array is: int sum_array(int a[], int n) { int total = 0; int i; for (i=0; i