Why do function pointers exist?

Question

To be clear, I understand the uses of function pointers. But as far as I know, there is no way to use actual function types in the language, so why bother with pointers? We already have syntax sugar so instead of writing

int (*fptr)(int) = &some_func;
int x = (*fptr)(5);

we write

int (*fptr)(int) = some_func;
int x = fptr(5);

Why not just use the function type directly?

int fptr(int) = some_func;
int x = fptr(5);

Posted Mar 9, 2017, 11:25 AM UTC

1615
YellowFlowerRanger•

You're asking about the syntax of it? The problem is that:

{
  int foo(int);
  int x = foo(3);
}

Is perfectly valid syntax in C. The first line is a declaration to an existing function called foo that's defined somewhere. That is not a pointer to a function, just a regular old function. Compare that with:

{
  int foo(int);
  foo = some_func;
  int x = foo(3);
}

Now we're using foo as a pointer to a function, not a function. At the time foo is declared, the syntax would make it ambiguous as to whether foo is a function or a pointer to a function.

19
pherlo•

With the benefit of hindsight, they should have made functions values, and function declarations would then just be uninitialized function values. All that's missing is a function-value literal (lambda)

I wonder if there is a variant of C with first-class functions, not just tacked on syntax like objc blocks. It seems the delta to such a language is not very large.

2
crackez•

If the syntax were somehow mostly backwards compatible (especially with existing programmers' minds) then that could be an interesting addition to a future version of ANSI C.

1
MerionesOP•

Ah, that makes sense.

1
Rhomboid•

Because the syntax for declaring a function value was already taken... by function declarations.

10
KeytarVillain•

Think about the difference between a variable and a pointer to the variable. With a variable, you are storing the data somewhere, typically on the stack. With a pointer, you have a pointer to data stored somewhere else. So if you could use a function type directly instead of as a pointer, you would be storing the function as data on the stack. This isn't even possible on a Harvard architecture. But even on an architecture that doesn't have strict instruction/data separation, there are a lot of complications and reasons you wouldn't want to do it. It's similar to why you use a pointer to an array instead of the array itself.

5
necheffa•

Because functions aren't types (nor should they be) and nor are they variables.

If you check the C grammar (appendix A section 13 of K&R), you can start at the production for function-definition and work your way down. type-specifier by itself never satisfies any production that could be a function-definition.

If you allow a function-definition to also be a type-specifier you end up with multiple levels of left-recursion. Now you could probably come up with some shoe string and bubble gum to work around that; but it makes the parser more difficult to write and the language more ambiguous.

2
[deleted]•

[deleted]

1
chalk46•

I've seen them used in the linux kernel, with soundcore for example. They have structures filled with function pointers, and they point to a different function depending on what driver is loaded.

0
MerionesOP•

I'm asking why the language makes us use the unwieldy int (*ptr)(int) syntax when the actual function type int ptr(int) is barely used.

2†
dmc_2930•

Because under the hood, they are pointers, and not native types?

Every function name is actually a pointer. When you declare one that doesn't have a value, you have to use the pointer syntax so that you can give it an address.

Does that answer your question?

2

It cleared something up for me!.. but Im not op

2
ratatask•

If you see this code, what would it be ? A prototype for a function, or a definition of a function pointer ?

   int ptr(int);

In C, int ptr(int) a function declaration(or a prototype if you like). If you wanted that same syntax to also be a function pointer, it would cause quite a bit of ambiguity.

2
CatsAreTasty•

You can use them for object oriented programming in C by emulating methods and polymorphism.

0
crackez•

Here's a good example, note the arrays declared just before main(); ^^^//pardon ^^^the ^^^c++

#include <iostream>

bool b0 (bool a, bool b) 	{ return a&&!a; }
int i0 (int a, int b) 		{ return a&~a;  }
bool b1 (bool a, bool b) 	{ return a&&b;  }
int i1 (int a, int b) 		{ return a&b;   }
bool b2 (bool a, bool b) 	{ return a&&!b; }
int i2 (int a, int b) 		{ return a&~b;  }
bool b3 (bool a, bool b) 	{ return a;     }
int i3 (int a, int b) 		{ return a;     }
bool b4 (bool a, bool b) 	{ return !a&&b; }
int i4 (int a, int b) 		{ return ~a&b;  }
bool b5 (bool a, bool b) 	{ return b;     }
int i5 (int a, int b) 		{ return b;     }
bool b6 (bool a, bool b) 	{ return a!=b;  }
int i6 (int a, int b) 		{ return a^b;   }
bool b7 (bool a, bool b) 	{ return a||b;  }
int i7 (int a, int b) 		{ return a|b;   }
bool b8 (bool a, bool b) 	{ return !a&&!b;}
int i8 (int a, int b) 		{ return ~a&~b; }
bool b9 (bool a, bool b) 	{ return a==b;  }
int i9 (int a, int b) 		{ return ~a^b;  }
bool bA (bool a, bool b) 	{ return !b;    }
int iA (int a, int b) 		{ return ~b;    }
bool bB (bool a, bool b) 	{ return a||!b; }
int iB (int a, int b) 		{ return a|~b;  }
bool bC (bool a, bool b) 	{ return !a;    }
int iC (int a, int b) 		{ return ~a;    }
bool bD (bool a, bool b) 	{ return !a||b; }
int iD (int a, int b) 		{ return ~a|b;  }
bool bE (bool a, bool b) 	{ return !a||!b;}
int iE (int a, int b) 		{ return ~a|~b; }
bool bF (bool a, bool b) 	{ return a||!a; }
int iF (int a, int b) 		{ return a|~a;  }

bool (* const bx[]) (bool, bool) = { 
	    b0, b1, b2, b3, b4, b5, b6, b7,
        b8, b9, bA, bB, bC, bD, bE, bF };
int (* const ix[]) (int, int) = { 
	    i0, i1, i2, i3, i4, i5, i6, i7,
        i8, i9, iA, iB, iC, iD, iE, iF };

int main (void) {
    (std::cout << "A 0 0 1 1 3\nB 0 1 0 1 5\n\n" << std::hex).setf(std::ios::uppercase);
    for (int x = 0; x != 16; ++ x) {
        std::cout << x << " ";
        for (int a = 0; a != 2; ++ a)
            for (int b = 0; b != 2; ++ b)
                std::cout << bx[x](a, b) << " ";
        std::cout << (ix[x](3, 5) & 0XF) << "\n";
    }
}

^^^^edit:formatting

-1
mcandre•

function pointers allow you to customize algorithms. they operate similarly to lamdas

-4