isspace函數的debug版本處理中文字符時程序異常

示例代碼:

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <algorithm>
    #include <functional>
    #include <locale>
    #include <cctype>
     
    using namespace std;
     
    inline string& ltrim(string &str) {  
        string::iterator p = find_if(str.begin(), str.end(), std::not1(ptr_fun<int, int>(isspace)));  
        str.erase(str.begin(), p);  
        return str;  
    }  
     
    inline string& rtrim(string &str) {  
        string::reverse_iterator p = find_if(str.rbegin(), str.rend(), std::not1(ptr_fun<int , int>(isspace)));  
        str.erase(p.base(), str.end());  
        return str;  
    }  
     
    inline string& trim(string &str) {  
        ltrim(rtrim(str));  
        return str;  
    }  
     
    int main(){
     
        #if _DEBUG
        setlocale(LC_ALL, "chs");
        #endif
        string str = "\t\r\n 123sggdery中 國455 68 \r\n";  
        string str1 = str;  
        string str2 = str;  
     
        cout << "str: ~" << str << "~" << endl << endl;  
     
        cout << "ltrim(str): ~" << ltrim(str1) << "~" << endl;  
        cout << "rtrim(ltrim(str)): ~" << rtrim(str1) << "~" << endl << endl;  
     
        cout << "rtrim(str): ~" << rtrim(str2) << "~" << endl;  
        cout << "ltrim(rtrim(str)): ~" << ltrim(str2) << "~" << endl << endl;  
     
        cout << "trim(str): ~" << trim(str) << "~" << endl;  
     
        return 0;  
    }


上述 代碼(沒有設置 setlocale 的時候 )在debug編譯的情況下會 assert失敗。沒有辦法,只好跟蹤到c運行庫裏,isspace的實現如下(在"_ctype.c"文件裏):


extern __inline int (__cdecl isspace) (
        int c
        )
{
    if (__locale_changed == 0)
    {
        return __fast_ch_check(c, _SPACE);
    }
    else
    {
        return (_isspace_l)(c, NULL);
    }
}


    跟蹤發現,__locale_changed的值爲0,走第一個分支,調用__fast_ch_check,它其實是個宏定義,最後進入_chvalidator函數,它的實現代碼如下:


extern "C" int __cdecl _chvalidator(
        int c,
        int mask
        )
{
        _ASSERTE((unsigned)(c + 1) <= 256);
        return _chvalidator_l(NULL, c, mask);
}


    錯誤就發生在這個函數的第一行“ _ASSERTE((unsigned)(c + 1) <= 256);”。
    
    代碼看到這裏,錯誤的原因基本也就出來了。“高”的GBK編碼是“b8 df”,調用isspace函數是逐個字節判斷,但是isspace和_chvalidator接受的參數都是int,這樣就會產生一個char到int的轉型,在vc下,char默認是"signed char",這樣char“b8”轉型到int後,會變成一個負數,然後在assert的時候,又強制轉型爲unsigned,它又變成了一個非常巨大的正數,自然assert就失敗了。
    爲什麼在gcc下沒有錯誤?gcc下的isspace函數實現我倒沒看,不過我的gcc中的char默認就是“unsighed char”,所以即使isspace的實現跟上面的一樣,也不會產生問題。
    另:在win32 release和wince下,也不會有上述問題,因爲這幾種環境下isspace的實現跟上面不一樣。
    
    問題找到了,剩下的就看怎麼解決了。
    首先用用一個最簡單的方法,既然問題發生在轉型上,只要把char的默認類型改爲unsigned char就可以了,vc也提供了這個選項。但這有幾個問題,一是別人用我的代碼的時候還得修改編譯選項,二是修改了整個工程的編譯選項可能會對其它代碼產生不好的影響。
    既然上面的方法不大好操作,那就再想想別的方法。經觀察發現,isspace函數中靠__locale_changed變量控制流程走向,搜索整個crt的源代碼,發現__locale_changed的值只有在setlocale函數中發生了改變。最後,我把代碼進行修改 添加了   setlocale 解決此問題
---------------------


作者:Mr_John_Liang
來源:CSDN
原文:https://blog.csdn.net/liangzhao_jay/article/details/78064399
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章