程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 淺談指針的比較,淺談指針比較

淺談指針的比較,淺談指針比較

編輯:C++入門知識

淺談指針的比較,淺談指針比較


一、前言

  有人說指針是C語言的靈魂,也有人說沒學好指針就等於不會C語言。

  雖然在現代C++中一般都是推薦盡量避免使用原生的raw指針,而是以smart pointer 和reference替代之。但是無論怎樣,對於C/C++來說,指針始終是個繞不過去的坎。究其原因,是因為C/C++都是支持面向底層操作的語言,而面向底層操作就得能操縱內存,這個時候就需要指針了。為什麼呢?個人覺得指針實際上就是對機器語言/ASM中的通過虛擬地址操作內存的這一行為的一種抽象。

  例如

movl %eax, (%edx)

  將寄存器eax中的值寫入內存地址為寄存器edx的值的內存中。如果把edx看做一個指針的話,也就相當於

*p_edx = value_eax

二、指針的比較

  關於指針,有著許多技巧和用途,後文主要談談關於C++中指針比較操作的一些容易踏入的坑。

  先來看看這段代碼

 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 }

  輸出的結果是:

  pb = 0028FEE0
  pd = 0028FED8
  pb == pd

  可以看到指針pb和pd值並不一樣,但是編譯器卻認為他們相等,為什麼呢?

  1.  當2個指針的靜態類型以及所指對象的類型都屬於同一個繼承層次結構中,並且其中一個指針類型是所指對象的靜態類型的時候,指針的比較,實際上比較的是兩個指針是否指向同一個對象。

  若2個指針指向同一個對象,被編譯器決議為相等。編譯器在比較的時候加上適當的offset值,例如上面的情況,相當於在比較的時候編譯器做了這樣的改動:

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

  offset是由C++對象的內存模型決定的,具體的就不再本文討論的范疇了。

  若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 cast
if (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

三、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(int argc, char const *argv[])
 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 意義下它們是相等的。

  cpluscplus中是這樣解釋的:

Returns whether the object is considered to go before x following a strict weak owner-based order.

Unlike the operator< overload, this ordering takes into consideration the shared_ptr's owned pointer, and not the stored pointer in such a way that two of these objects are considered equivalent (i.e., this function returns false no matter the order of the operands) if they both share ownership, or they are both empty, even if their stored pointer value are different.

The stored pointer (i.e., the pointer the shared_ptr object dereferences to) may not be the owned pointer (i.e., the pointer deleted on object destruction) if the shared_ptr object is an alias (alias-constructed objects and their copies).

This function is called by owner_less to determine its result.

  cppreference中是的解釋:

Checks whether this shared_ptr precedes other in implementation defined owner-based (as opposed to value-based) order. The order is such that two smart pointers compare equivalent only if they are both empty or if they both own the same object, even if the values of the pointers obtained by get() are different (e.g. because they point at different subobjects within the same object)

This ordering is used to make shared and weak pointers usable as keys in associative containers, typically through std::owner_less.

 

四、總結

  • 指針之間的比較,要麼指針的靜態類型相同,要麼指針的靜態類型不同但他們的類型同屬於同一繼承層次且其中一個指針的靜態類型為所指對象的類型。
  • 指針的靜態類型相同時,比較的是地址的值的大小。
  • 指針的靜態類型不同,但是他們的類型屬於同一繼承層次,並且其中一個指針的靜態類型為所指對象的類型時,比較的是兩指針是否指向同一對象。若是指向同一對象,則兩指針“相等”;若不是指向同一對象,則比較指針的地址值的大小。
  • 智能指針shared_ptr/weak_ptr的onwer_before成員函數描述的是:當比較的2個智能指針的類型屬於同一繼承層次時表現為“相等”的含義;當2個智能指針的類型不屬於同一繼承層次時,比較的是所管理指針的地址值的大小。

(完)

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved