简体   繁体   中英

Strange behavior in casting of function pointers in C++

I have recently encountered a behavior in C++ regarding function pointers, that I can't fully understand. I asked Google for help as well as some of my more experienced colleagues, but even they couldn't help.

The following code showcases this mystique behavior:

class MyClass{
private:
    int i;

public:
    MyClass(): i(0) {}
    MyClass(int i): i(i) {}

    void PrintText() const { std::cout << "some text " << std::endl;}
};

typedef void (*MyFunction) (void*);

void func(MyClass& mc){
    mc.PrintText();
}

int main(){    
    void* v_mc = new MyClass;
    MyFunction f = (MyFunction) func; //It works!
    f(v_mc); //It works correctly!!!

    return 0;
}

So, first I define a simple class that will be used later (especially, it's member method PrintText ). Then, I define name object void (*) (void*) as MyFunction - a pointer to function that has one void* parameter and doesn't return a value.

After that, I define function func() that accepts a reference to MyClass object and calls its method PrintText .

And finally, magic happens in main function. I dynamically allocate memory for new MyClass object casting the returned pointer to void* . Then, I cast pointer to func() function to MyFunction pointer - I didn't expect this to compile at all but it does.

And finally, I call this new object with a void* argument even though underlying function ( func() ) accepts reference to MyClass object. And everything works correctly!

I tried compiling this code with both Visual Studio 2010 (Windows) and XCode 5 (OSX) and it works in the same manner - no warnings are reported whatsoever. I imagine the reason why this works is that C++ references are actually implemented as pointers behind the scenes but this is not an explanation.

I hope someone can explain this behavior.

The formal explanation is simple: undefined behaviour is undefined. When you call a function through a pointer to a different function type, it's undefined behaviour and the program can legally do anything (crash, appear to work, order pizza online ... anyting goes).

You can try reasoning about why the behaviour you're experiencing happens. It's probably a combination of one or more of these factors:

  • Your compiler internally implements references as pointers.
  • On your platform, all pointers have the same size and binary representation.
  • Since PrintText() doesn't access *this at all, the compiler can effectively ignore the value of mc altogether and just call the PrintText() function inside func .

However, you must remember that while you're currently experiencing the behaviour you've described on your current platform, compiler version and under this phase of the moon, this could change at any time for no apparent reason whatsoever (such as a change in surrounding code triggering different optimisations). Remember that undefined behaviour is simply undefined.


As to why you can cast &func to MyFunction - the standard explicitly allows that (with a reinterpret_cast , to which the C-style cast translates in this context). You can legally cast a pointer to function to any other pointer to function type. However, pretty much the only thing you can legally do with it is move it around or cast it back to the original type. As I said above, if you call through a function pointer of the wrong type, it's undefined behaviour.

I hope someone can explain this behavior.

The behaviour is undefined.

MyFunction f = (MyFunction) func; //It works!

It "works" because you use c-style cast which has the same effect as reinterpret_cast in this case I think. If you had used static_cast or simply not cast at all, the compiler would have warned of your mistake and failed. When you call the wrongly interpreted function pointer, you get undefined behaviour.

It's only by chance that it works. Compilers are not guaranteed to make it work. Behind the scenes, your compiler is treating the reference as a pointer, so your alternative function signature just happens to work.

I'm sorry, to me isn't clear why you call this a strange behavior, I don't see a undefined behavior that depends on moon cycle here, is the way to use function pointers in C.

Adding some debug output you may see that the pointer to the object remain the same in all the calls.

void PrintText() const { std::cout << "some text " << this << std::endl;}
                                                      ^^^^
void func(MyClass& mc){
    std::cout << (void *)&mc << std::endl;
                         ^^^
void *v_mc = new MyClass;
std::cout << (void *)v_mc << std::endl;
                     ^^^^

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM