int向string赋值潜在的溢出bug

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的则不会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值