1

I am trying to print a function pointer address two times using a printf statement shown in below code...

class B
{
public:
        int fun()
        {
        }
};

int main()
{
        int (B::*pb)()=&B::fun;
        printf("ptr:%x | %x\n",pb,pb);   //Output is ptr:8048730 | 0
}

when i am passing same variable to printf it should print the same value but after getting the result i am surprised.

Can anyone explain the reason for this or somewhere i am doing wrong.

gcc version 4.8.2 (GCC)

rajenpandit
  • 1,265
  • 1
  • 15
  • 21
  • 2
    You are using the wrong format. %x is for integers, not for pointers. If you turn some warnings on, the compiler will tell you. – gnasher729 May 22 '14 at 13:33
  • To expand gnasher729's comment, %p gets you a pointer address. – IdeaHat May 22 '14 at 13:37
  • 1
    Though that doesn't always work for function pointers, possible duplicate of http://stackoverflow.com/questions/2741683/how-to-format-a-function-pointer – IdeaHat May 22 '14 at 13:38
  • Strictly speaking, there is no format specification for pointers to functions. – ach May 22 '14 at 13:38
  • Non-static member function pointers are NOT *really* pointers! – Nawaz May 22 '14 at 13:38
  • I have tried with `%p` also but i am getting output as `ptr:0x80485e6 | (nil)` – rajenpandit May 22 '14 at 13:40
  • @Nawaz, moreover, even non-member pointers can be of size other than `void*` (although on most modern machines that's not the case). – ach May 22 '14 at 13:41
  • 1
    @Nawaz to expand that (cus i'm in an expanding mood) the following statement CAN be true based on the standard: `sizeof(functionptr) != sizeof(void*)`. You can't (directly) interpret a function ptr as a pointer. – IdeaHat May 22 '14 at 13:41
  • cout is giving me 1 on IDEone.com. printf() is not C++ IMHO. – Cool_Coder May 22 '14 at 13:42

4 Answers4

2

If you turn on the warnings (pass the -Wall flag to the compiler), the compiler will tell you what you are doing wrong:

warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int (B::*)()’ [-Wformat]
warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 3 has type ‘int (B::*)()’ [-Wformat]

In general, I can only encourage you to turn on warnings.

Now a hack (undefined behavior) is the following:

std::printf("ptr:%p | %p\n",(void*)funcptr,(void*)funcptr);

This still gives the following warning:

warning: converting from ‘int (B::*)()’ to ‘void*’ [-Wpmf-conversions]

but the program prints the same address twice as you wished. It is still undefined behavior, see How to format a function pointer? By following the accepted answer (code shamelessly stolen from there), one could print the address as follows:

#include <cstdio>

class B {
public:
        int fun() { return 0; }
};

int main()
{
    int (B::*funcptr)()=&B::fun;

    unsigned char *p = (unsigned char *)&funcptr;

    for (unsigned int i = 0; i < sizeof funcptr; ++i)
    {
        std::printf("%02x ", p[i]);
    }

    std::putchar('\n');

    return 0;
}

According to that answer it is the only legal way to achieve this. It doesn't give any warning.

Community
  • 1
  • 1
Ali
  • 56,466
  • 29
  • 168
  • 265
0

Pointer-to-member function holds the "relative address" of where the function is in the class layout. Make the following change to the code:

class B
{
public:
        static int fun() // Add static keyword
        {
        }
};

int main()
{
        int (*pb)()=&B::fun;  // Change B::*pb for *pb
        // You should use %p instead of %x as suggested by @Praetorian
        printf("ptr:%p | %p\n",pb,pb);   //Now ouput is the same. 
}

A static member has no "part of the class". You can make other test, just try to print the address of a non-member function.

You can find more about pointer to member functions here.

Raydel Miranda
  • 13,825
  • 3
  • 38
  • 60
  • `%x` is still the wrong format specifier for printing pointer values. Use `%p`. – Praetorian May 22 '14 at 14:11
  • @Praetorian I'm agree with you. But despite of the wrong format, the output is the same. This is just ilustrative. Shows why the original code have differents outputs for the "same pointer address". – Raydel Miranda May 22 '14 at 14:12
0

The reason this is happening is that:

  1. pb (as written) is a pointer-to-member-function, not an ordinary pointer to function.
  2. The size of a pointer-to-member-function is unknown; it may or may not be the same as the size of other pointers, or of unsigned integers. It could easily be more than the 'usual' 32-bits (4 bytes), as it has more info to encode.
  3. The %x format specifier is for unsigned integers only. Assuming the 'usual' architecture, that means 32-bits or 4 bytes.
  4. The vargs calling sequence used by printf simply pulls two unsigned ints off the argument list and prints them. If the arguments passed to it don't match then the printed arguments don't line up and what you see is what you get.

I could speculate on exactly what the various numbers might be, but without knowing the compiler it's hard to be sure. I tried it on VS2010 and got a perfectly sensible 6 byte integer printed twice. Go figure.

You can prove it to yourself by taking the sizeof for that pointer (which should be more than 4), or by changing it into a pointer-to-function, which is almost always the same size as other pointers.

Please note that the standard does not guarantee anything about the size of pointers to functions as compared to other pointers. The precise meaning or behaviour of %p and whether it works with function pointers is not guaranteed either.

david.pfx
  • 10,520
  • 3
  • 30
  • 63
0

as we all know, a function ptr is equivalent to a 32bit unsigned number on a 32bit machine, so, if you want to see the value of a func ptr, you can use reinterpret_cast to convert it to a unsigned int

unsigned int a = reinterpret_cast<unsigned int>(fptr);
cout << hex << a << endl;
Gavin
  • 135
  • 1
  • 4