When we talk about pointers, it is often assumed that it is something that can be represented by a void *
pointer, which is 8 bytes in size under the x86_64 platform. For example, here is an excerpt from a Wikipedia article on x86_64:
Pushes and pops on the stack is always in 8-byte strides, and pointers is 8 bytes wide.
From the CPU point of view, the pointer is nothing more than the address of the memory, all the memory address in the X86_64 platform is represented by 64 bits, so assume that it is 8 bytes is correct. It is not difficult to verify what we say by simply outputting the lengths of different types of pointers.
#Include<iostream>int main () { Std::cout << "sizeof (int*) = = " << sizeof (int*) << " \n "" sizeof (double*) = = "<< sizeof (double*) << < Span class= "Pl-pds" > "\n" " sizeof (void (*) ()) = = "<< sizeof (void (*) ()) < < Std::endl;}
Compile and run the above program, from the results can be seen all the length of the pointer is 8 bytes:
$ uname-ix86_64$ g++-wall/example.cc$./a.outsizeof (int*) = = 8sizeof (double*) = = 8sizeof (void ( *) ()) = = 8
In C + +, however, there is a special case-a pointer to a member function. It's interesting, the member function pointer is twice times the length of any other pointer. This can be verified by the following simple procedure, with the result of the output being "16":
#<iostream>const {}}; sizeof (&foo::bar) << Std::endl;}
Does this mean that Wikipedia is wrong? Obviously not! From a hardware point of view, all pointers are still 8 bytes. So, what is the pointer to the member function? This is an attribute of the C + + language, where pointers to member functions are not mapped directly to hardware, and are implemented by the runtime (compiler), which can incur some additional overhead, often resulting in a loss of performance. The C + + language specification does not refer to the implementation details, nor does it explain this type of pointer. Fortunately, the Itanium C + + ABI specification shares the details of the C + + runtime implementation--for example, it explains how Virtual Table, RTTI, and exceptions are implemented, and also explains the member pointers in §2.3:
A pointer to member function is a pair as follows:
Ptr:
For a non-virtual function, this field was a simple function pointer. For a virtual function, it's 1 plus the virtual table offset (in bytes) of the function, represented as a ptrdiff_t. The value zero represents a NULL pointer, independent of the adjustment field value below.
Adj
The required adjustment to this, represented as a ptrdiff_t.
Therefore, the member pointer is 16 bytes instead of 8 bytes, because after a simple function pointer you need to save information about how to adjust the "this" pointer (which is always passed implicitly to the non-static member function). The ABI specification does not say why and when it is necessary to adjust the this pointer. It may not be obvious at first, let's look at the following examples of class inheritance:
A { const {} char pad0[32];}; Char pad2[64];}; c:a, b{};
Both A and B have a non-static member function and a data member. These two methods can be used to access data members in their classes by implicitly passing them the "this" pointer. In order to access any data member, an offset is added to the "This" pointer, which is the offset of the data member to the base address of the class object, which can be represented by ptrdiff_t. However, things will become more complex in multiple succession. We have a class C that inherits A and B, what will happen? The compiler puts A and B in memory at the same time, b under a, so the method of Class A and the class B method see the value of the this pointer is not the same. This can be verified by practice, such as:
#include <iostream>struct A { void foo() const { std::cout << "A‘s this: " << this << std::endl; } char pad0[32];};struct B { void bar() const { std::cout << "B‘s this: " << this << std::endl; } char pad2[64];};struct C : A, B{ };int main(){ C obj; obj.foo(); obj.bar();}
&&/testa' s this:0x7fff57ddfb48B' s this:0x7fff57ddfb68
As you can see, the value of the "This" pointer to B is 32 bytes larger than the method of a-the actual size of a Class A object. But what happens when we use the following function to invoke the method of class C with a pointer?
Call_by_ptr (const) { (Obj.*mem_func) ();}
In relation to what functions are called, different "this" pointer values are passed to these functions. But the call_by_ptr
function does not know whether its arguments are foo()
pointers or bar()
pointers, and the only time that the information can be known is when these methods are used. This is why the pointer to the member function needs to know how to adjust the pointer before calling this
. Now, we put all of them into a simple program that illustrates the mechanism of internal work:
#Include<iostream>structAvoidFoo ()const {Std::cout <<"A ' s This:\ t"<<This << Std::endl; }Char pad0[32];};structBvoidBar ()const {Std::cout <<"B ' s This:\ t"<<This << Std::endl; }Char pad2[64];};structC:a, b{};voidCall_by_ptr (Const C &obj,void (C::* mem_func) ()Const) {void *data[2];std::memcpy (data, &mem_func,sizeof (MEM_FUNC)); Std::cout <<"------------------------------\ n" "Object ptr:\ t" << &obj << "\ nfunction ptr:\ t"<< data[0] << "\npointer adj:\ t"<< data[1 ] << Std::endl; (Obj.*mem_func) ();} int Main () {C obj; call_by_ptr (obj, &c::foo); call_by_ptr (obj, &c::bar);}
The above program output is as follows:
------------------------------Object ptr: 0x7fff535dfb28Function ptr: 0x10c620cacPointer adj: 0A‘s this: 0x7fff535dfb28------------------------------Object ptr: 0x7fff535dfb28Function ptr: 0x10c620cfePointer adj: 0x20B‘s this: 0x7fff535dfb48
Hopefully this article will make the problem a little clearer.
Translated from: http://741mhz.com/wide-pointers/
Why is the member function pointer in C + + 16 bytes?