您的位置:首页 > 其它

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

2017-09-22 16:34 405 查看
开发环境 :VS2008

示例代码:

#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 解决此问题

    

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: