Review

I l @ ve RuBoard

Review

First, what is a function? A function is a self-contained unit of program code designed to accomplish a particular task. A function in C plays the same role that functions, subroutines, and procedures play in other languages, although the details might differ . Some functions cause action to take place. For example, printf() causes data to be printed on your screen. Some functions find a value for a program to use. For instance, strlen() tells a program how long a certain string is. In general, a function can both produce actions and provide values.

Why should you use functions? For one, they save you from repetitious programming. If you have to do a certain task several times in a program, you need to write an appropriate function only once. The program can then use that function wherever needed, or you can use the same function in different programs, just as you have used putchar () in many programs. Two, even if you do a task just once in just one program, using a function is worthwhile because functions make a program more modular, hence easier to read and easier to change or fix. Suppose, for example, you want to write a program that does the following:

 Read in a list of numbers Sort the numbers Find their average Print a bar graph 

You could use this program:

 #include <stdio.h> #define SIZE 50 int main(void) {   float list[SIZE];   readlist(list, SIZE);   sort(list, SIZE);   average(list, SIZE);   bargraph(list, SIZE);   return 0; } 

Of course, you would also have to write the four functions readlist() , sort() , average() , and bargraph() mere details. Descriptive function names make it quite clear what the program does and how it is organized. You can then work with each function separately until it does its job right, and if you make the functions general enough, you can use them in other programs.

Many programmers like to think of a function as a "black box" defined in terms of the information that goes in (its input) and the value or action it produces (its output). What goes on inside the black box is not your concern, unless you are the one who has to write the function. For example, when you use printf() , you know that you have to give it a control string and, perhaps, some arguments. You also know what output printf() should produce. You never had to think about the programming that went into creating printf() . Thinking of functions in this manner helps you concentrate on the program's overall design rather than the details. Think carefully about what the function should do and how it relates to the program as a whole before worrying about writing the code.

What do you need to know about functions? You need to know how to define them properly, how to call them up for use, and how to set up communication between functions. To refresh your memory on these points, we will begin with a very simple example and then bring in more features until you have the full story.

Creating and Using a Simple Function

Our modest first goal is to create a function that types 65 asterisks in a row. To give the function a context, we will include it in a program that prints a simple letterhead. Listing 9.1 presents the complete program. It consists of the functions main() and starbar() .

Listing 9.1 The lethead1.c program.
 /* lethead1.c */ #include <stdio.h> #define NAME "MEGATHINK, INC." #define ADDRESS "10 Megabuck Plaza" #define PLACE "Megapolis, CA 94904" #define LIMIT 65 void starbar(void);  /* prototype the function */ int main(void) {     starbar();     printf("%s\n", NAME);     printf("%s\n", ADDRESS);     printf("%s\n", PLACE);     starbar();           /* use the function */     return 0; } void starbar(void)       /* define the function */ {     int count;     for (count = 1; count <= LIMIT; count++)         putchar(`*');     putchar(`\n'); } 

Here is the output:

 ***************************************************************** MEGATHINK, INC. 10 Megabuck Plaza Megapolis, CA 94904 ***************************************************************** 

There are several major points to note about this program:

  • It uses the starbar identifier in three separate contexts: a function prototype that tells the compiler what sort of function starbar() is, a function call that causes the function to be executed, and a function definition that specifies exactly what the function does.

  • Like variables , functions have types. Any program that uses a function should declare the type for that function before it is used. Therefore, this ANSI C prototype precedes the main() function definition:

 void starbar(void); 

The parentheses indicate that starbar is a function name. The first void is a function type; the void type indicates that the function does not return a value. The second void (the one in the parentheses) indicates that the function takes no arguments. The semicolon indicates that you are declaring the function, not defining it. That is, this line announces that the program uses a type void function called starbar() and that the compiler should expect to find the definition for this function elsewhere. For compilers that don't recognize ANSI C prototyping, just declare the type, as follows :

 void starbar(); 

Note that some very old compilers don't recognize the void type. In that case, use type int for functions that don't have return values.

  • The program places the starbar() prototype before main() ; instead, it can go inside main() , at the same location you would place any variable declarations. Either way is fine.

  • The program calls (invokes, summons) the function starbar() from main() by using its name followed by parentheses and a semicolon, creating this statement:

 starbar(); 

This is one form for calling up a function, but it isn't the only one. Whenever the computer reaches a starbar(); statement, it looks for the starbar() function and follows the instructions there. When finished with the code within starbar() , the computer returns to the next line of the calling function main() , in this case (see Figure 9.1).

Figure 9.1. Control flow for lethead1.c (refer to Listing 9.1).
graphics/09fig01.jpg
  • The program follows the same form in defining starbar() as it does in defining main() . It starts with the type, name, and parentheses. Then it supplies the opening brace, a declaration of variables used, the defining statements of the function, and then the closing brace (see Figure 9.2). Note that this instance of starbar() is not followed by a semicolon. The lack of a semicolon tells the compiler that you are defining starbar() instead of calling or prototyping it.

    Figure 9.2. Structure of a simple function
    graphics/09fig02.jpg
  • The program includes starbar() and main() in the same file. You can use two separate files. The single-file form is slightly easier to compile. Two separate files make it simpler to use the same function in different programs. If you do place the function in a separate file, then you would also place the necessary #define and #include directives in that file. We will discuss using two or more files later. For now, we will keep all the functions together. The closing brace of main() tells the compiler where that function ends, and the following starbar() header tells the compiler that starbar() is a function.

  • The variable count in starbar() is a local variable. This means it is known only to starbar() . You can use the name count in other functions, including main() , and there will be no conflict. You simply wind up with separate, independent variables having the same name.

If you think of starbar() as a black box, its action is printing a line of stars. It doesn't have any input because it doesn't need to use any information from the calling function. It doesn't provide (return) any information to main() , so starbar() doesn't have a return value. In short, starbar() doesn't require any communication with the calling function.

Let's create a case where communication is needed.

Function Arguments

The letterhead shown earlier would look a little nicer if the text were centered. You can center text by printing the correct number of leading spaces before printing the text. This is similar to the starbar() function, which printed a certain number of asterisks, but now you want to print a certain number of spaces. Instead of writing separate functions for each task, we'll follow the C philosophy and write a single, more general function that does both. We'll call the new function n_char() (to suggest displaying a character n times). Instead of having the display character and number of repetitions built into the function, we will use function arguments to convey those values.

Let's get more specific. The bar of stars is 65 characters wide, and the function call n_char(`*', 65) should print that. What about spaces? MEGATHINK, INC. is 15 spaces wide, so in the first version, there were 50 spaces following the heading. To center it, you should lead off with 25 spaces, which will result in 25 spaces on either side of the phrase. Therefore, you could use the call n_char(' ', 25) .

Aside from using arguments, the n_char() function will be quite similar to starbar() . One difference is that it won't add a newline the way starbar() does because you might want to print other text on the same line. Listing 9.2 shows the revised program. To emphasize how arguments work, the program uses a variety of argument forms.

Listing 9.2 The lethead2.c program.
 /* lethead2.c */ #include <stdio.h> #include <string.h>       /* for strlen() prototype      */ #define NAME "MEGATHINK, INC.' #define ADDRESS "10 Megabuck Plaza" #define PLACE "Megapolis, CA 94904" #define LIMIT 65 #define SPACE ` ' void n_char(char ch, int num); int main(void) {   int spaces;   n_char(`*', LIMIT);      /* using constants as arguments */   putchar(`\n');   n_char(SPACE, 25);       /* using constants as arguments */   printf("%s\n", NAME);   spaces = (65 - strlen(ADDRESS)) / 2;                            /* Let the program calculate    */                            /* how many spaces to skip      */   n_char(SPACE, spaces);   /* use a variable as argument   */   printf("%s\n", ADDRESS);   n_char(SPACE, (65 - strlen(PLACE)) / 2);                            /* an expression as argument    */   printf("%s\n", PLACE);   n_char(`*', LIMIT);   putchar(`\n');   return 0; } /* here is n_char() */ void n_char(char ch, int num) {   int count;   for (count = 1; count <= num; count++)   putchar(ch); } 

Here is the result of running the program:

 *****************************************************************                          MEGATHINK, INC.                         10 Megabuck Plaza                        Megapolis, CA 94904 ***************************************************************** 

Now let's review how to set up a function that takes arguments. After that, you'll look at how the function is used.

Defining a Function with an Argument: Formal Arguments

The function definition begins with this ANSI C declaration:

 void n_char(char ch, int num) 

This line informs the compiler that n_char() uses two arguments called ch and num , that ch is type char , and that num is type int . Both the ch and num variables are called formal arguments . Like variables defined inside the function, formal arguments are local variables, private to the function. That means you don't have to worry about duplicating variable names used in other functions. These variables will be assigned values each time the function is called.

Note that the ANSI C form requires that each variable be preceded by its type. That is, unlike the case with regular declarations, you can't use a list of variables of the same type:

 void dibs(int x, y, z)          /* invalid function header */ void dubs(int x, int y, int z)  /* valid function header   */ 

ANSI C also recognizes the pre-ANSI form:

 void n_char(ch, num) char ch; int num; 

Here, the parentheses contain the list of argument names, but the types are declared afterward. Note that the arguments are declared before the brace that marks the start of the function's body, but ordinary local variables are declared after the brace. This form does enable you to use comma-separated lists of variable names if the variables are of the same type, as shown here:

 void dibs(x, y, z) int x, y, z;          /* valid */ 

The intent of the standard is to phase out the pre-ANSI form. You should be aware of it so that you can understand older code, but you should use the modern form for new programs.

Although the n_char() function accepts values from main() , it doesn't return a value. Therefore, n_char() is type void .

Now let's see how this function is used.

Prototyping a Function with Arguments

We used an ANSI prototype to declare the function before it is used:

 void n_char(char ch, int num); 

When a function takes arguments, the prototype indicates their number and type by using a comma-separated list of the types. If you like, you can omit variable names in the prototype:

 void n_char(char, int); 

Using variable names in a prototype doesn't actually create variables. It merely clarifies the fact that char means a char variable, and so on.

Again, ANSI C also recognizes the older form of declaring a function, which is without an argument list:

 void n_char(); 

Later, we'll look at the advantages of using the prototype format.

Calling a Function with an Argument: Actual Arguments

You give ch and num values by using actual arguments in the function call. Consider the first use of n_char() :

 n_char(SPACE, 25); 

The actual arguments are the space character and 25 . These values are assigned to the corresponding formal arguments in n_char() ”the variables ch and num . In short, the formal argument is a variable in the called function, and the actual argument is the particular value assigned to the formal variable by the calling function. As we showed in the example, the actual argument can be a constant, a variable, or an even more elaborate expression. Regardless of which it is, the actual argument is evaluated, and its value copied to the corresponding formal argument for the function. For instance, consider the final use of n_char() :

 n_char(SPACE, (65 - strlen(PLACE)) / 2); 

The long expression forming the second actual argument is evaluated to 23 . Then the value 23 is assigned to the variable num . The function neither knows nor cares whether that number came from a constant, a variable, or a more general expression. Again, the actual argument is a specific value that is assigned to the variable known as the formal argument (see Figure 9.3). Because the called function works with data copied from the calling function, the original data in the calling function is protected from whatever manipulations the called function applies to the copies.

Figure 9.3. Formal arguments and actual arguments.
graphics/09fig03.jpg

The Black Box Viewpoint

Taking a black box viewpoint of n_char() , the input is the character to be displayed and the number of spaces to be skipped . The resulting action is printing the character the specified number of times. The input is communicated to the function via arguments. This information is enough to tell you how to use the function in main() . Also, it serves as a design specification for writing the function.

The fact that ch , num , and count are local variables private to the n_char() function is an essential aspect of the black box approach. If you were to use variables with the same names in main() , they would be separate, independent variables. That is, if main() had a count variable, changing its value wouldn't change the value of count in n_char() , and vice versa. What goes on inside the black box is hidden from the calling function.

Returning a Value from a Function with return

You have seen how to communicate information from the calling function to the called function. To send information in the other direction, you use the function return value. To refresh your memory on how that works, we'll construct a function that returns the smaller of its two arguments. We'll call the function imin() because it's designed to handle int values. Also, we will create a simple main() whose sole purpose is to check to see whether imin() works. A program designed to test functions this way is sometimes called a driver . The driver takes a function for a spin. If the function pans out, then it can be installed in a more noteworthy program. Listing 9.3 shows the driver and the minimum value function.

Listing 9.3 The lesser.c program.
 /* lesser.c -- finds the lesser of two evils */ #include <stdio.h> int imin(int, int); int main(void) {   int evil1, evil2;   printf("Enter a pair of integers (q to quit):\n");   while (scanf("%d %d", &evil1, &evil2) == 2)   {      printf("The lesser of %d and %d is %d.\n",             evil1, evil2, imin(evil1,evil2));      printf("Enter a pair of integers (q to quit):\n");   }   return 0; } int imin(int n,int m) {   int min;   if (n < m)      min = n;   else      min = m;   return min; } 

Here is a sample run:

 Enter a pair of integers (q to quit):  509 333  The lesser of 509 and 333 is 333. Enter a pair of integers (q to quit):  -9393 6  The lesser of -9393 and 6 is -9393. Enter a pair of integers (q to quit):  q  

The keyword return causes the value of the following expression to be the return value of the function. In this case, the function returns the value that was assigned to min . Because min is type int , so is the imin() function.

The variable min is private to imin() , but the value of min is communicated back to the calling function with return . The effect of a statement such as the next one is to assign the value of min to lesser :

 lesser = imin(n,m); 

Could you say the following instead?

 imin(n,m); lesser = min; 

No, because the calling function doesn't even know that min exists. Remember that imin() 's variables are local to imin() . The function call imin(evil1,evil2) copies the values of one set of variables to another set.

Not only can the returned value be assigned to a variable, it can also be used as part of an expression. You can do this, for example:

 answer = 2 * imin(z, zstar) + 25; printf("%d\n", imin(-32 + answer, LIMIT)); 

The return value can be supplied by any expression, not just a variable. For example, you can shorten the program to the following:

 /* minimum value function, second version */ imin(int n,int m) {      return (n lt; m) ? n : m; } 

The conditional expression is evaluated to either n or m , whichever is smaller, and that value is returned to the calling function. If you prefer, for clarity or style, to enclose the return value in parentheses, you may, although parentheses are not required.

Using return has one other effect. It terminates the function and returns control to the next statement in the calling function. This occurs even if the return statement is not the last in the function. Therefore, you can write imin() this way:

 /* minimum value function, third version */ imin(int n,int m) {    if (n < m)        return n;    else        return m; } 

Many, but not all, C practitioners deem it better to use return just once and at the end of a function to make it easier for someone to follow the control flow through the function. However, it's no great sin to use multiple return s in a function as short as this one. Anyway, to the user , all three versions are the same, because all take the same input and produce the same output. Just the innards are different. Even this version works the same:

 /* minimum value function, fourth version */ imin(int n, int m) {    if (n < m)        return n;    else        return m;    printf("Professor Fleppard is like totally a fopdoodle.\n"); } 

The return statements prevent the printf() statement from ever being reached. Professor Fleppard can use the compiled version of this function in his own programs and never learn the true feelings of his student programmer.

You can use a statement like this, too:

 return; 

It causes the function to terminate and return control to the calling function. Because no expression follows return , no value is returned, and this form should be used only in a type void function.

Function Types

Functions should be declared by type. A function with a return value should be declared the same type as the return value. Functions with no return value should be declared as type void . If no type is given for a function, C assumes that the function is type int . This convention stems from the early days of C, when most functions were type int anyway. However, ANSI C recommends providing the type explicitly; eventually, support for the older form of assuming an int type will be dropped.

The type declaration is part of the function definition. Keep in mind that it refers to the return value, not to the function arguments. For instance, the following function heading indicates that you are defining a function that takes two type int arguments but that returns a type double value:

 double klink(int a, int b) 

To use a function correctly, a program needs to know the function type before the function is used for the first time. One way to accomplish this is to place the complete function definition ahead of its first use. However, this method could make the program harder to read. Also, the functions might be part of the C library or in some other file. Therefore, you generally inform the compiler about functions by declaring them in advance. For example, the main() function in Listing 9.3 contains these lines:

 #include <stdio.h> int imin(int, int); int main(void) {   int evil1, evil2, lesser; 

The second line establishes that imin is the name of a function that returns a type int value. Now the compiler will know how to treat imin() when it appears later in the program.

We've placed the advance function declarations outside the function using them. They can also be placed inside the function. For example, you can rewrite the beginning of lesser.c as follows:

 #include <stdio.h> int main(void) {   int imin(int, int);      /* imin() declaration */   int evil1, evil2, lesser; 

In either case, your chief concern should be that the function declaration appear before the function is used.

In the ANSI C standard library, functions are grouped into families, each having its own header file. These header files contain, among other things, the declarations for the functions in the family. For instance, the stdio.h header contains function declarations for the standard I/O library functions, such as printf() and scanf() . The math.h header contains function declarations for a variety of mathematical functions. For example, it contains

 double sqrt(double); 

(or a pre-ANSI equivalent) to tell the compiler that the sqrt() function returns a type double value. Don't confuse these declarations with definitions. A function declaration informs the compiler which type the function is, but the function definition supplies the actual code. Including the math.h header file tells the compiler that sqrt() returns type double , but the code for sqrt() resides in a separate file of library functions.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net