【问题标题】:Printing a pointer-to-member-field打印指向成员字段的指针
【发布时间】:2011-12-11 05:40:31
【问题描述】:

我正在调试一些涉及指向成员字段的指针的代码,我决定将它们打印出来以查看它们的值。我有一个函数返回一个指向成员的指针:

#include <stdio.h>
struct test {int x, y, z;};
typedef int test::*ptr_to_member;
ptr_to_member select(int what)
{
    switch (what) {
    case 0: return &test::x;
    case 1: return &test::y;
    case 2: return &test::z;
    default: return NULL;
    }
}

我尝试使用cout

#include <iostream>
int main()
{
    std::cout << select(0) << " and " << select(3) << '\n';
}

我收到了1 and 0。我以为数字表示struct 内的字段位置(即1y0x),但不,打印的值实际上是1 非空指针和0 为空指针。我想这是一种符合标准的行为(即使它没有帮助)——我说的对吗?此外,兼容的 c++ 实现是否可以始终为指向成员的指针打印 0?甚至是一个空字符串?

最后,我怎样才能以有意义的方式打印指向成员的指针?我想出了两个丑陋的方法:

printf("%d and %d\n", select(0), select(3)); // not 64-bit-compatible, i guess?

ptr_to_member temp1 = select(0); // have to declare temporary variables
ptr_to_member temp2 = select(3);
std::cout << *(int*)&temp1 << " and " << *(int*)&temp2 << '\n'; // UGLY!

还有更好的方法吗?

【问题讨论】:

  • 发布您的代码,这甚至无法编译。

标签: c++ printf standards-compliance pointer-to-member


【解决方案1】:

指向成员的指针并不像您想象的那么简单。它们的大小因编译器和类而异,取决于类是否具有虚拟方法以及是否具有多重继承。假设它们是 int 大小的不是正确的方法。你可以做的是用十六进制打印它们:

void dumpByte(char i_byte)
{
    std::cout << std::hex << static_cast<int>((i_byte & 0xf0) >> 4);
    std::cout << std::hex << static_cast<int>(i_byte & 0x0f));
} // ()

template <typename T>
void dumpStuff(T* i_pStuff)
{
    const char* pStuff = reinterpret_cast<const char*>(i_pStuff);
    size_t size = sizeof(T);
    while (size)
    {
        dumpByte(*pStuff);
        ++pStuff;
        --size;
    } // while
} // ()

但是,我不确定这些信息对您有多大用处,因为您不知道指针的结构是什么以及每个字节(或几个字节)的含义。

【讨论】:

    【解决方案2】:

    成员指针不是普通的指针。您期望的 &lt;&lt; 的重载实际上并不存在。

    如果你不介意某种类型的双关语,你可以修改一些东西来打印实际值:

    int main()
    {
      ptr_to_member a = select(0), b = select(1);
      std::cout << *reinterpret_cast<uint32_t*>(&a) << " and "
                << *reinterpret_cast<uint32_t*>(&b) << " and "
                << sizeof(ptr_to_member) << '\n';
    }
    

    【讨论】:

    • 指向成员的指针可能比 uint32_t 大,特别是来自虚函数和/或多重继承类时。
    • 是的,因此检查尺寸......这非常有技巧。我只是想要一种快速打印一些有意义的结果的方法。
    • @Kerrek "Overloads ... 实际上并不存在" - 你想说我指向成员的指针在打印之前转换为 bool 吗?
    • @anatolyg:我实际上并不完全确定发生了什么,但我也在想……你可以设置alpha 格式化标志,看看它是否确实是一个布尔值。跨度>
    • @Kerrek 是的,它输出 truefalse
    【解决方案3】:

    您可以按如下方式显示这些指向成员的原始值:

    #include <iostream>
    struct test {int x, y, z;};
    typedef int test::*ptr_to_member;
    ptr_to_member select(int what)
    {
        switch (what) {
        case 0: return &test::x;
        case 1: return &test::y;
        case 2: return &test::z;
        default: return NULL;
        }
    }
    
    int main()
      {
      ptr_to_member x = select(0) ;
      ptr_to_member y = select(1) ;
      ptr_to_member z = select(2) ;
      std::cout << *(void**)&x << ", " << *(void**)&y << ", " << *(void**)&z << std::endl ;
      }
    

    您会收到有关违反严格抗锯齿规则的警告(请参阅 this link),但结果可能如您所愿:

    0, 0x4, 0x8
    

    尽管如此,编译器可以随意实现指向成员的功能,因此您不能依赖这些值是有意义的。

    【讨论】:

      【解决方案4】:

      我认为你应该使用printf 来解决这个问题

       #include <stdio.h>
      
      struct test{int x,y,z;}
      int main(int argc, char* argv[])
      {
        printf("&test::x=%p\n", &test::x);
        printf("&test::y=%p\n", &test::y);
        printf("&test::z=%p\n", &test::z);
        return 0;
      }
      

      【讨论】:

      • 这会导致未定义的行为,%p 对应的参数必须具有 void * 类型
      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 2018-03-24
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 2023-03-12
      • 2021-12-03
      • 1970-01-01
      相关资源
      最近更新 更多