C++程序在debug模式下遇到Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call問題。


今天遇到一個Access Violation的crash,只看crash call stack沒有找到更多的線索,於是在debug模式下又跑了一遍,遇到了如下的一個debug的錯誤提示框:

runtimecheckfailureESP

這個是什么原因呢?我們來看一個簡單的例子來重現這個錯誤。

假設我們有2個父類,分別是BaseA和BaseB。有一個子類Child,繼承自BaseA和BaseB。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class BaseA
{
public:
    virtual void foo()=0;
};

class BaseB
{
public:
    virtual void bar(int a)=0;
};

class Child: public BaseA, public BaseB
{
public:
    void foo()
    {
        cout<<"i'm foo in Child!"<<endl;
    };
    void bar(int a)
    {
        cout<<"i'm bar in Child!"<<endl;
    };
};

 

假設我們有如下的main函數:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main() {

    BaseB* b = new Child();
    BaseA* a = (BaseA*)b;
    BaseA* a2 = dynamic_cast<BaseA*> (b);
    // This is actually calling bar(),
    // and will cause Runtime check failure about ESP if the foo and bar have different signature.
    a->foo(); 
    a2->foo();
}

 

在這個main函數里a是通過C-Style的強轉轉來的,a2是通過dynamic_cast轉換來的。如果我們運行這個程序,在release下就會報access violation的crash,在debug下就會出上面列的ESP錯誤的對話框。

函數的輸出如下圖所示:

espoutput

這就能解釋為什么會出這個ESP的錯誤了,因為我們想調用foo,但是實際上調用的bar,但是foo和bar的函數簽名不一樣,所以導致了出現那個debug版本下的ESP的錯誤提示。但是加入我們把bar(int a)中的參數去掉,這樣foo和bar就有一樣的函數簽名了,這樣及時在debug版本下也不會出這個ESP的提示,在release或者debug模式下都不會報任何錯誤,但是我們在runtime調用了錯誤的函數!!!

可以看看下圖回憶一下vtable是怎么回事:)

image

 

也可以參考怎么看C++對象的內存結構 和 怎么解密C++的name mangling - fresky - 博客園來看看C++對象的內存結構。

對於ESP的錯誤,可以參考stackoverflow上的這個問題:c++ - Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call - Stack Overflow


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM