By uu dd


2019-03-14 11:19:29 8 Comments

I tried the following code of C++. However, the outputs of printf and std::cout are different. Why?

struct Foo
{
    int a;
    int b;
    int c;
};

int main()
{
    printf("%d\n", &Foo::c);  // The output is 8
    std::cout << &Foo::c << "\n"; // The output is 1
}

3 comments

@Davislor 2019-03-14 16:55:11

In addition to the more literal answer about why the compiler interpreted your code the way it did: you seem to have an XY problem You’re trying to format a pointer-to-member as an integer, which strongly suggests you meant to do something different.

If what you wanted was an int value stored in .c, you either need to create an instance Foo some_foo; and take some_foo.c, or else you need to declare Foo::c a static member, so there’s one unambiguous Foo::c across the entire class. Do not take the address in this case.

If what you wanted was to take an address of the .c member of some Foo, you should do as above so that Foo::c is static and refers to one specific variable, or else declare an instance and take its .c member, then take the address. The correct printf() specifier for an object pointer is %p, and to print an object pointer representation with <iostream>, convert it to void*:

printf( "%p\n", &some_foo.c );
std::cout << static_cast<void*>{&some_foo.c} << '\n';

If what you want is the offset of Foo::c within class Foo, you want the offsetof() macro in <stddef.h>. Since its return value is size_t, which is not the same size as int on 64-bit platforms, you would want to either cast the result explicitly or pass printf() the z type specifier:

#include <stddef.h>

/* ... */

  constexpr size_t offset_c = offsetof( Foo, c );
  printf( "%zu\n", offset_c );
  cout << offset_c << '\n';

Whatever you were trying to do, if your compiler didn’t warn you about the type mismatch, you ought to turn on more warnings. This is especially true for someone coding by trial and error until the program compiles.

@geza 2019-03-14 11:31:06

printf("%d\n", &Foo::c): this is undefined behavior, as &Foo::c is not an integer, but a pointer to member (but, actually, it is usual that the compiler stores pointer to data member as offset, and as 8 is the offset of Foo::c, 8 is printed).

std::cout << &Foo::c: this prints the value &Foo::c. As iostream doesn't have a pointer to member printer, it chooses the closest one: it converts it to bool, and prints it as integer. As &Foo::c converted to bool is true, 1 is printed.

@Deduplicator 2019-03-14 12:50:29

You know member-pointers have to take into account the possibility of virtual inheritance?

@MSalters 2019-03-14 12:57:52

@Deduplicator: Only for classes that use virtual inheritance, of course. There's no member pointer equivalent of void*, each class has its own unique types of member pointers. Even sizeof(int Foo::*) may depend on Foo.

@Deduplicator 2019-03-14 13:00:11

@MSalters No. You can have and use a int Foo::* perfectly fine while Foo is still incomplete and in no danger of being completed anytime soon. Admittedly, MS especially likes to play fast and loose with member-pointers of any kind.

@StoryTeller 2019-03-14 13:07:46

@all - I held my peace until now, but can we stop with the "member-pointer" business? The term is "pointer-to-member". And when it comes to technical terms, it matters. Just as "chocolate milk" isn't the same as "milk chocolate". </pedantry>

@geza 2019-03-14 13:26:43

@StoryTeller: and yet we have std::is_member_pointer :)

@StoryTeller 2019-03-14 13:28:24

@geza - Yes, someone dropped the ball there. I guess is_pointer_to_member wasn't catchy enough.

@ilkkachu 2019-03-14 18:57:25

...but why is bool the "closest one"?

@geza 2019-03-14 19:08:01

@ilkkachu: a pointer-to-member only has a few implicit conversion possibilities: to bool, and to a "similar" pointer-to-member type. Among these, iostream only supports bool.

@ilkkachu 2019-03-14 19:11:49

@geza, on second thought, that makes sense. It was just my internal low-level programmer thinking about automatically converting to an int.

@StoryTeller 2019-03-14 11:31:36

The output is different because the behavior of your printf is undefined.

A pointer to member (like the one produced from &Foo::c) is not an integer. The printf function expects an integer, since you told it too with the %d specifier.

You can amend it by adding a cast to bool, like this:

printf("%d\n", (bool)&Foo::c)

A pointer to member may be converted to a bool (which you do with the cast), and the bool then undergoes integral promotion to an int on account of being an integral variadic argument to a variadic function.

Speaking of the conversion to bool, it's exactly the conversion that is applied implicitly by attempting to call std::ostream's operator<<. Since there isn't an overload of the operator that supports pointers to members, overload resolution selects another that is callable after implicitly converting &Foo::c to a boolean.

Related Questions

Sponsored Content

15 Answered Questions

[SOLVED] Why can templates only be implemented in the header file?

22 Answered Questions

35 Answered Questions

2 Answered Questions

[SOLVED] C++ difference in behavior between cout and printf

  • 2019-02-11 20:17:18
  • S. Swami
  • 80 View
  • 0 Score
  • 2 Answer
  • Tags:   c++

31 Answered Questions

10 Answered Questions

35 Answered Questions

[SOLVED] Why is "using namespace std" considered bad practice?

2 Answered Questions

[SOLVED] file descriptors, difference between printf and std::cout

  • 2014-03-31 15:30:19
  • lcjury
  • 140 View
  • 0 Score
  • 2 Answer
  • Tags:   c++ c

9 Answered Questions

[SOLVED] Why does this output of the same expression from printf differ from cout?

Sponsored Content