0

In my cpp implementation I have:

static MyClass *instance;

floating freely outside all containing scopes (curly {}). I've tried initializing it to nullptr too.

void MyClass::myMethod() {
 instance = this;
 LOG("Hello, %d, %d", wList, instance->wList);

The above log displays an arbitrary location for member object pointer wList but instance should be pointing to the same data that this does, and hence the same wList, but it doesn't. instance->wList is still 0. What's happening here?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
John
  • 6,433
  • 7
  • 47
  • 82
  • Who knows? Present a testcase so we can tell you. – Lightness Races in Orbit Jul 27 '14 at 01:56
  • 7
    You can't use `%d` to print a pointer reliably - use `%p`. – Mark Ransom Jul 27 '14 at 01:58
  • I've rolled this back to the original question @LightnessRacesinOrbit those extra tags proved to be spot on for locating the problem. Obviously the solution isn't ideal but as a quick n dirty hack I don't think anyone had predicted what the real cause of error was.. Saves passing member function pointers http://stackoverflow.com/questions/12662891/c-passing-member-function-as-argument – John Jul 27 '14 at 17:47
  • I take it back! I found I had redeclared `wList` at the start of the method! Must stop work when my eyes close :) – John Jul 27 '14 at 18:05
  • 1
    No, those tags are utterly useless and shall not be used, thanks. – Lightness Races in Orbit Jul 27 '14 at 18:14

1 Answers1

3

As Mark Ransom noted in the comments, you can't use the %d format specifier to print a pointer, you must use %p in order for your code to be portable. It's technically Undefined Behavior to use anything besides %p for pointers, but in all likelihood you'll see that it will seem to work ok on 32-bit systems.

However, on 64-bit systems, which I'd wager you're using, it blows up. Pointers are 8 bytes, but printf only tries to read 4 bytes off of the stack when it sees %d. For the second %d specifier, it reads the next 4 bytes, which are the other 4 bytes of the pointer—if you're seeing 0 for that, it probably means that your pointer was allocated within the first 4 GB of memory on a little-endian system (i.e. its value was something like 0x00000000'xxxxxxxx). The 8 bytes on the stack from the second pointer passed to printf never get read.

The %p specifier portably prints a pointer on all platforms, regardless of whether they're 32 bits, 64 bits, or some other size. One unfortunate consequence of this is that the exact output format is implementation-defined, meaning it can and does change between systems. Some systems might use a leading 0x or 0X, while others might have no prefix at all and just print the raw hex value. Some might pad to the left with 0's, and others might not.

If you want to control the exact output, then you can use a format of your choice (e.g. %08x or %016llx) as long as you cast the pointer appropriately for that specifier. For example, here's how you would do that on 32- and 64-bit systems:

printf("0x%08x\n", (unsigned int)(uintptr_t)myPointer);           // 32-bit
printf("0x%016llx\n", (unsigned long long)(uintptr_t)myPointer);  // 64-bit

The reason for casting twice is to avoid spurious compiler warnings, since some compilers will complain if you cast from a pointer type to an integer type which isn't guaranteed to be large enough to hold a pointer.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • thanks for the comprehensive answer with reinforcement from Mark Ransom. I will be able to try again tonight but I had completely overlooked 64bit pointers as was running through the Android emulator. – John Jul 27 '14 at 11:09
  • Yep well argued, my error was in fact that I redeclared `wList` at line one of the calling function. Your at least 100x as right as me so well done. – John Jul 27 '14 at 18:06