98年的VC6到08年的VS2008-从atoi的用法差别看UNICODE的使用

由于平时写的较多的是工业设备程序,所以用的XP+VC6比较多,最近写一个小程序,用上了VS2008。之前也用过很长时间的VS2008,没出什么大问题,但是今天将之前在VC6上写的一个工作正常小接口程序拿过来直接用居然不行。自认为一直很注意C++语法标准的问题,也比较清楚VC6和VS2008的差别,可是试了很长时间都没发现问题,最后解决了问题发现是UNICODE的问题。

在这里记下问题,希望对遇到相同问题的人有帮助。


问题:用atoi函数将一个字符串转换为一个整数

基本代码如下

TCHAR szBuffer[10] = TEXT("10");
printf("%d", atoi(szBuffer));
在VC6中这段代码没有问题,但是在VS2008中就会报错 error C2664: 'atoi' : cannot convert parameter 1 from 'unsigned short [10]' to 'const char *'


猜想为UNICODE问题,为了解决问题,我写了完整的测试用例

在VC6中代码如下:

#include <STDIO.H>
#include <windows.h>

int main(int argc, char *argv[])
{
	char string1[10]="123";
	WCHAR string2[10]=L"156";
	TCHAR string3[10]=TEXT("456");

	printf("string1=%d, string2=%d, string3=%d\n", atoi(string1), atoi(string2), atoi(string3));

	return 0;
}
编译出错,检查为atoi(string2)调用错误,报错为 error C2664: 'atoi' : cannot convert parameter 1 from 'unsigned short [10]' to 'const char *'

那我就强制转换一下

改为

printf("string1=%d, string2=%d, string3=%d\n", atoi(string1), atoi((const char *)string2), atoi(string3));
编译正常,结果显示如下

string1=123, string2=1, string3=456
等等,为什么只输出了1,为了验证问题接着用_wtoi输出如下

string2=156
这就对了,_woti是针对UNICODE编码的函数,atoi是针对ASCLL编码的函数

查MSDN,两者的声明分别是

int atoi( const char *string );
int _wtoi( const wchar_t *string );
这里char是ASCLL编码占一个字节,wchar_t为UNICODE编码占两个字节,在微软头文件中可以查到TCHAR、wchat_t和unsigned short是一样的,在故我们使用atoi(string2)时会报错,默认是无法进行UNICODE两个字节向ASCLL单字节的隐式转换的,当我使用(const char *)将string2进行转换时,由于这时候编译器类型检查是对了,但是结果有问题。

理解为什么156强制转换后输出为1这个问题,需要从内存上来看

对于string2为UNICODE编码每个字符占两个字节,"156“对应的内存为6个字节,查询ASCLL表为31 00 35 00 36 00(注意存储结构,低位在前)

那么我们强制转换后,它对应翻译为ASCLL字符串中的字符就依次是'1'、'\0'、‘5’、‘\0’、‘6’、'\0'

atoi是以'\0'检测字符串结尾的,显然这里输出为1


那么,下面我将这段代码搬到VS2008中

#include <STDIO.H>
#include <windows.h>

int main(int argc, char *argv[])
{
	char string1[10]="123";
	WCHAR string2[10]=L"156";
	TCHAR string3[10]=TEXT("456");

	printf("string1=%d, string2=%d, string3=%d\n", atoi(string1), atoi((const char *)string2), atoi(string3));
	printf("string2=%d\n", _wtoi(string2));
	return 0;
}
保存,依旧是 error C2664: “atoi”: 不能将参数 1 从“TCHAR [10]”转换为“const char *”

检测发现atoi(string3)出现问题,联系之前的结论很显然这里的TCHAR代表的是WCHAR才会出现问题


我们知道只有定义了UNICODE标识符,TCHAR才会表示为WCHAR,由此可以发现VC6的默认编码不是UNICODE而在VS2008中默认编码为UNICODE

可以在VC6的工程->设置中添加UNICODE标识

这时候刚才运行正常的问题现在会报和VS2008一样的错误

可以在VS2008的项目->属性中取消设置UNICODE标识



这时候编译正常,运行结果和VC6一样

结论:

所以,这里总结来说可以看到从VC6到VS2008编译器倾向于引导开发者习惯UNICODE编码的开发,这样在开发多国语言的程序版本时不会出现乱码的问题。对于我们开发者来说我们当然期待微软像提供lstrlen这样的自适应编码的函数,但是不知道出于什么原因,貌似现在微软没有提供类似的函数,那就必须要求我们针对当前的程序编码使用对应的函数(ASCLL使用atoi,UNICODE使用_wtoi),否则程序出现莫名其妙的结果和错误往往让我们不知所措。作为笔者,推荐使用UNICODE编码和对应的函数,毕竟现在来说使用ASCLL编码省下来的那点内存开销值五毛钱吗?


补充:没认真看MSDN,谢谢baita96指出,微软提供了自适应函数_ttoi,如果你对程序可移植性没那么高要求,就想在Windows在运行的话用它就不会出错了。

测试用例完整源代码

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值