Functions and Pointers

I l @ ve RuBoard

Functions and Pointers

As the discussion on declarations illustrated , it's possible to declare pointers to functions. You might wonder if such a beast has any usefulness . Typically, a function pointer is used as an argument to another function, telling the second function which function to use. For instance, the qsort () function from the C library takes a pointer to a function as one of its arguments. We've used one sorting function for integers and another for strings. The algorithm was the same, but we used > for comparing integers and strcmp() for strings. The qsort() function takes a more general approach. You pass it a pointer to a comparison function appropriate to the type you want to sort , and qsort() then uses that function to sort the type, whether it be integer, string, or structure.

Let's take a closer look at function pointers. First, what does it mean? A pointer to, say, an int holds the address of a location in memory at which an int can be stored. Functions, too, have addresses, for the machine-language implementation of a function consists of code loaded into memory. A pointer to a function can hold the address marking the start of the function code.

Next , when you declare a data pointer, you have to declare the type of data pointed to. When declaring a function pointer, you have to declare the type of function pointed to. To specify the function type, you indicate the return type for the function and the parameter types for a function. For example, consider this prototype:

 void ToUpper(char *);   /* convert string to uppercase  */ 

The type for the ToUpper() function is "function with char * parameter and return type void ." To declare a pointer pf to this function type, do this:

 void (*pf)(char *);     /* pf a pointer-to-function    */ 

Reading this declaration, you see the first parentheses pair associates the * operator with pf , meaning that pf is a pointer to a function. This makes (*pf) a function, which makes (char *) the parameter list for the function and void the return type. Probably the simplest way to create this declaration is to note that it replaces the function name ToUpper with the expression (*pf ). So if you want to declare a pointer to a specific type of function, you can declare a function of that type, then replace the function name with an expression of the form (*pf) to create a function pointer declaration. As mentioned earlier, the first parentheses are needed because of operator precedence rules. Omitting them leads to something quite different:

 void *pf(char *);     /* pf a function that returns a pointer  */ 

Tip

To declare a pointer to a particular type of function, first declare a function of the desired type, and then replace the function name with an expression of the form (*pf) ; pf then becomes a pointer to a function of that type.


After you have a function pointer, you can assign to it the addresses of functions of proper type. In this context, the name of a function can be used to represent the address of the function:

 void ToUpper(char *); void ToLower(char *); int round(double); void (*pf)(char *); pf = ToUpper;        /* valid, ToUpper is address of the function   */ pf = ToLower;        /* valid, ToLower is address of the function   */ pf = round;          /* invalid, round is the wrong type of function */ pf = ToLower();      /* invalid, ToLower() is not an address        */ 

The last assignment is also invalid because you can't use a void function in an assignment statement. Note that the pointer pf can point to any function that takes a char* argument and has a return type of void , but not to functions with other characteristics.

Just as you can use a data pointer to access data, you can use a function pointer to access a function. Strangely, there are two logically inconsistent syntaxes for doing so, as the following illustrates:

 void ToUpper(char *); void ToLower(char *); void (*pf)(char *); char mis[] = "Nina Metier"; pf = ToUpper; (*pf)(mis);     /* apply ToUpper to mis (syntax 1) */ pf = ToLower; pf(mis);        /* apply ToLower to mis (syntax 2) */ 

Each approach sounds sensible . Here is the first approach: Because pf points to the ToUpper function, *pf is the ToUpper function, so the expression (*pf)(mis) is the same as ToUpper(mis) . Just look at the declarations of ToUpper and of pf to see that ToUpper and (*pf) are equivalent. Here is the second approach: Because the name of a function is a pointer, you can use a pointer and a function name interchangeably, hence pf(mis) is the same as ToLower(mis) . Just look at the assignment statement for pf to see that pf and ToLower are equivalent. Historically, the developers of C and UNIX at Bell Labs took the first view and the extenders of UNIX at Berkeley took the second view. K&R C did not allow the second form, but to maintain compatibility with existing code, ANSI C accepts both forms as equivalent.

Just as one of the most common uses of a data pointer is an argument to a function, one of the most common uses of a function pointer is an argument to a function. For instance, consider this function prototype:

 void show(void (* fp)(char *), char * str); 

It looks messy, but it declares two parameters, fp and str . The fp parameter is a function pointer, and the str is a data pointer. More specifically , fp points to a function that takes a char * parameter and has a void return type, and str points to a char . So, given the declarations we had earlier, you can make function calls such as the following:

 show(ToLower, mis);  /* show() uses ToLower() function: fp = ToLower   */ show(pf, mis);       /* show() uses function pointed to by pf: fp = pf */ 

And how does show() use the function pointer passed to it? It uses either the fp() or the (*fp)() syntax to invoke the function:

 void show(void (* fp)(char *), char * str) {     (*fp)(str); /* apply chosen function to str */     puts(str);  /* display result               */ } 

Here, for example, show() first transforms the string str by applying to it the function pointed to by fp , and then it displays the transformed string.

By the way, functions with return values can be used two different ways as arguments to other functions. For example, consider the following:

 function1(sqrt);      /* passes address of sqrt function     */ function2(sqrt(4.0)); /* passes return value of sqrt function */ 

The first passes the address of the sqrt() function and, presumably, function1() will use that function in its code. The second statement initially calls the sqrt() function, evaluates it, and then passes the return value (2.0, in this case) to function2() .

To show the essential ideas, the program in Listing 14.12 uses show() with a variety of transforming functions as arguments. The listing also shows some useful techniques for handling a menu.

Listing 14.12 The func_ptr.c program.
 /* func_ptr.c -- uses function pointers */ #include <stdio.h> #include <string.h> #include <ctype.h> char showmenu(void); void eatline(void);     /* read through end of line     */ void show(void (* fp)(char *), char * str); void ToUpper(char *);   /* convert string to uppercase  */ void ToLower(char *);   /* convert string to uppercase  */ void Transpose(char *); /* transpose cases              */ void Dummy(char *);     /* leave string unaltered       */ int main(void) {     char line[81];     char copy[81];     char choice;     void (*pfun)(char *); /* points a function having a */                           /* char * argument and no     */                           /* return value               */     puts("Enter a string (empty line to quit):");     while (gets(line) != NULL && line[0] != ' 
  /* func_ptr.c -- uses function pointers */ #include <stdio.h> #include <string.h> #include < ctype .h> char showmenu(void); void eatline(void); /* read through end of line */ void show(void (* fp)(char *), char * str); void ToUpper(char *); /* convert string to uppercase */ void ToLower(char *); /* convert string to uppercase */ void Transpose(char *); /* transpose cases */ void Dummy(char *); /* leave string unaltered */ int main(void) { char line[81]; char copy[81]; char choice; void (*pfun)(char *); /* points a function having a */ /* char * argument and no */ /* return value */ puts("Enter a string (empty line to quit):"); while (gets(line) != NULL && line[0] != '\0') { while ((choice = showmenu()) != 'n') { switch (choice ) /* switch sets pointer */ { case 'u' : pfun = ToUpper; break; case 'l' : pfun = ToLower; break; case 't' : pfun = Transpose; break; case 'o' : pfun = Dummy; break; } strcpy (copy, line);/* make copy for show() */ show(pfun, copy); /* use selected function */ } puts("Enter a string (empty line to quit):"); } puts("Bye!"); return 0; } char showmenu(void) { char ans; puts("Enter menu choice:"); puts("u) uppercase l) lowercase"); puts("t) transposed case o) original case"); puts("n) next string"); ans = getchar(); /* get response */ ans = tolower(ans); /* convert to lowercase */ eatline(); /* dispose of rest of line */ while ( strchr ("ulton", ans) == NULL) { puts("Please enter a u, l, t, o, or n:"); ans = tolower(getchar()); eatline(); } return ans; } void eatline(void) { while ( getchar () != '\n') continue; } void ToUpper(char * str) { while (*str != '\0') { *str = toupper(*str); str++; } } void ToLower(char * str) { while (*str != '\0') { *str = tolower(*str); str++; } } void Transpose(char * str) { while (*str != '\0') { if ( islower (*str)) *str = toupper(*str); else if ( isupper (*str)) *str = tolower(*str); str++; } } void Dummy(char * str) { /* leaves string unchanged */ } void show(void (* fp)(char *), char * str) { (*fp)(str); /* apply chosen function to str */ puts(str); /* display result */'''' }  
') { while ((choice = showmenu()) != 'n') { switch (choice ) /* switch sets pointer */ { case 'u' : pfun = ToUpper; break; case 'l' : pfun = ToLower; break; case 't' : pfun = Transpose; break; case 'o' : pfun = Dummy; break; } strcpy(copy, line);/* make copy for show() */ show(pfun, copy); /* use selected function */ } puts("Enter a string (empty line to quit):"); } puts("Bye!"); return 0; } char showmenu(void) { char ans; puts("Enter menu choice:"); puts("u) uppercase l) lowercase"); puts("t) transposed case o) original case"); puts("n) next string"); ans = getchar(); /* get response */ ans = tolower(ans); /* convert to lowercase */ eatline(); /* dispose of rest of line */ while (strchr("ulton", ans) == NULL) { puts("Please enter a u, l, t, o, or n:"); ans = tolower(getchar()); eatline(); } return ans; } void eatline(void) { while (getchar() != '\n') continue; } void ToUpper(char * str) { while (*str != '
  /* func_ptr.c -- uses function pointers */ #include <stdio.h> #include <string.h> #include < ctype .h> char showmenu(void); void eatline(void); /* read through end of line */ void show(void (* fp)(char *), char * str); void ToUpper(char *); /* convert string to uppercase */ void ToLower(char *); /* convert string to uppercase */ void Transpose(char *); /* transpose cases */ void Dummy(char *); /* leave string unaltered */ int main(void) { char line[81]; char copy[81]; char choice; void (*pfun)(char *); /* points a function having a */ /* char * argument and no */ /* return value */ puts("Enter a string (empty line to quit):"); while (gets(line) != NULL && line[0] != '\0') { while ((choice = showmenu()) != 'n') { switch (choice ) /* switch sets pointer */ { case 'u' : pfun = ToUpper; break; case 'l' : pfun = ToLower; break; case 't' : pfun = Transpose; break; case 'o' : pfun = Dummy; break; } strcpy (copy, line);/* make copy for show() */ show(pfun, copy); /* use selected function */ } puts("Enter a string (empty line to quit):"); } puts("Bye!"); return 0; } char showmenu(void) { char ans; puts("Enter menu choice:"); puts("u) uppercase l) lowercase"); puts("t) transposed case o) original case"); puts("n) next string"); ans = getchar(); /* get response */ ans = tolower(ans); /* convert to lowercase */ eatline(); /* dispose of rest of line */ while ( strchr ("ulton", ans) == NULL) { puts("Please enter a u, l, t, o, or n:"); ans = tolower(getchar()); eatline(); } return ans; } void eatline(void) { while ( getchar () != '\n') continue; } void ToUpper(char * str) { while (*str != '\0') { *str = toupper(*str); str++; } } void ToLower(char * str) { while (*str != '\0') { *str = tolower(*str); str++; } } void Transpose(char * str) { while (*str != '\0') { if ( islower (*str)) *str = toupper(*str); else if ( isupper (*str)) *str = tolower(*str); str++; } } void Dummy(char * str) { /* leaves string unchanged */ } void show(void (* fp)(char *), char * str) { (*fp)(str); /* apply chosen function to str */ puts(str); /* display result */'''' }  
') { *str = toupper(*str); str++; } } void ToLower(char * str) { while (*str != '
  /* func_ptr.c -- uses function pointers */ #include <stdio.h> #include <string.h> #include < ctype .h> char showmenu(void); void eatline(void); /* read through end of line */ void show(void (* fp)(char *), char * str); void ToUpper(char *); /* convert string to uppercase */ void ToLower(char *); /* convert string to uppercase */ void Transpose(char *); /* transpose cases */ void Dummy(char *); /* leave string unaltered */ int main(void) { char line[81]; char copy[81]; char choice; void (*pfun)(char *); /* points a function having a */ /* char * argument and no */ /* return value */ puts("Enter a string (empty line to quit):"); while (gets(line) != NULL && line[0] != '\0') { while ((choice = showmenu()) != 'n') { switch (choice ) /* switch sets pointer */ { case 'u' : pfun = ToUpper; break; case 'l' : pfun = ToLower; break; case 't' : pfun = Transpose; break; case 'o' : pfun = Dummy; break; } strcpy (copy, line);/* make copy for show() */ show(pfun, copy); /* use selected function */ } puts("Enter a string (empty line to quit):"); } puts("Bye!"); return 0; } char showmenu(void) { char ans; puts("Enter menu choice:"); puts("u) uppercase l) lowercase"); puts("t) transposed case o) original case"); puts("n) next string"); ans = getchar(); /* get response */ ans = tolower(ans); /* convert to lowercase */ eatline(); /* dispose of rest of line */ while ( strchr ("ulton", ans) == NULL) { puts("Please enter a u, l, t, o, or n:"); ans = tolower(getchar()); eatline(); } return ans; } void eatline(void) { while ( getchar () != '\n') continue; } void ToUpper(char * str) { while (*str != '\0') { *str = toupper(*str); str++; } } void ToLower(char * str) { while (*str != '\0') { *str = tolower(*str); str++; } } void Transpose(char * str) { while (*str != '\0') { if ( islower (*str)) *str = toupper(*str); else if ( isupper (*str)) *str = tolower(*str); str++; } } void Dummy(char * str) { /* leaves string unchanged */ } void show(void (* fp)(char *), char * str) { (*fp)(str); /* apply chosen function to str */ puts(str); /* display result */'''' }  
') { *str = tolower(*str); str++; } } void Transpose(char * str) { while (*str != '
  /* func_ptr.c -- uses function pointers */ #include <stdio.h> #include <string.h> #include < ctype .h> char showmenu(void); void eatline(void); /* read through end of line */ void show(void (* fp)(char *), char * str); void ToUpper(char *); /* convert string to uppercase */ void ToLower(char *); /* convert string to uppercase */ void Transpose(char *); /* transpose cases */ void Dummy(char *); /* leave string unaltered */ int main(void) { char line[81]; char copy[81]; char choice; void (*pfun)(char *); /* points a function having a */ /* char * argument and no */ /* return value */ puts("Enter a string (empty line to quit):"); while (gets(line) != NULL && line[0] != '\0') { while ((choice = showmenu()) != 'n') { switch (choice ) /* switch sets pointer */ { case 'u' : pfun = ToUpper; break; case 'l' : pfun = ToLower; break; case 't' : pfun = Transpose; break; case 'o' : pfun = Dummy; break; } strcpy (copy, line);/* make copy for show() */ show(pfun, copy); /* use selected function */ } puts("Enter a string (empty line to quit):"); } puts("Bye!"); return 0; } char showmenu(void) { char ans; puts("Enter menu choice:"); puts("u) uppercase l) lowercase"); puts("t) transposed case o) original case"); puts("n) next string"); ans = getchar(); /* get response */ ans = tolower(ans); /* convert to lowercase */ eatline(); /* dispose of rest of line */ while ( strchr ("ulton", ans) == NULL) { puts("Please enter a u, l, t, o, or n:"); ans = tolower(getchar()); eatline(); } return ans; } void eatline(void) { while ( getchar () != '\n') continue; } void ToUpper(char * str) { while (*str != '\0') { *str = toupper(*str); str++; } } void ToLower(char * str) { while (*str != '\0') { *str = tolower(*str); str++; } } void Transpose(char * str) { while (*str != '\0') { if ( islower (*str)) *str = toupper(*str); else if ( isupper (*str)) *str = tolower(*str); str++; } } void Dummy(char * str) { /* leaves string unchanged */ } void show(void (* fp)(char *), char * str) { (*fp)(str); /* apply chosen function to str */ puts(str); /* display result */'''' }  
') { if (islower(*str)) *str = toupper(*str); else if (isupper(*str)) *str = tolower(*str); str++; } } void Dummy(char * str) { /* leaves string unchanged */ } void show(void (* fp)(char *), char * str) { (*fp)(str); /* apply chosen function to str */ puts(str); /* display result */'''' }

Here is a sample run:

 Enter a string (empty line to quit):  Does C make you feel loopy?  Enter menu choice: u) uppercase       l) lowercase t) transposed case o) original case n) next string  t  dOES c MAKE YOU FEEL LOOPY? Enter menu choice: u) uppercase       l) lowercase t) transposed case o) original case n) next string  l  does c make you feel loopy? Enter menu choice: u) uppercase       l) lowercase t) transposed case o) original case n) next string  n  Enter a string (empty line to quit): Bye! 

Note that the ToUpper() , ToLower( ), Transpose( ), and Dummy() functions all have the same type, so all four can be assigned to the pfun pointer. This program uses pfun as the argument to show() , but you can also use any of the four function names directly as arguments, as in show(Transpose, line ).

You can use typedef in situations like these. For example, the program could have done this:

 typedef void (*V_FP_CHARP)(char *); void show (V_FP_CHARP fp, char *); V_FP_CHARP pfun; 

If you're feeling adventurous, you can declare and initialize an array of such pointers:

 V_FP_CHARP arpf[4] = {ToUpper, ToLower, Transpose, Dummy}; 

If you then modify the showmenu() function so that is type int and returns if the user enters u , 1 if the user enters l , 2 if the user enters t , and so on, you could replace the loop holding the switch statement with the following:

 index = showmenu(); while (index >= 0 && index <= 3) {     strcpy(copy, line);       /* make copy for show()  */     show(arpf[index], copy);  /* use selected function */     index = showmenu(); } 

You can't have an array of functions, but you can have an array of function pointers.

You've now seen all four ways in which a function name can be used: in defining a function, in declaring a function, in calling a function, and as a pointer. Figure 14.4 sums up the uses.

Figure 14.4. Uses for a function name.
graphics/14fig04.jpg

As far as menu handling goes, the showmenu() function shows several techniques. First, the code

 ans = getchar();    /* get response            */ ans = tolower(ans); /* convert to lowercase    */ 

and

 ans = tolower(getchar()); 

show two ways to convert user input to one case so that you don't have to test for both `u' and `U' , and so on.

The eatline() function disposes of the rest of the entry line. This is useful on two accounts. First, to enter a choice, the user types a letter, then presses the Enter key, which generates a newline character. That newline character will be read as the next response unless you get rid of it first. Second, suppose the user responds by typing the entire word uppercase instead of u . Without the eatline() function, the program would treat each character in the word uppercase as a separate response. With eatline() , the program processes the u and discards the rest of the line.

Next, the showmenu() function is designed to return only valid choices to the program. To help with that task, the program uses the standard library function strchr() from the string. h header file:

 while (strchr("ulton", ans) == NULL) 

This function looks for the location of the first occurrence of the character ans in the string "ulton" and returns a pointer to it. If it doesn't find the character, it returns the null pointer. Therefore, this while loop test is a more convenient replacement for the following:

 while (ans != 'u' && ans != 'l' && ans != 't' && ans != 'o' && ans != 'n') 

The more choices you have to check, the more convenient using strchr() becomes.

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