isspace函数的debug版本处理中文字符时程序异常

示例代码:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    using namespace std;
     
    inline string& ltrim(string &str) {  
        string::iterator p = find_if(str.begin(), str.end(), std::not1(ptr_fun(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(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 解决此问题
---------------------

原文:https://blog.csdn.net/liangzhao_jay/article/details/78064399

你可能感兴趣的:(C/C++,VC/MFC,qt)