在网络通信中经常用到16进制和ASCII码字符数组互相转换的功能,虽然功能简单,但初学者写出来的代码经常会有各种问题。从16进制转为ASCII吗的功能函数,一个比较简洁的实现版本如下:
void HexToAscii( char * Dest, char * Src, int SrcLen )
{
for ( int i = 0, j = 0; i < SrcLen; i++ )
{
char HiHalf = Src[i] >> 4;
char LoHalf = Src[i] & 0x0F;
Dest[j++] = ( HiHalf <= 9 ) ? ( HiHalf + '0' ) : ( HiHalf - 10 + 'A' );
Dest[j++] = ( LoHalf <= 9 ) ? ( LoHalf + '0' ) : ( LoHalf - 10 + 'A' );
}
}
在以上短短几行代码里,基础比较扎实的同学比较容易发现的一个Bug是用signed char来处理二进制数据,这样当被处理的字节Src[i]是负数时就会出现错误的转换结果。同样把HiHalf和LoHalf声明为int,也是有相同Bug的。解决办法是先把Src[i]转为unsigned char,并且把HiHalf和LoHalf定义为unsigned char。
一个比较隐蔽的严重问题是当Dest和Src字符数组有重叠的地址空间时,转换结果也会不对。例如Dest和Src的地址相等时,Src[0]转换为Dest[0]和Dest[1]后,Src[1]被Dest[1]覆盖,Src[1]还没转换里面的数据已经不是原来的数据了,依次类推。
除此以外,传进来的指针Dest和Src都要先判断是否等于NULL,这样才能尽量保证写出安全的代码。
最后一点,最好把Dest和Src声明为void *类型的指针,这样对非char类型的数据调用此函数时,就不需要强制转化。
修正后的代码如下:
void HexToAscii( void * DestBuf, void * SrcBuf, int SrcLen )
{
if ( ( DestBuf == NULL ) || ( SrcBuf == NULL ) )
{
return;
}
unsigned char * Dest = (unsigned char *) DestBuf;
unsigned char * Src = (unsigned char *) SrcBuf;
int DestPos = SrcLen << 1;
int SrcPos = SrcLen;
while ( --SrcPos >= 0 )
{
unsigned char HiHalf = Src[SrcPos] >> 4;
unsigned char LoHalf = Src[SrcPos] & 0x0F;
Dest[--DestPos] = ( HiHalf <= 9 ) ? ( HiHalf + '0' ) : ( HiHalf + 55 );
Dest[--DestPos] = ( LoHalf <= 9 ) ? ( LoHalf + '0' ) : ( LoHalf + 55 );
}
}
有兴趣的同学可以写个单元测试验证一下转化结果。或者改用效率更高的查表法来实现,更加节省计算步骤和时间。