簡體   English   中英

在C ++中轉換函數指針時的奇怪行為

[英]Strange behavior in casting of function pointers in C++

我最近在C ++中遇到了關於函數指針的行為,我無法完全理解。 我向谷歌尋求幫助以及一些經驗豐富的同事,但即使他們也無能為力。

以下代碼展示了這種神秘行為:

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;
}

所以,首先我定義一個稍后將使用的簡單類(特別是它的成員方法PrintText )。 然后,我將名稱對象void (*) (void*)MyFunction - 指向具有一個void*參數且不返回值的函數的指針。

之后,我定義了函數func() ,它接受對MyClass對象的引用並調用其方法PrintText

最后,魔術發生在主要功能上。 我為新的MyClass對象動態分配內存,將返回的指針強制轉換為void* 然后,我將指向func()函數的指針轉換為MyFunction指針 - 我沒想到它會完全編譯,但確實如此。

最后,我使用void*參數調用這個新對象,即使底層函數( func() )接受對MyClass對象的引用。 一切正常!

我嘗試使用Visual Studio 2010(Windows)和XCode 5(OSX)編譯此代碼,它以相同的方式工作 - 不報告任何警告。 我想這就是為什么它的工作原理是C ++引用實際上是作為幕后指針實現的,但這不是解釋。

我希望有人可以解釋這種行為。

形式化的解釋很簡單: 未定義的行為未定義。 當您通過指向不同函數類型的指針調用函數時,它是未定義的行為,程序可以合法地執行任何操作(崩潰,似乎工作,在線訂購披薩......任何進展)。

您可以嘗試推理您遇到的行為發生的原因。 它可能是以下一個或多個因素的組合:

  • 您的編譯器在內部將引用實現為指針。
  • 在您的平台上,所有指針都具有相同的大小和二進制表示。
  • 由於PrintText()根本不訪問*this ,編譯器可以完全有效地忽略mc的值,只需在func調用PrintText()函數。

但是,你必須記住,當你正在經歷你在當前平台,編譯器版本和月球這個階段所描述的行為時,這可能在任何時候都沒有明顯的原因(例如改變)周圍代碼觸發不同的優化)。 請記住,未定義的行為是未定義的。


至於為什么你可以將&funcMyFunction - 標准明確允許(使用reinterpret_cast - reinterpret_cast ,C風格的強制轉換在此上下文中轉換)。 您可以合法地將指向函數的指針強制轉換為任何其他指向函數類型的指針。 但是,你可以合法地做的唯一事情就是移動它或將其轉換回原始類型。 如上所述,如果通過錯誤類型的函數指針調用,則它是未定義的行為。

我希望有人可以解釋這種行為。

行為未定義。

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

它“有效”,因為你使用c-style reinterpret_cast在這種情況下我認為與reinterpret_cast具有相同的效果。 如果你曾經使用過static_cast或根本就沒有使用,那么編譯器會警告你的錯誤並且失敗了。 當您調用錯誤解釋的函數指針時,您將獲得未定義的行為。

這只是偶然的機會。 編譯器不保證能夠正常工作。 在幕后,您的編譯器將引用視為指針,因此您的替代函數簽名恰好起作用。

對不起,對我來說不清楚為什么你稱之為奇怪的行為,我沒有看到這里依賴於月亮周期的未定義行為,是在C中使用函數指針的方法。

添加一些調試輸出,您可能會看到指向對象的指針在所有調用中保持不變。

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