Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of function pointers

Tags:

I have a problem with understanding some C++ syntax combined with function pointers and function declarations, that is:

Usually when we want to declare a type of function we make something like:

typedef void(*functionPtr)(int); 

and this is fine for me. From now on functionPtr is a type, that represents pointer to the function, that returns void and takes int by a value as an argument.

We can use it as follows:

typedef void(*functionPtr)(int);  void function(int a){     std::cout << a << std::endl; }  int main() {      functionPtr fun = function;     fun(5);      return 0; } 

And we get 5 printed on a screen.

we have got pointer to function fun, we assign some existing pointer to function - function and we execute this function by a pointer. Cool.

Now as I read in some books, function and pointer to function are treated somehow the same, so in fact after declaration of function() function everytime we say function we mean real function and pointer to function at the same type, so following compiles and every instruction gives the same result (5 printed on a screen):

int main() {      functionPtr fun = function;     fun(5);     (*fun)(5);     (*function)(5);     function(5);      return 0; } 

So now as long as I can imagine, that pointers to functions and functions are pretty much the same, then it's somehow fine for me.

Then I though, if pointer to function and real function is the same, then why cannot I do following:

typedef void(functionPtr)(int); //removed *  void function(int a){     std::cout << a << std::endl; }  int main() {      functionPtr fun = function;     fun(5);      return 0; } 

This gives me following error:

prog.cpp:12:14: warning: declaration of 'void fun(int)' has 'extern' and is initialized functionPtr fun = function;

therefore I understood, that for some reason compiler now understands, that fun is already existing function. Then I tried following:

int main() {      functionPtr fun;     fun(5);      return 0; } 

And I got linking error. I somehow understand, that as compiler now treats fun as already existing function, then due to the fact, that fun is nowhere defined, I will get linking error. Therefore I changed the name of the variable:

typedef void(functionPtr)(int);  void function(int a){     std::cout << a << std::endl; }  int main() {      functionPtr function;     function(5);      return 0; } 

So now function in main shadows global name function, so function(5) is used from declaration functionPtr function; It works fine and prints 5 on the screen.

So now I am shocked. Why did this happen? Also misleading thing is, that when function pointer is declared like this:

typedef void(*functionPtr)(int); 

i can create function of type functionPtr in following manner:

functionPtr function(int a){     std::cout << a << std::endl; } 

whereas, when declaring something like:

typedef void(functionPtr)(int); 

makes this:

functionPtr function(int a){     std::cout << a << std::endl; } 

being interpreted by a compiler as function returning function. If this is so, why previous declaration (typedef void(functionPtr)(int);) knew, that this is a function returning void and not function returning functionPtr?

Could someone please explain what is really happening underhood for me?

I am using g++ C++ compiler with C++14 option enabled.

like image 732
DawidPi Avatar asked Feb 26 '16 14:02

DawidPi


2 Answers

Well, it is confusing a bit.

Function type and pointer to function type are indeed two different types (no more similar than int and pointer to int). However, there is a rule, that a function type decays to pointer to function type in almost all contexts. Here decaying loosely means converted (there is a difference between type conversion and decaying, but you are probably not interested in it right now).

What is important, is that almost every time you use a function type, you end up with pointer to function type. Note the almost, however - almost every time is not always!

And you are hitting some cases when it doesn't.

typedef void(functionPtr)(int); functionPtr fun = function; 

This code attempts to copy one function (not the pointer! the function!) to another. But of course, this is not possible - you can't copy functions in C++. The compiler doesn't allow this, and I can't believe you got it compiled (you are saying you got linker errors?)

Now, this code:

typedef void(functionPtr)(int); functionPtr function; function(5); 

function does not shadow anything. Compiler knows it is not a function pointer which can be called, and just calls your original function.

like image 91
SergeyA Avatar answered Oct 06 '22 01:10

SergeyA


The most interesting of your examples is this one, reproduced here without the use of typedef:

void function(int a) { // declaration and definition of 'function'     std::cout << a << std::endl; }  int main() {     void function(int); // declaration of 'function'     function(5); } 

In most contexts in C++, the re-declaration of function in local scope would shadow the global ::function. So expecting a linker-error there makes sense - main()::function has no definition right?

Except functions are special in this regard. From a note in [basic.scope.pdel]:

Function declarations at block scope and variable declarations with the extern specifier at block scope refer to declarations that are members of an enclosing namespace, but they do not introduce new names into that scope.

So that code example is exactly equivalent to:

void function(int a) { /* ... */ } void function(int ); // just redeclaring it again, which is ok  int main() {     function(5); } 

You can also verify this by putting the global function into some namespace, N. At this point, the local scope declaration would add a name to ::, but it wouldn't have a definition - hence you do get a linker error.


The other interesting thing you touched on is the notion of function-to-pointer conversion, [conv.func]:

An lvalue of function type T can be converted to a prvalue of type “pointer to T”. The result is a pointer to the function.

When you have a function call expression - if the thing you're calling is a function type, it is first converted to a pointer-to-function. That's why these are equivalent:

fun(5);         // OK, call function pointed to by 'fun' (*fun)(5);      // OK, first convert *fun back to 'fun' function(5);    // OK, first convert to pointer to 'function' (*function)(5); // OK, unary* makes function get converted to a pointer                 // which then gets dereferenced back to function-type                 // which then gets converted back to a pointer 
like image 32
Barry Avatar answered Oct 06 '22 00:10

Barry