Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does statically accessing class data members via pointers returns 1?

Tags:

Given the following code:

#include <iostream>

using std::cout;

class A {
    public:
        virtual ~A() {}
        int x,y,z;
};

int main (void)
{
    std::cout<<&A::x;
    std::cout<<&A::y;
    std::cout<<&A::z;
}

The output is:

111

What is the meaning of the output? Why is it 1? Are there any good reason to access class members via pointers (without created object)?

EDIT - Using:

printf("%p",&A::x);
printf("%p",&A::y);
printf("%p",&A::z);

prints: 4 , 8 and C.

It makes more sense now I guess.. (bytes) But still, is there any use for that?

like image 604
Rouki Avatar asked Apr 16 '13 14:04

Rouki


2 Answers

There is no operator<<(std::ostream&, T) defined for T = &C::m. Normally you'd get an error.

But instead, there is one for T = bool and an implicit conversion from a member-pointer to bool. So the output you're seeing is just the result of those pointers not being null (being converted to true).

Try this, for example:

#include <iomanip>
#include <iostream>

struct A
{
    int x, y, z;
};

int main()
{
    std::cout << std::boolalpha; // print boolean values as text

    std::cout << &A::x << std::endl;
    std::cout << &A::y << std::endl;
    std::cout << &A::z << std::endl;
}

Output:

true
true
true


Note that in the code printf("%p", &A::X), you have undefined behavior.

The value for the %p specifier must be a void*, and there is no conversion from a member-pointer to a void*. Instead, what you have aliases (type-puns) to void*, which is undefined behavior. (Imagine that sizeof(&A::x) was 4 while sizeof(void*)was 64; nothing says this cannot be the case.)

You just have to come to terms with the idea that not all pointers can be viewed as integer offsets. That we can even print a pointer is implementation-defined: it could print "apple" for null, "pear" for one value, and "milk" for another if a (dumb) implementation wanted to. I've touched on this difference between values and their representations before.

And in this case, there is no output for the value at all. And that's okay, not all values have meaningful printed outputs. The most you can do is print out the individual bits:

#include <climits>
#include <iostream>
#include <type_traits>

template <typename T>
auto print_bits(const T& value)
    -> typename std::enable_if<std::is_standard_layout<T>::value>::type
{
    // it's okay to alias a standard-layout type as a sequence of bytes:
    const auto valueAsBytes = reinterpret_cast<const unsigned char*>(&value);

    for (std::size_t byte = 0; byte < sizeof(T); ++byte)
    {
        // print in reverse order
        const std::size_t byteIndex = sizeof(T) - byte - 1;

        const unsigned char byteValue = valueAsBytes[byteIndex];

        for (std::size_t bit = 0; bit < CHAR_BIT; ++bit)
        {
            // print in reverse order
            const std::size_t bitIndex = CHAR_BIT - bit - 1;

            const bool bitValue = (byteValue & (1U << bitIndex)) != 0;

            std::cout << (bitValue ? 1 : 0);
        }

        std::cout << ' ';
    }

    std::cout << std::endl;
}

(I print the bytes and bits in reverse order because on my architectures this puts the least-significant bit on the right. I prefer to view binary values this way.)

This gives:

struct A
{
    int x, y, z;
};

int main()
{
    // example:
    for (unsigned i = 0; i < 64; ++i)
        print_bits(i);

    std::cout << std::endl;

    // member-pointers:
    print_bits(&A::x);
    print_bits(&A::y);
    print_bits(&A::z);
}

Output:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
[...]
00000000 00000000 00000000 00111101
00000000 00000000 00000000 00111110
00000000 00000000 00000000 00111111

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000100
00000000 00000000 00000000 00001000

There are no guarantees on what you see for the member-pointers.

like image 51
GManNickG Avatar answered Sep 29 '22 14:09

GManNickG


&A::x creates a pointer to member of class A. A pointer to member is, in a way, a representation of the "position" of the member within the class. What these numbers mean exactly depends on the particular implementation. I would guess that in your particular implemtation, it means x is on index 1 in the class (the virtual function table pointer being on index 0). EDIT Actually, GManNickG's answer shows I was wrong with this assumption and it's just printing the pointer to member implicitly converted to bool.

Note that a pointer to member is not a pointer in the classic sense. You can't dereference a pointer to member directly. You always need a pointer/reference to an object to go along with it. Example (using your class):

int main()
{
  int A::*pm;  // pm is a pointer to member of A of type int
  A a;
  pm = &A::x;
  std::cout << a.*pm; //will output a.x
  pm = &A::y;
  std::cout << a.*pm; //will output a.y
  A *pa = &a;
  std::cout << pa->*pm;  //will output pa->y
}
like image 27
Angew is no longer proud of SO Avatar answered Sep 29 '22 15:09

Angew is no longer proud of SO