判断随机字符串之间的是否相等是程序设计中常用的技巧,再C++时代,我们可以通过把字符串中每四个字节转换为一个int对象,通过int对象一次比较四个字符,从而实现相对高效的字符串比较工作。那么,这个思路在C#中能否是实现呢?答案是肯定的。 在C#中使用上述思想,必须要解决两个问题,其一是在C#中使用指针,并且指针指向的托管变量位置不能被GC重新分配。其二,托管字符串在内存中与int或long之间的对应关系。 很 多文章中已经详细描述了在C#中使用指针的方法,本文不再详细叙述,开启unsafe开关的方式为,右键单击解决方案目录——选择属性——再Build对 话框中选中“允许unsafe代码”选项, 这样再C#中就可通过unsafe关键字标记可以使用指针的区域了。我们知道,被托管的变量由系统随机分配、回收和调整在内存中的位置,因此,指向托管变 量的指针可能会由于托管变量被随机调整而指向错误区域。为了保证指针自始至终都能指向同一个托管变量,C#中提供了fixed语句来完成该任务。被标记了 fixed的指针,在fixed所标志的区域中回自始至终的指向改变量,而系统不会对该托管变量进行调整和再分配。代码如下: 使用sizeof可以测出,C#中long占用8个字节,那么在上面代码中如果将ps的前8个字节强转为long型,得到的结果是什么呢?我们利用如下代码进行测试: 可 以看出,上面MessageBox中的输出分别为H,e和l,l共4个字符,并非我们直观上认为的8个字符,这是由于在转换的过程中,每个字符从byte 被转成为一个2个字节的短整型short而造成的,因此,将字符串指针强转为long以后,该long型变量的值可以表示4个字符,那么,比较两个 long型对象的值实际上就是在比较这4个字符的值,采用这个思路,实际上实现了做一次比较操作就能同时比较4个字符。 比较两字符串是否相等的代码如下:
string str= " Hello World! "; unsafe { fixed ( char* ps = str) { // 该区域中ps始终指向托管字符串str } }
long n= 0; long nLow = 0, nHigh = 0; string str = " Hello World! "; unsafe { fixed ( char* ps = str) { char* psTemp = ps; n=*( long*)psTemp; nLow = n & 0xFFFF; // 取最后两个字节 nHigh = (n >> 16) & 0xFFFF; // 取第3,4个字节 MessageBox.Show((( char)nLow).ToString() + " " + (( char)nHigh).ToString()); nLow = (n >> 32) & 0xFFFF; // 取第5,6个字节 nHigh = (n >> 48) & 0xFFFF; // 取第7,8个字节 MessageBox.Show((( char)nLow).ToString() + " " + (( char)nHigh).ToString()); } }
protected bool IsEquals( string str1, string str2) { bool bRet = true; int nC1 = 0, nC2 = 0, nLen = 0; int i = 0; if (str1.Length != str2.Length) // 长度不相等则字符串不相等 { return false; } // 长度不是4的倍数则补位 nC1 = ((str1.Length % 4) != 0) ? ( 4 - str1.Length% 4) : 0; // 计算补偿位 nC2 = ((str2.Length % 4) != 0) ? ( 4 - str1.Length% 4) : 0; // 计算补偿位 nLen = (nC1 > nC2) ? nC1 : nC2; for (i = 0; i { if (i < nC1) { str1 += " "; } if (i < nC2) { str2 += " "; } } unsafe { fixed ( char* psStr1 = str1) fixed ( char* psStr2 = str2) { char* psTemp1 = psStr1; char* psTemp2 = psStr2; while (i < str1.Length) { if (*( long*)psTemp1 != (*( long*)psTemp2)) // 一次比较4个字符 { bRet = false; break; } i += 4; psTemp1 += 4; psTemp2 += 4; } } } return bRet; }