【问题标题】:Why are the outputs of printf and std::cout different?为什么 printf 和 std::cout 的输出不同?
【发布时间】:2019-03-14 11:19:29
【问题描述】:

我尝试了以下 C++ 代码。但是,printfstd::cout 的输出是不同的。为什么?

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
}

【问题讨论】:

  • main.cpp:22:27: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int Foo::*’ [-Wformat=]
  • @Angew -- 我正在回复一条现已被删除的评论。它说Foo::c没有被初始化。

标签: c++


【解决方案1】:

printf("%d\n", &amp;Foo::c):这是未定义的行为,因为&amp;Foo::c 不是整数,而是指向成员的指针(但实际上,编译器通常将指向数据成员的指针存储为偏移量,而8Foo::c的偏移量,8被打印出来了)。

std::cout &lt;&lt; &amp;Foo::c:这将打印值&amp;Foo::c。由于 iostream 没有指向成员打印机的指针,它选择最接近的一个:将其转换为 bool,并将其打印为整数。由于&amp;Foo::c 转换为booltrue,所以打印1

【讨论】:

  • 你知道成员指针必须考虑虚拟继承的可能性吗?
  • @Deduplicator:当然,仅适用于使用虚拟继承的类。没有与void* 等效的成员指针,每个类都有自己独特的成员指针类型。甚至sizeof(int Foo::*) 也可能依赖于Foo
  • @MSalters 没有。您可以拥有和使用int Foo::* 非常好,而Foo 仍然不完整,并且没有很快完成的危险。诚然,MS 特别喜欢用任何类型的成员指针快速和松散地玩。
  • @all - 我一直保持冷静,但我们可以停止“会员指针”业务吗?术语是“指向成员的指针”。当涉及到技术术语时,这很重要。正如“巧克力牛奶”与“牛奶巧克力”不同。
  • @geza - 是的,有人在那儿丢球。我猜is_pointer_to_member 不够吸引人。
【解决方案2】:

输出不同,因为您的 printf 的行为未定义。

指向成员的指针(如从&amp;Foo::c 生成的指针)不是整数。 printf 函数需要一个整数,因为您也使用 %d 说明符告诉它。

您可以通过向bool 添加演员来修改它,如下所示:

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

指向成员的指针可以转换为布尔值(您可以通过强制转换来实现),然后 bool 由于是可变参数函数的完整可变参数,因此会整体提升为 int

说到到bool 的转换,正是通过尝试调用std::ostreamoperator&lt;&lt; 隐式应用的转换。由于不存在支持指向成员的指针的运算符的重载,因此重载决策会选择另一个在将&amp;Foo::c 隐式转换为布尔值后可调用的运算符。

【讨论】:

    【解决方案3】:

    除了关于为什么编译器以这种方式解释您的代码的更字面答案之外:您似乎有 an XY problem 您正在尝试将指向成员的指针格式化为整数,这强烈表明您的意思做一些不同的事情。

    如果您想要的是存储在.c 中的int 值,则需要创建一个实例Foo some_foo; 并获取some_foo.c,否则您需要将Foo::c 声明为static 成员,所以整个班级都有一个明确的Foo::c。这种情况下不要取地址。

    如果您想要获取某个Foo.c 成员的地址,您应该按照上述操作,使Foo::cstatic 并引用一个特定的变量,或者声明一个实例并获取其.c 成员,然后获取地址。对象指针的正确printf() 说明符是%p,要使用&lt;iostream&gt; 打印对象指针表示,请将其转换为void*

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

    如果你想要的是Foo::c 在类Foo 中的偏移量,你需要offsetof() 宏在&lt;stddef.h&gt; 中。由于它的返回值是size_t,它与 64 位平台上的 int 大小不同,因此您可能希望显式转换结果或将 printf() 传递给 z 类型说明符:

    #include <stddef.h>
    
    /* ... */
    
      constexpr size_t offset_c = offsetof( Foo, c );
      printf( "%zu\n", offset_c );
      cout << offset_c << '\n';
    

    无论你试图做什么,如果你的编译器没有警告你类型不匹配,你应该打开更多的警告。对于在程序编译之前通过反复试验进行编码的人来说尤其如此。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多