实用经验 18 如何判定变量是否相等?

判断两个变量是否相等,我想是所有程序开发人员都会碰到的问题。肯定有人会说判断两个变量是否相等还不简单:假设两个变量分别为a和b,那如果(a==b)为真就说明a和b相等了,否则就说明不相等。也许大多数人都这么认为,那说明你们还是C/C++开发新手,其实在C/C++中判断两个变量相等与否是一件很麻烦的事情。下面我们将慢慢阐述。

其实判断两个变量是否相等,有两点是非常重要的:1、两个变量分别都是什么类型?首先两个变量的类型应该是一样的。如果两个变量都不是一个类型,那判断两者是否相等也就无任何意义了。这个很容易理解。2、变量相等的判断依据是什么?这一点也许大家不是很理解。这也是判断两个变量是否相等最为关键的地方。

小心陷阱

  • 判断两个变量是否相等时,两个变量类型必须相同。绝对不允许出现:一个为int,而另一个为short这种情况。
  • 每种类型的变量,其判定依据各不相同。有些可判定是否相等,有些则根本无法判断是否相等。只有那些允许判定是否相等的变量才可以判断是否相等。

编程时,我们接触的变量主要有这么几种:布尔变量,整型值,浮点型变量,字符串变量,指针变量。本实用经验的后续部分将主要讲述各类变量是否相等的判定依据。

bool值比较

bool变量表示真值true和false。在C++中布尔值的类型是bool。Bool变量一般描述某一操作执行成功与否,成功返回true,否则返回false。布尔类型的变量一般无法判定是否相等。因为判断两个bool变量相等无任何意义。

整型值比较

整型值一般包括(unsigned)char,(unsigned)short,(unsigned)int这几种数据类型。判断两个整型变量是否相等是我们经常遇到的编程问题。判断两个整型变量是否相等,一般有以下两步骤:

(1)一般应保证两整型值为同一类型,如果两个整型值不是同一类型,C++编译器会默认将类型阶低的变量隐式转化为阶高的变量(参见实用经验16)。

(2)两整型值类型相同,通过C++提供的“==”操作符即可进行两个整型变量是否相等的判定。

小心陷阱

在判断两个整型值是否相等时,应尽量保证两个整型值为同种类型:

  • 如果两者不是同类型,C++编译器会进行隐式类型转换。因为编译的隐式转化有时会给我带来很多意想不到的麻烦,所以一般禁止两整型值比较式发生隐式转化。
  • 如果两者不是同类型,不仅应该禁止隐式转化,显示类型转化更应该是明令禁止的。在这种情况下,你应该重新审视设计。以避免这类问题的出现。

为了把整型值的变量比较讲述的更加明了,我们看下面这两个例子: 首先我们看一个隐式转化的例子。

// 判断两个数,是否相等。
char cValue1 = 255;
int  nValue2 = -1;
if (cValue1 == nValue2)
{
    printf(“cValue == nValue2”);
}
else
{
    printf(“cValue != nValue2”);
}

如果问你上述代码的执行结果,我想你肯定会说上述代码的执行结果是cValue != nValue2。但实际上是这样的吗?答案是:上述代码的执行结果是cValue == nValue2。也许你不相信这一执行结果,那请你把上述代码在VS2010上执行以下。也许你就相信了。

下面我们分析一下为什么会是这样的执行结果:在实用经验5中我们讲过char的取值范围为-128-127。如果给一个char型变量cValue1赋值255。cValue1应该等于1111 1111B。由于变量中第一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) 
    {
        return true;
    }
    if (fabs(fValueA - fValueB)<absError )
    {
        return true;
    }
    if (fabs(fValueA > fValueB)
    {
        return (fabs((a-b)/a>relError ) ? true : false;
    }
    return (fabs((fValueA - fValueB)/b>relError ) ? true : false;
}

实现说明:fValueA和fValueB表示两个浮点数, absError是预设的绝对精度,relError是相对精度。

缺点:无。

适用环境:此方法适用于所以浮点数是否相等进行判定的场合。

字符串比较

字符串是我们编程过程中普遍使用的,一个字符串一般有多个字符组成。两个字符串相等要满足两个条件:(1)两个字符串的长度必须相等,(2)两个字符串的每个字符必须相等。

C语言标准库提供了几个标准函数可比较两个字符串是否相等。他们是strcmp和strcmpi()。strcmp对两个字符串进行大小写敏感的比较,strcmpi() 对两个字符串进行大小写不敏感的比较。

int     strcmp(_In_z_ const char * _Str1, _In_z_ const char * _Str2);
int     strcmpi(_In_z_ const char * _Str1, _In_z_ const char * _Str2);

两函数的输入、输出及功能说明,如表2-2所示。

表2-2 字符串比较函数功能表
函数名称_Str1_Str2功能返回值说明
strcmp字符串1字符串2大小写敏的比较< 0第一个字符串小于第二个字符串
= 0两个字符串相等
> 0第一个字符串大于第二个字符串
strcmpi字符串1字符串2大小写不敏的比较< 0第一个字符串小于第二个字符串
= 0两个字符串相等
> 0第一个字符串大于第二个字符串

指针值比较

指针变量是C++中功能最强大,也是出现问题最多的地方。指针变量在比较时,必须保证两个变量是同一类型的指针变量。为了说明指针类型在指针变量比较时的作用,我们看下面这段代码:

// 父类:基类CBaseA
class CBaseA    
{
public:
    // 构造函数
    CBaseA() {}
    // 析构函数
    ~CBaseA() {}
};
// 父类:基类CBaseB
class CBaseB    
{
public:
    // 构造函数
    CBaseB() {}
    // 析构函数
    ~CBaseB() {}
}; 
// 派生类CDrived,派生于CBaseA和CBaseB
class CDrived : public CBaseApublic 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。这也许会超乎你的想象,但实际上计算机确实是这么处理的,指针变量在强制转换后,已经失去了其原本的面目。

小心陷阱:指针变量除了可比较是否相等外,无法进行大于和小于的比较。

请谨记

  • 在比较变量是否相等时,首先需要明确变量的类型,因为不同的变量其比较方式是不同的。这是您必须注意的。
  • 在比较变量是否相等时,请小心隐式转换。注意隐式转换时编译器偷偷替你做了什么。并注意这些隐式操作给你带来的副作用。
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值