浅谈指针的比较

1、前言

有人说指针是C语言的灵魂,也有人说没学好指针就等于不会C语言。html

虽然在现代C++中通常都是推荐尽可能避免使用原生的raw指针,而是以smart pointer 和reference替代之。可是不管怎样,对于C/C++来讲,指针始终是个绕不过去的坎。究其缘由,是由于C/C++都是支持面向底层操做的语言,而 面向底层操做就得能操纵内存,这个时候就须要指针了。为何呢?我的以为指针实际上就是对机器语言/ASM中的经过虚拟地址操做内存的这一行为的一种抽 象。linux

例如ide

movl %eax, (%edx)

将寄存器eax中的值写入内存地址为寄存器edx的值的内存中。若是把edx看作一个指针的话,也就至关于函数

*p_edx = value_eax

2、指针的比较

关于指针,有着许多技巧和用途,后文主要谈谈关于C++中指针比较操做的一些容易踏入的坑。spa

先来看看这段代码指针

 1 class BaseA
 2 {
 3 public:
 4     int a;
 5 };
 6 
 7 class BaseB
 8 {
 9 public:
10     double b;
11 };
12 
13 class Derived : public BaseA, public BaseB
14 {
15 };
16 
17 int main(int argc, char const *argv[])
18 {
19     Derived derivd;
20     Derived* pd = &derivd;
21     BaseB* pb = &derivd;
22     printf("pb = %p\n", pb);
23     printf("pd = %p\n", pd);
24     if (pb == pd)
25     {
26         printf("pb == pd\n");
27     }
28     else
29     {
30         printf("pb != pd\n");
31     }
32 }

输出的结果是:orm

pb = 0028FEE0htm

pd = 0028FED8对象

pb == pd继承

能够看到指针pb和pd值并不同,可是编译器却认为他们相等,为何呢?

1.  当2个指针的静态类型以及所指对象的类型都属于同一个继承层次结构中,而且其中一个指针类型是所指对象的静态类型的时候,指针的比较,实际上比较的是两个指针是否指向同一个对象。

若2个指针指向同一个对象,被编译器决议为相等;编译器在比较的时候加上适当的offset值。例如上面的状况,至关于在比较的时候编译器作了这样的改动:

if(pd == (pb - sizeof(int))

若2个指针指向不一样的对象,就被决议为不相等,而且比较的是指针保存的地址的值的大小。

 1 int main(int argc, char const *argv[])
 2 {
 3     Derived derived1;
 4     Derived derived2;
 5     Derived* pd = &derived1;
 6     BaseB* pb = &derived2;
 7     printf("%p\n", pd);
 8     printf("%p\n", pb);
 9     if (pd < pb)
10     {
11         printf("pd < pb\n");
12     }
13     else if (pd == pb)
14     {
15         printf("pd == pb\n");
16     }
17     else
18     {
19         printf("pd > pb\n");
20     }
21 }

获得的结果为:

0028FED8

0028FED0

pd > pb

2.  当2个指针的静态类型不属于同一个继承层次结构中,可是2个指针都指向同一个对象的时候,该比较是违法行为,编译器会报编译期错误

 1 int main(int argc, char const *argv[])
 2 {
 3     Derived derivd;
 4     Derived* pd = &derivd;
 5     int* pb = reinterpret_cast<int*>(&derivd);
 6     printf("pb = %p\n", pb);
 7     printf("pd = %p\n", pd);
 8     if (pb == pd)
 9     {
10         printf("pb == pd\n");
11     }
12     else
13     {
14         printf("pb != pd\n");
15     }
16 }

编译器报错为:

 

error: comparison between distinct pointer types 'int*' and 'Derived*' lacks a cast [-fpermissive]if (pb == pd)

3.  当2个指针的静态类型以及所指对象类型都属于同一个继承层次结构,可是2个指针的静态类型都不是所指对象的类型时,该比较是违法行为,编译器会报编译期错误:

 1 int main(int argc, char const *argv[])
 2 {
 3     Derived derivd;
 4     BaseB* pb = &derivd;
 5     BaseA* pa = &derivd;
 6     printf("pb = %p\n", pb);
 7     printf("pd = %p\n", pa);
 8     if (pb == pa)
 9     {
10         printf("pb == pa\n");
11     }
12     else
13     {
14         printf("pb != pa\n");
15     }
16 }

编译器报错为:

 

error: comparison between distinct pointer types 'BaseB*' and 'BaseA*' lacks a castif (pb == pa)

另一些其余的行为,例如2个指针的类型同属于一个继承层次,而后经过强制类型转换让他们俩都指向一个不属于该继承层次的对象,这样的行为都是为未定义行为,也许编译器不会报编译期错误,但结果是未定义的,多是任何结果。

可能有人会说,何时指针比较的是他们所保存的地址的值呢呢?

答案是当2个指针的静态类型相同的时候:

 1 int main(int argc, char const *argv[])
 2 {
 3     Derived derived1;
 4     Derived derived2;
 5     Derived* p1 = &derived1;
 6     Derived* p2 = &derived2;
 7     if (p1 < p2)
 8     {
 9         printf("p1 < p2\n");
10     }
11     else if (p1 == p2)
12     {
13         printf("p1 == p2\n");
14     }
15     else
16     {
17         printf("p1 > p2\n");
18     }
19 }

结果为:p1 > p2

3、shared_ptr的owner_before

boost::shared_ptr/std::shared_ptr中有一个owner_before成员函数,原型为

template <class U> bool owner_before (const shared_ptr<U>& x) const;
template <class U> bool owner_before (const weak_ptr<U>& x) const;

当该shared_ptr和x的类型同属一个继承层次时,无论他们类型是否相同,他们两都被决议为相等。当他们的类型不属于同一继承层次时,比较的为指针的地址值的大小。

 1 int main()
 2 {
 3     boost::shared_ptr<Derived> pd(new Derived);
 4     boost::shared_ptr<BaseB> pb(pd);
 5     printf("%p %p\n", pd.get(), pb.get());
 6     printf("%d %d\n", pd < pb, pb < pd);  // 0 0
 7     printf("%d %d\n", pd.owner_before(pb), pb.owner_before(pd));  // 0 0
 8     boost::shared_ptr<void> p0(pd), p1(pb);
 9     printf("%p %p\n", p0.get(), p1.get());
10     printf("%d %d\n", p0.get() < p1.get(), p1.get() < p0.get());  // 1 0
11     printf("%d %d\n", p0.owner_before(p1), p1.owner_before(p0));  // 0 0
12 }

为何shared_ptr会提供这样的成员函数呢?

由于一个智能指针有可能指向了另外一个智能指针中的某一部分,但又要保证这两个智能指针销毁时,只对那个被指的对象完整地析构一次,而不是两个指针分别析构一次。

在这种状况下,指针就能够分为两种,一种是 stored pointer 它是指针自己的类型所表示的对象(多是一个大对象中的一部分);另外一种是 owned pointer 指向内存中的实际完整对象(这一个对象可能被许多智能指针指向了它里面的不一样部分,但最终只析构一次)。owner-based order 就是指后一种状况,若是内存中只有一个对象,而后被许多 shared pointer 指向了其中不一样的部分,那么这些指针自己的地址确定是不一样的,也就是operator<()能够比较它们,而且它们都不是对象的 owner,它们销毁时不会析构对象。但它们都指向了一个对象,在owner-based order 意义下它们是相等的。

4、总结

  • 指针之间的比较只能是指针的静态类型相同,或者同属于同一继承层次且其中一个指针的静态类型为所指对象的类型。

  • 指针的静态类型相同时,比较的是地址的值的大小。

  • 指针的静态类型不一样,可是同属于同一继承对象,而且其中一个指针的静态类型为所指对象的类型时,比较的是两指针是否指向同一对象。如果指向同一对象,则两指针相等;若不是指向同一对象,则比较指针的地址值的大小。

  • 智能指针shared_ptr/weak_ptr的onwer_before成员函数描述的是:当比较的2个智能指针的类型属于同一继承层次时表现为“相等”的意义;当2个只能指针的类型不属于同一继承层次时,比较的是指针的地址值的大小。

(完)