为什么printf和std :: cout的输出不同?

uu *_* dd 43 c++

我尝试了以下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
}
Run Code Online (Sandbox Code Playgroud)

gez*_*eza 78

printf("%d\n", &Foo::c):这是未定义的行为,因为&Foo::c不是整数,而是一个指针构件(但,实际上,作为偏移编译器存储指向数据成员,通常,并且作为8是的偏移Foo::c8打印)。

std::cout << &Foo::c:打印值&Foo::c。由于iostream没有指向成员打印机的指针,因此它选择最接近的指针:将其转换为bool,并将其打印为整数。由于&Foo::c转换为booltrue1被打印出来。

  • @all-我一直保持和平直到现在,但是我们可以停止“成员指针”业务吗?该术语是“指向成员的指针”。当涉及到技术术语时,这一点很重要。正如“巧克力牛奶”和“牛奶巧克力”不同。&lt;/ pedantry&gt; (4认同)
  • @StoryTeller:但是我们还有`std :: is_member_pointer` :) (2认同)

Sto*_*ica 21

输出是不同的,因为您的行为printf是不确定的。

指向成员的指针(如从产生的指针&Foo::c)不是整数。该printf函数需要一个整数,因为您也已在%d说明符中告诉了它。

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

printf("%d\n", (bool)&Foo::c)
Run Code Online (Sandbox Code Playgroud)

指向成员的指针可以转换为布尔型(您可以使用强制转换),bool然后int由于是可变参数的完整可变参数而经历了整数提升。

说到到的转换bool,正是通过尝试调用std::ostream的隐式应用的转换operator<<。由于不存在支持成员指针的运算符的重载,因此重载解析会选择一个隐式转换&Foo::c为布尔值后可以调用的运算符。


Dav*_*lor 5

除了关于编译器为何以这种方式解释代码的更直截了当的答案之外:您似乎还有一个XY问题 您正在尝试将一个指向成员的指针格式化为整数,这强烈表明您打算做某事不同。

如果您想要的是一个int存储在中的值.c,则您需要创建一个实例Foo some_foo;并接受some_foo.c,或者您需要声明Foo::c一个static成员,因此Foo::c整个类中没有一个明确的成员。在这种情况下,请勿使用该地址。

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

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

如果你想被偏移什么Foo::c类中Foo,你想要的offsetof()<stddef.h>。由于它的返回值是size_t,这不是大小相同的int在64位平台上,你会明确地想要么把结果或传递printf()z类型说明:

#include <stddef.h>

/* ... */

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

无论您尝试做什么,如果编译器没有警告您类型不匹配,您都应该打开更多警告。对于经过反复试验直到程序编译的人来说尤其如此。