int向string赋值潜在的溢出bug

来源:互联网 发布:淘宝店铺护肤品简介 编辑:程序博客网 时间:2024/05/16 08:46

2011-11-1 wcdj

在程序中,有时会这样写代码:

#define XXX_ID 1string str_result;str_result = XXX_ID;// ...int iResultCode = atoi(str_result.c_str());
问题:这样的代码是否正确?

程序员本意的想法是iResultCode 应该为 1:

// errorstr = 1; int iRes = atoi(str.c_str());// iRes == 1 ? , wrong! iRes == 0

实际的情况:
str = 数字,编译器会把这个数字解释为一个字符的ASCII值,从而把这个字符通过重载赋值运算符函数,赋值给str这个对象。因此,当str = 49; 时,str被赋值为字符 '1' 。然后,再通过atoi转换,iRes的值也为1。
// okstr = 49; int iRes = atoi(str.c_str());// iRes == 1, ok

原因分析:

string str;str = 1;会调用:_Myt& operator=(_Elem _Ch)// _Ch == 1{// assign 1 * _Chreturn (assign(1, _Ch));}str = '1';会调用:_Myt& operator=(_Elem _Ch)// _Ch == 49{// assign 1 * _Chreturn (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;// 0str = 1;// 1str = 127;// 127str = 128;// -128 str = 255;// -1str = 256;// 0str = 257;// 1*/// warning C4305: 'argument' : truncation from 'int' to 'char'// warning C4309: 'argument' : truncation of constant valuestr3 = 257;str4  = 1;// ok, no warningif (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的则不会。