3

I'm reading over a solution to a problem in K&R's chapter 7 posted here. Basically, the program will convert the standard input into lowercase or uppercase depending on the name of the program ("upper" or "lower"). It seems to store the names of the conversion functions in some kind of dictionary, like so:

int (*convcase[2])(int) = {toupper, tolower};

and later access these functions depending on whether the name of the program started with a u or an l:

if(argc > 0)
  {
    if(toupper((unsigned char)argv[0][0]) == 'U')
    {
      func = 0;
    }
    else
    {
      func = 1;
    }

    while((ch = getchar()) != EOF)
    {
      ch = (*convcase[func])((unsigned char)ch);
      putchar(ch);
    }
  }

I understand what that code block is doing, but I've never seen anything like the initial declaration of convcase. It seems like some kind of weird combination of a macro, an enum, and an array. Could anyone explain (1) why convcase is a pointer; (2) what's with the (int) cast after its name; (3) what exactly toupper and tolower are within that declaration, because they're not char *s; and (4) when/why to use this kind of setup. Is it just a quick macro-like tool to save some space when you have multiple possible function calls?

user1427661
  • 11,158
  • 28
  • 90
  • 132

1 Answers1

3
  1. convcase is an array of two function pointers; It's not a pointer. An array expression will be implicitly converted to a pointer when it is used in a place where a pointer is expected.
  2. That's not a cast. It's specifying that the functions accept an int argument, as the toupper and tolower functions do.
  3. convcase is initialised with the function pointers toupper and tolower.
  4. I presume this question is regarding function pointers. Use function pointers when you wish for the abstraction of function pointers. I'd suggest that this example is an unnecessary use of function pointers. The ability to change convcase[0] or convcase[1] to Spanish or Japanese, depending upon user input would more likely warrant the use of function pointers in this case. Additionally, function pointers could be used in arguments to provide additional abstraction. For example, the qsort function is able to sort arrays of any type because of it's usage of function pointers for a comparer of objects. Here's an example that sorts int input using the int_compare function as the comparer.
#define nelem(array) (sizeof (array) / sizeof *(array))

int int_compare(const void *p1, const void *p2) {
    int x = *(int *)p1, y = *(int *)p2;
    return (x > y) - (y > x);
}

int main(void) {
    int value[16];
    for (size_t x = 0; x < nelem(value); x++) {
        if (scanf("%d", &value[x]) != 1) {
            break;
        }
    }

    qsort(value, sizeof *value, x, int_compare);
    return 0;
}
autistic
  • 1
  • 3
  • 35
  • 80