移植代碼到 DLL 時遇到的一個問題

有一個功能模塊, 本來是寫在主程序當中. 現在覺得有必要將它寫成一個 DLL. 於是開始代碼的移植. 費了好大的勁. 終於移植完成, 通過編譯了. 這時運行程序, CRASH!
調試之, 發現是在一個 map 的賦值出現了問題.
看 vc6 自帶的 STL 的代碼:

map 的賦值操作, 也就是其中的樹賦值操作.
_Myt& operator=(const _Myt& _X)
{
    _Tr = _X._Tr;
    return (*this);
}

樹的賦值操作:
_Myt& operator=(const _Myt& _X)
{
    if (this != &_X)
    {
        erase(begin(), end());
        key_compare = _X.key_compare;
        _Copy(_X);
    }
    return (*this);
}
先刪除自己, 然後調用 _Copy(const _Myt&);

void _Copy(const _Myt& _X)
{
    _Root() = _Copy(_X._Root(), _Head);
    _Size = _X.size();
    if (_Root() != _Nil)
    {
        _Lmost() = _Min(_Root());
        _Rmost() = _Max(_Root());
    }
    else
        _Lmost() = _Head, _Rmost() = _Head;
}

其中又調用了 _Copy(_Nodeptr, _Nodeptr);

_Nodeptr _Copy(_Nodeptr _X, _Nodeptr _P)
{
    _Nodeptr _R = _X;
    for (; _X != _Nil; _X = _Left(_X))      // error here
    {
        _Nodeptr _Y = _Buynode(_P, _Color(_X));
        if (_R == _X)
            _R = _Y;
        _Right(_Y) = _Copy(_Right(_X), _Y);
        _Consval(&_Value(_Y), _Value(_X));
        _Left(_P) = _Y;
        _P = _Y;
    }
    _Left(_P) = _Nil;
    return (_R);
}

看標記的那一行. _X 與 _Nil 比較. 其中的 _Nil 如下:
static _Tree<_K, _Ty, _Kfn, _Pr, _A>::_Nodeptr _Tree<_K, _Ty, _Kfn, _Pr, _A>::_Nil = 0;

是一個靜態變量. 初始值爲 0. 在一個 module (注: 這裏的 module 是指的一個exe, 或者 dll. 下同) 中構建第一個 map 實例時, 有這樣的代碼:
                if (_Nil == 0)
                        {_Nil = _Buynode(0, _Black);
                        _Left(_Nil) = 0, _Right(_Nil) = 0; }

如果 _Nil 未初始化則創建一個 node, 初始化 _Nil. 然後 map 將內部的 _Head._Parent 指向這個 _Nil.

設想這樣一種情形. 一個 EXE, 一個 DLL.
                       
EXE:
void main()
{
 map m;
 func(m);
}

DLL:
void func(map& m)
{
 map n = m;
}

在 EXE 中構建了一個 map 實例. 然後傳到 DLL 中做賦值操作.
分析執行過程, 首先 EXE 中的 m 初始化, 完成之後 m._Head._Parent  指向了一個 _Nil 節點. 然後這個 m 傳到 dll 中. 此時, n 進行初始化, 又執行這樣的代碼:
                if (_Nil == 0)
                        {_Nil = _Buynode(0, _Black);
                        _Left(_Nil) = 0, _Right(_Nil) = 0; }

注意, 在 DLL 中, 這裏的 _Nil 爲 0. 因爲這個 _Nil 和 EXE 中的 _Nil 並不是同一份拷貝. 因此又會創建一個 node, 然後讓 _Nil 指向它. 再讓 n._Head._Parent 指向這個 _Nil.
問題在這裏開始出現了. map 的代碼認爲其所有的實例的 _Head._Parent 都指向同一個 _Nil. 但這裏已經違背了這個原則.
最終的結果就是 crash. 在這個例子中, crash 出現在 _Copy(_Nodeptr, _Nodeptr) 函數中. 

下載示例源碼. (右擊, 目標另存爲.., 得到一 .png, 改名爲 .rar) 如果要編譯其中的代碼. 比如使用 vc6, 可以執行 C:/Program Files/Microsoft Visual Studio/VC98/Bin/VCVARS32.BAT 然後在其中用命令行編譯.

發佈了64 篇原創文章 · 獲贊 2 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章