Comparison of pointers and comparison of pointers
I. Preface
Some people say that pointers are the soul of the C language, and some people say that if you don't learn pointers well, it will not be the C language.
In modern C ++, we recommend that you avoid using native raw pointers instead of smart pointer and reference. However, for C/C ++, pointers are always inapplicable. The reason is that C/C ++ supports the language for the underlying operations, and the memory can be manipulated for the underlying operations. In this case, pointers are required. Why? In my opinion, pointers are actually an abstraction of the behavior of operating memory in machine language/ASM through virtual addresses.
For example
movl %eax, (%edx)
Write the value in the register eax to the memory where the memory address is the value of the Register edx. If you think of edx as a pointer, it is equivalent
*p_edx = value_eax
2. pointer comparison
There are many tips and uses for pointers. I will talk about some easy-to-step pitfalls of pointer comparison operations in C ++ later.
Let's take a look at this code.
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 BaseB14 {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 else29 {30 printf("pb != pd\n");31 }32 }
The output result is:
Pb = 0028FEE0
Pd = 0028FED8
Pb = pd
We can see that the pointer pb AND pd values are not the same, but the compiler thinks they are equal. Why?
1. when the static types of the two pointers and the types of the objects referred to belong to the same inheritance hierarchy and one of the pointer types is the static types of the objects referred to, the pointer is compared, in fact, it compares whether two pointers point to the same object.
If the two pointers point to the same object, the compiler determines that the two pointers are equal. The compiler adds an appropriate offset value during the comparison. For example, in the above case, the compiler makes the following changes during the comparison:
if((pb - sizeof(int) == pd)
Offset is determined by the memory model of the C ++ object. The specific scope is no longer discussed in this article.
If two pointers direct to different objects, they are determined to be unequal and compared to the value of the address saved by the pointer.
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 else18 {19 printf("pd > pb\n");20 }21 }
The result is as follows:
0028FED8
0028FED0
Pd> pb
2. when the static types of the two pointers do not belong to the same inheritance hierarchy but both Pointer Points to the same object, this comparison is illegal and the compiler reports a compilation error.
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 else13 {14 printf("pb != pd\n");15 }16 }
The compiler reports the following error:
error: comparison between distinct pointer types 'int*' and 'Derived*' lacks a cast [-fpermissive]
if (pb == pd)
3. when both the static types of the two pointers and the object types belong to the same inheritance hierarchy, but the static types of the two pointers are not the types of the objects referred to, this comparison is illegal, the compiler reports a compilation error:
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 else13 {14 printf("pb != pa\n");15 }16 }
The compiler reports the following error:
error: comparison between distinct pointer types 'BaseB*' and 'BaseA*' lacks a cast
if (pb == pa)
Other behaviors, such as two pointers of different types but belong to the same inheritance level, and then force type conversion allows both of them to point to an object not in this inheritance level, such behavior is undefined. Maybe the compiler will not report a compilation error, but the result is undefined and may be any result.
Some people may say, when do pointers compare the values of their saved addresses?
The answer is when the static types of the two pointers are the same:
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 else16 {17 printf("p1 > p2\n");18 }19 }
Result: p1> p2
3. owner_before of shared_ptr
Boost: shared_ptr/std: shared_ptr has an owner_before member function. The prototype is
template <class U> bool owner_before (const shared_ptr<U>& x) const;template <class U> bool owner_before (const weak_ptr<U>& x) const;
When the shared_ptr and x types belong to the same inheritance layer, both of them are determined to be "equal" regardless of whether their types are the same ". When their types do not belong to the same inheritance level, they compare the size of the address values of the pointers they manage.
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 011 printf("%d %d\n", p0.owner_before(p1), p1.owner_before(p0)); // 0 012 }
Why does shared_ptr provide such a member function?
Because a smart pointer may point to another smart pointer pointing to a part of the object, but it must ensure that when the two smart pointers are destroyed, only the whole structure of the object to be pointed is performed, instead of analyzing the two pointers separately.
In this case, the pointer can be divided into two types, one is stored pointer, which is the object represented by the pointer type (may be part of a large object ); the other is that the owned pointer Points to the actual complete object in the memory (this object may be pointed by many smart pointers to different parts of it, but it will only be parsed once ). Owner-based order refers to the latter case. If there is only one object in the memory and many shared pointers point to different parts, the addresses of these pointers must be different, that is, operator <() can compare them, and they are not the owner of the object. They will not parse the object when they are destroyed. But they all point to an object, which is equal in the owner-based order sense.
Cpluscplus is explained as follows:
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 is explained as follows:
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.
Iv. Summary
- For comparison between pointers, either the static type of the pointer is the same, or the static type of the pointer is different, but their types belong to the same inheritance level, and the static type of one pointer is the type of the indicated object.
- When the static type of the pointer is the same, the size of the address value is compared.
- The static types of pointers are different, but their types belong to the same inheritance level. When the static type of a pointer is the type of the object referred to, the comparison compares whether the two pointers direct to the same object. If it points to the same object, the two pointers are "equal". If it does not point to the same object, the address value of the pointer is compared.
- The onwer_before member function of the smart pointer shared_ptr/weak_ptr describes the meaning of "equal" when the two smart pointer types belong to the same inheritance layer; when the types of the two smart pointers do not belong to the same inheritance level, the size of the address value of the managed pointer is compared.
(End)