判断两个变量是否相等,是所有程序开发人员都会碰到的问题。肯定有人会说判断两个变量是否相等还不简单:假设两个变量分别为a和 b,如果(a=b)为真就说明 a和 b相等,否则就说明不相等。也许大多数人都这么认为,那说明你们还是C/C++开发新手,其实在 C/C++中判断两个变量相等与否是一件很麻烦的事情,下面我们将慢慢阐述。
其实判断两个变量是否相等,有两点是非常重要的:一是两个变量分别都是什么类型。首先两个变量的类型应该是一样的,如果两个变量都不是一个类型,那判断两者是否相等也就无任何意义了。二是变量相等的判断依据是什么。这一点也许大家不是很理解,这也是判断两个变量是否相等最为关键的地方。
小心陷阱
(1)判断两个变量是否相等时,两个变量类型必须相同,绝对不允许出现一个为int而另一个为 short这种情况。
(2)每种类型的变量,其判定依据各不相同。有些可判定是否相等,有些则根本无法判断是否相等。只有那些允许判定是否相等的变量才可以判断是否相等。
编程时,我们接触的变量主要有这么几种:布尔变量、整型值、浮点型变量、字符串变量、指针变量。本实用经验的后续部分将主要讲述各类变量是否相等的判定依据。
在C++中布尔值的类型是 bool。bool变量一般描述某一操作执行成功与否,成功返回true,否则返回false。布尔类型的变量一般无法判定是否相等,因为判断两个 bool变量相等无任何意义。
整型值一般包括(unsigned)char、(unsigned)short、(unsigned)int 这几种数据类型。判断两个整型变量是否相等是我们经常遇到的编程问题。判断两个整型变量是否相等,一般有以下两个步骤:
(1)应保证两个整型值为同一类型,如果两个整型值不是同一类型,C++编译器会默认将类型阶低的变量隐式转换为阶高的变量(参见实用经验16)。
(2)如果两个整型值类型相同,通过C++提供的“==”操作符即可进行两个整型变量是否相等的判定。
小心陷阱
在判断两个整型值是否相等时,应尽量保证两个整型值为同种类型。
(1)如果两者不是同类型,C++编译器会进行隐式类型转换。因为编译的隐式转换有时会给编程人员带来很多意想不到的麻烦,所以一般禁止两个整型值比较时发生隐式转换。
(2)如果两者不是同类型,不仅应该禁止隐式转换,显式类型转换更应该是明令禁止的。在这种情况下,应该重新审视设计,以避免这类问题的出现。为了把整型值的变量比较讲述得更加明了,来看下面这两个例子。首先看一个隐式转换的例子:
//判断两个数是否相等
char cValue1=255;
int nValue2=-1;
if(cValue1 =nValue2)
{
printf("cValue -nValue2");
{
else
{
printf("cValue != nValue2");
}
如果问你上述代码的执行结果,你肯定会说上述代码的执行结果是cValue !=nValue2。但实际上是这样的吗?答案是:上述代码的执行结果是 cValue == nValue2。也许你不相信这一执行结果,那请你把上述代码在VisualStudio 2010上执行一下,也许你就相信了。
下面我们分析一下为什么会是这样的执行结果。在实用经验5中我们讲过char的取值范围为-128~127。如果给一个char 型变量cValue1赋值 255,cValue1 应该等于11111111B。由于变量中第一 bit 代表变量的正负值,1 代表负数,将 char 变量隐式转换为int 变量时会进行符号位扩展,所以cValue1会提升为一个int 型临时变量temp,其值等于1111,1111,1111,1111,1111,1111,1111,1111B(此补码值对应原码值为-1)。所以C++编译器在编译时会将上述代码转换为下述代码,然后进行编译:
//判断两个数是否相等
char cValue1 =255;
int nValue2=-1;
int temp =cValue1:
if(temp ==nValue2)
{
printf("cValue = nValue2");
}
else
{
printf("cValue !=nValue2");
}
通过上述分析可以看出,cValue1 首先提升为 int 型变量temp,其值等于-1,在if判断时为真,所以会打印出cValue ==nValue2语句。
浮点型变量包括单精度浮点型和双精度浮点型。浮点数的比较不能通过简单的“==”进行判定,必须通过差值的绝对值的精度判定。浮点型变量的这种判定标准是由浮点数据在内存中存放的格式决定的。
小心陷阱
判断两个浮点型变量是否相等,不能简单地通过“==”运算符实现。在内存中,没有任何两个变量是完全相等的。
如果两个浮点型变量非常接近时,“==”运算结果是随机值,所以不能用“==”运算符进行两个浮点数是否相等的判定。
判断两个浮点型变量是否相等的实现方法及其优缺点如下。
(1)利用差值的绝对值精度来判断。先来看看这种方法的代码实现:
bool IsEqual(float fValueA, float fValueB,float frelError)
{
if(fabs(fValueA-fValueB)<=frelError)
{
return true;
}
return false;
}
实现说明:fValueA和 fValueB表示两个浮点数,frelError 是预设的精度,如 1e-6。如果要求更高的精度,可以把frelError 设置得更小一些。
缺点:此方法使用误差分析中所说的绝对误差,在某些场合下基于绝对误差下进行的判断是不可靠的。比如frelError 取值为 0.0001,而fValueA 和 fValueB也在 0.0001附近,那么显然不合适。另外,对于 fValueA 和 fValueB 大小是 10000 这样数据的时候,它也不合适,因为10000和10001也可以是伪相等的。
适用环境:fValueA或fValueB在1或者0附近的时候。
(2)利用差值的相对值精度来判断。先看看这种方法的代码实现:
bool IsEqual(float fValueA, float fValueB,float frelError)
{
if(fabs(fValueA)<fabs(fValueB))
{
return (fabs((fValueA-fValueB)/fValueA)>frelError)?(true):(false);
}
return (fabs((fValueA-fValueB)/fValueB)>frelError)?(true):(false);
}
实现说明:此方法通过相对误差实现,fValueA和fValueB表示两个浮点数,frelError 是预设的相对精度,比如 1e-6。如果要求更高的精度,可以把 frelError 设置得更小一些。
缺点:在某些情况下,相对精度也不能代表全部。例如在判断空间三点是否共线的时候,使用判断点到另外两个点形成的线段的距离的方法的时候。
适用环境:适用于那些通过相对误差可进行是否相等判定的场合。
(3)绝对精度和相对精度相结合的方法。先看这种方法的代码实现:
bool IsEqual(float fValueA, float fValueB, float absError, float relError)
{
If(fValueA == b)
{
returm true;
}
if(fabs(fValueA-fValueB)<absError)
{
return true;
}
if(fabs(fValueA>fValueB)
{
return (fabs((a-b)/a>relError)?true : false;
}
retum (fabs((fValueA-fValueB)/b>relEror ) ?true :false;
}
实现说明:fValueA和fValueB表示两个浮点数,absError 是预设的绝对精度relError 是相对精度。
缺点:无。
适用环境:此方法适用于所有浮点数是否相等判定的场合。
字符串是编程过程中普遍使用的,一个字符串一般由多个字符组成。两个字符串相等要满足两个条件:一是两个字符串的长度必须相等;二是两个字符串的每个字符必须相等。
C语言标准库提供了几个标准函数,可比较两个字符串是否相等,它们是 strcmp和 strcmpi()。strcmp 对两个字符串进行大小写敏感的比较,strcmpi()对两个字符串进行大小写不敏感的比较。例如:
int strcmp(_In_z_const char*_Strl,_In_z_const char*_St2);
int strcmpi(_In_z_const char*_Strl,_In_z_const char*_Str2);
两个函数的输入、输出及功能说明如表2-2所示。
表2-2字符串比较函数功能表
指针变量是C++中功能最强大,也是出现问题最多的地方,在进行比较时必须保证两个变量是同一类型的指针变量。为了说明指针类型在指针变量比较时的作用,我们看下面这段代码:
//父类:基类CBaseA
class CBaseA
{
public.
//构造函数
CBaseA() {}
//析构函数
~CBaseA(){}
};
//父类:基类CBaseB
class CBaseB
{
public:
//构造函数
CBaseB() {}
//析构函数
~CBaseB() {}
};
//派生类 CDrived,派生于 CBaseA和 CBaseB
class CDrived : public CBaseA, public CBaseB
{
public:
//构造函数
CDrived() {}
//析构函数
~CDrived() {}
};
int _tmain(int argc, char* argv[])
{
CDrived d;
//将d对象地址转换为CDrived*指针地址
CDrived*pd=&d;
//将d对象地址转换为CBaseB*指针地址
CBaseB*pb=&d;
//判断 pd指针地址和 pb指针地址是否相等,如果相等则输出 pVoidD一pVoidB
//否则输出pVoidD!-pVoidB
void*pVoidD =pd;
void *pVoidB=pb;
if(pVoidD == pVoidB)
{
printf("pVoidD==pVoidB");
}
else
{
printf("pVoidD !=pVoidB");
}
return 0;
}
上述这段代码的执行结果是:pVoidD!= pVoidB。这也许会超乎你的想象,但实际上计算机确实是这么处理的,指针变量在强制转换后已经失去了其本来的面目。
小心陷阱
指针变量只能比较是否相等,无法进行大于和小于的比较。、
请谨记
●在比较变量是否相等时,首先需要明确变量的类型,因为不同的变量其比较方式是不同的。
●在比较变量是否相等时,请小心隐式转换。注意隐式转换时编译器私下做了什么,并注意这些隐式操作带来的副作用。