简体   繁体   English

在C ++中转换函数指针时的奇怪行为

[英]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. 我最近在C ++中遇到了关于函数指针的行为,我无法完全理解。 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 ). 所以,首先我定义一个稍后将使用的简单类(特别是它的成员方法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. 然后,我将名称对象void (*) (void*)MyFunction - 指向具有一个void*参数且不返回值的函数的指针。

After that, I define function func() that accepts a reference to MyClass object and calls its method PrintText . 之后,我定义了函数func() ,它接受对MyClass对象的引用并调用其方法PrintText

And finally, magic happens in main function. 最后,魔术发生在主要功能上。 I dynamically allocate memory for new MyClass object casting the returned pointer to void* . 我为新的MyClass对象动态分配内存,将返回的指针强制转换为void* Then, I cast pointer to func() function to MyFunction pointer - I didn't expect this to compile at all but it does. 然后,我将指向func()函数的指针转换为MyFunction指针 - 我没想到它会完全编译,但确实如此。

And finally, I call this new object with a void* argument even though underlying function ( func() ) accepts reference to MyClass object. 最后,我使用void*参数调用这个新对象,即使底层函数( func() )接受对MyClass对象的引用。 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. 我尝试使用Visual Studio 2010(Windows)和XCode 5(OSX)编译此代码,它以相同的方式工作 - 不报告任何警告。 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. 我想这就是为什么它的工作原理是C ++引用实际上是作为幕后指针实现的,但这不是解释。

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 . 由于PrintText()根本不访问*this ,编译器可以完全有效地忽略mc的值,只需在func调用PrintText()函数。

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). 至于为什么你可以将&funcMyFunction - 标准明确允许(使用reinterpret_cast - reinterpret_cast ,C风格的强制转换在此上下文中转换)。 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. 它“有效”,因为你使用c-style reinterpret_cast在这种情况下我认为与reinterpret_cast具有相同的效果。 If you had used static_cast or simply not cast at all, the compiler would have warned of your mistake and failed. 如果你曾经使用过static_cast或根本就没有使用,那么编译器会警告你的错误并且失败了。 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. 对不起,对我来说不清楚为什么你称之为奇怪的行为,我没有看到这里依赖于月亮周期的未定义行为,是在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;
                     ^^^^

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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