2011-11-1 wcdj
在程序中,有时会这样写代码:
#define XXX_ID 1
string str_result;
str_result = XXX_ID;
// ...
int iResultCode = atoi(str_result.c_str());
问题:这样的代码是否正确?
程序员本意的想法是iResultCode 应该为 1:
// error
str = 1;
int iRes = atoi(str.c_str());// iRes == 1 ? , wrong! iRes == 0
实际的情况:
str = 数字,编译器会把这个数字解释为一个字符的ASCII值,从而把这个字符通过重载赋值运算符函数,赋值给str这个对象。因此,当str = 49; 时,str被赋值为字符 '1' 。然后,再通过atoi转换,iRes的值也为1。
// ok
str = 49;
int iRes = atoi(str.c_str());// iRes == 1, ok
原因分析:
string str;
str = 1;
会调用:
_Myt& operator=(_Elem _Ch)// _Ch == 1
{ // assign 1 * _Ch
return (assign(1, _Ch));
}
str = '1';
会调用:
_Myt& operator=(_Elem _Ch)// _Ch == 49
{ // assign 1 * _Ch
return (assign(1, _Ch));
}
str = "1";
会调用:
_Myt& operator=(const _Elem *_Ptr)
{ // assign [_Ptr, <null>)
return (assign(_Ptr));
}
一个存在bug的例子:
#include <iostream>
#include <string>
using std::string;
using namespace std;
int main(int argc, char* argv[])
{
string str1, str2, str3, str4;
str1 = "1";
str2 = '1';
/*
str = (char)0; // 0
str = 1; // 1
str = 127; // 127
str = 128; // -128
str = 255; // -1
str = 256; // 0
str = 257; // 1
*/
// warning C4305: 'argument' : truncation from 'int' to 'char'
// warning C4309: 'argument' : truncation of constant value
str3 = 257;
str4 = 1;// ok, no warning
if (str3 == str4)
{
cout << "bug" <<endl;
}
cout << str1.c_str() << endl;
cout << str2.c_str() <<endl;
cout << str3.c_str() <<endl;
cout << str4.c_str() <<endl;
return 0;
}
结论:
用一个常量向string赋值的时候,这个常量的最大有效范围是char,即,0~127,超过这个范围就会被截断,而且用常量向string赋值然后再atoi转换回来,这个结果也是与我们的意愿不符的,并不会返回正确的值。因此,安全的做法应该是:先将int转换为char*,再赋值给string,这样我们想保存的最大数值(ID)可以为最大整型类型所表示的最大数值。
char szTmp[128]={0};
sprintf(szTmp, "%d", 257);
str3 = szTmp;
memset(szTmp, 0 ,sizeof(szTmp));
sprintf(szTmp, "%d", 1);
str3 = szTmp;
安全一点的做法是使用:snprintf(Linux)/ _snprintf(Windows)。
const int iLen = 128;
char szTmp[iLen] = {0};
_snprintf(szTmp, sizeof(szTmp)-1, "%d", 257);
str3 = szTmp;
memset(szTmp, 0 ,sizeof(szTmp));
_snprintf(szTmp, sizeof(szTmp)-1, "%d", 1);
str3 = szTmp;
注意:snprintf(Linux)与 _snprintf(Windows)对补0有不同,Linux下的自动补0,Windows的则不会。