/* ** $Id: fp.c 802 2008-03-01 19:26:43Z phf $ ** ** Function pointers in action. :-) Start reading at main() ** below, then look at the other functions as needed. */ #include #include #include #include // we need this below to count how many comparisons were // made, never mind now what it is... :-) int comps = 0; /* ** A fancy version of puts that draws a border around ** the string. (Doesn't work if the string has "\n".) */ int fancy_puts(const char *s) { // Length of the string plus two "*" and two " ". const int length = strlen(s) + 4; // Top border. for (int i = 0; i < length; i++) { printf("*"); } // Center line with the string. printf("\n* %s *\n", s); // Bottom border. for (int i = 0; i < length; i++) { printf("*"); } printf("\n"); // Check what puts() is supposed to return to see why... return (length+2)*3; } /* ** Three functions to test our integration with. */ double constant(double x) { // the warning about "unused x" was bugging me... :-) return x = 1.0; } double linear(double x) { return 0.5*x+1; } double square(double x) { return x*x; } /* ** Numerical integration based on the trapezoidal rule. Divides ** the interval [lb..ub] into 1024 slices (hardcoded, not good) ** and computes the partial integral for that slice assuming F ** is linear in the slice. Pretty bad in terms of error, but we ** are not a numerical analysis course, so what the heck. :-) */ double integrate(double (*f)(double), double lb, double ub) { // Make sure the interval makes sense at all. assert(f != NULL && lb < ub); // How wide is one slices for this interval? const int slices = 1024; const double width = (ub - lb) / slices; // Current slice starts here. double cs = lb; // Estimate for the integral. double result = 0.0; // Accumulate the area of all slices. while(cs + width < ub) { // Function at bounds of the current slice. float one = f(cs); float two = f(cs + width); // Extend the estimate with the current slice. result += 0.5 * width * (one + two); // Next slice... cs += width; } // And we're done, at least approximately. :-) return result; // There's one more assumption hidden in the code, can you // figure out what it is and how to "break" the algorithm? } /* ** The following two functions are used with qsort(3) and ** bsearch(3); they determine which of two integers from ** an array is smaller and bigger. The return value is like ** strcmp(3). The first one is the inverse of the second ** one, resulting in inversely sorted arrays with qsort(3). */ int intcmp(const void *x, const void *y) { comps++; // never mind, not important const int *xx = x; const int *yy = y; return *xx - *yy; } int intcmp2(const void *x, const void *y) { comps++; // never mind, not important const int *xx = x; const int *yy = y; return *yy - *xx; } /* ** The main() event. :-) */ int main(void) { // Let's call two different versions of puts() first. puts("Compiler decides!"); fancy_puts("Compiler decides!"); // Sometimes, you don't want to have to decide which // function to call when you compile, rather you want // to wait until runtime. You need a function pointer: int (*fp) (const char *s); // This declares "fp" to be a pointer to a function // that takes a const char* and returns an int. Yep, // it's ugly. Set it to NULL like other pointers: fp = NULL; // Now it doesn't point to any function, and if we // were to call it, the program would crash. Try! // (*fp)("Hello!"); // causes a bus error on OS X // We can make it point to the usual puts() function // though, and then we can call it *through* fp. fp = puts; (*fp)("Program decides!"); // Note that the compiler will automatically dereference // the pointer if we try to call it as a function, so the // following works just as well: fp("Program decides!"); // Later we can change our mind and point it to the new // fancy_puts() function. fp = fancy_puts; fp("Program decides!"); // Check that out: Two *identical* lines of code, but // two *different* functions run depending on what is // in fp. So we can decide what piece of code to run // while the program is running, not when we compile // the program. Nifty. :-) // We can also use function pointers as part of more // complicated things. So here's an array of function // pointers, initialized to puts() and fancy_puts(): int (*fps[2]) (const char *s) = {puts, fancy_puts}; // We can run over that array and call each function // in turn, inside a loop: for (int i = 0; i < 2; i++) { fps[i]("Called through an array!"); } // We can use function pointers to do things like write // a function that integrates (in the mathematical sense) // another function in a certain interval. Check out the // example functions constant(), linear(), and square() // first. They are the things we integrate later. printf("constant(%g) = %g\n", 2.0, constant(2)); printf("linear(%g) = %g\n", 2.0, linear(2)); printf("square(%g) = %g\n", 2.0, square(2)); // Now check out the integrate() function above, then // come back here to see it work. printf("\\int_0^4 1 = %g\n", integrate( constant, 0, 4 )); printf("\\int_0^4 0.5*x+1 = %g\n", integrate( linear, 0, 4 )); printf("\\int_0^4 x^2 = %g\n", integrate( square, 0, 4 )); // Pretty fancy stuff. We could also write a function that // tabulates another function in a certain interval, or // one that actually plots it, or one that finds zeroes of // other functions, etc. etc. // Function pointers are *really* relevant since a number of // useful library functions expect you to pass them. There's // a library function qsort(3) that sorts *arbitrary* arrays // in O(n log n) expected time. Obviously it doesn't know how // to compare two *arbitrary* values, so you need to pass a // comparison function to it. Here's an example: int data[] = {8, 6, 7, 5, 3, 0, 9}; // Print the initial array. fp("Unsorted!"); for (int i = 0; i < 7; i++) { printf("data[%d]=%d\n", i, data[i]); } // Sort it using the first comparison function. qsort(data, 7, sizeof(int), intcmp); // Print the sorted array. fp("Sorted!"); for (int i = 0; i < 7; i++) { printf("data[%d]=%d\n", i, data[i]); } // Sort it again using the second comparison function. qsort(data, 7, sizeof(int), intcmp2); // Print the inversely sorted array. fp("Inversely sorted!"); for (int i = 0; i < 7; i++) { printf("data[%d]=%d\n", i, data[i]); } // Also, check out the bsearch(3) function for a fast O(log n) // way to search in a sorted array. You can implement a pretty // decent SDBM index facility using just those two functions and // ordinary arrays (which should of course "grow" as needed)... // initialize counter to measure how many comparisons we need comps = 0; // fill an array with squares int arr[1024]; for (int i = 0; i<1024; i++) { arr[i] = i*i; } // element we need to find int key = 144; //int key = 125; // try this one to see the other case // search for the element int *k = bsearch(&key, arr, 1024, sizeof(int), intcmp); // and make sure that's what we found... if (k == NULL) { printf("Needed %d comparisons to ensure element is missing!\n", comps); } else { printf("Found %d using %d comparisons!\n", *k, comps); } // Woohoo! return EXIT_SUCCESS; }