为什么通过指针静态访问类数据成员返回1?

时间:2022-12-14 19:57:42

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)?

输出是什么意思?为什么是1?是否有任何理由通过指针访问类成员(没有创建对象)?

EDIT - Using:

编辑 - 使用:

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

prints: 4 , 8 and C.

打印:4,8和C.

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

现在我觉得更有意义..(字节)但是,还有什么用呢?

2 个解决方案

#1


31  

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

没有为T =&C :: m定义的运算符<<(std :: ostream&,T)。通常你会收到一个错误。

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).

但相反,有一个用于T = bool和从成员指针到bool的隐式转换。所以你看到的输出只是那些指针不为空(转换为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.

请注意,在代码printf(“%p”,&A :: X)中,您有未定义的行为。

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.)

%p说明符的值必须是void *,并且不存在从成员指针到void *的转换。相反,你有什么别名(类型双关语)到void *,这是未定义的行为。 (想象一下,sizeof(&A :: x)是4而sizeof(void *)是64;没有人说这不是这种情况。)

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.

你必须接受这样的想法,即并非所有指针都可以被视为整数偏移。我们甚至可以打印一个指针是实现定义的:它可以打印“apple”为null,“pear”为一个值,“milk”为另一个(如果(哑)实现想要)。我之前已经触及了价值观和表现形式之间的这种差异。

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 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

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

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

您对成员指针的看法无法保证。

#2


2  

&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.

&A :: x创建指向类A成员的指针。在某种程度上,指向成员的指针表示类中成员的“位置”。这些数字究竟意味着什么取决于具体实施。我猜想在你的特定实现中,它意味着x在类中的索引1上(虚函数表指针在索引0上)。编辑实际上,GManNickG的答案显示我对这个假设是错误的,它只是将指针打印到隐式转换为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
}

#1


31  

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

没有为T =&C :: m定义的运算符<<(std :: ostream&,T)。通常你会收到一个错误。

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).

但相反,有一个用于T = bool和从成员指针到bool的隐式转换。所以你看到的输出只是那些指针不为空(转换为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.

请注意,在代码printf(“%p”,&A :: X)中,您有未定义的行为。

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.)

%p说明符的值必须是void *,并且不存在从成员指针到void *的转换。相反,你有什么别名(类型双关语)到void *,这是未定义的行为。 (想象一下,sizeof(&A :: x)是4而sizeof(void *)是64;没有人说这不是这种情况。)

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.

你必须接受这样的想法,即并非所有指针都可以被视为整数偏移。我们甚至可以打印一个指针是实现定义的:它可以打印“apple”为null,“pear”为一个值,“milk”为另一个(如果(哑)实现想要)。我之前已经触及了价值观和表现形式之间的这种差异。

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 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

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

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

您对成员指针的看法无法保证。

#2


2  

&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.

&A :: x创建指向类A成员的指针。在某种程度上,指向成员的指针表示类中成员的“位置”。这些数字究竟意味着什么取决于具体实施。我猜想在你的特定实现中,它意味着x在类中的索引1上(虚函数表指针在索引0上)。编辑实际上,GManNickG的答案显示我对这个假设是错误的,它只是将指针打印到隐式转换为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
}