我尝试了以下C ++代码。但是,printf
和std::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
}
答案 0 :(得分:77)
printf("%d\n", &Foo::c)
:这是未定义的行为,因为&Foo::c
不是整数,而是指向成员的指针(但实际上,编译器通常将指向数据成员的指针存储为偏移量,并且因为8
是Foo::c
的偏移量,所以会打印8
。
std::cout << &Foo::c
:这将打印值&Foo::c
。由于iostream没有指向成员打印机的指针,因此它选择最接近的成员:将其转换为bool
并将其打印为整数。将&Foo::c
转换为bool
时,将打印true
。
答案 1 :(得分:21)
输出不同,因为printf
的行为未定义。
指向成员的指针(如从&Foo::c
产生的指针)不是整数。 printf
函数需要一个整数,因为您也使用%d
说明符告诉了它。
您可以通过向bool
添加类型转换来修改它,如下所示:
printf("%d\n", (bool)&Foo::c)
指向成员的指针可能会转换为布尔值(您可以使用强制转换),然后bool
会由于对a的一个完整的可变参数而经历一个int
的整数提升。可变函数。
说到bool
的转换,正是通过尝试调用std::ostream
的{{1}}隐式应用的转换。由于不存在支持成员指针的运算符的重载,因此重载解析会选择将operator<<
隐式转换为布尔值后可以调用的另一个。
答案 2 :(得分:5)
除了关于编译器为何以这种方式解释代码的更直截了当的答案之外:您似乎还an XY problem试图将成员指针的格式设置为整数,这强烈建议你打算做些不同的事情。
如果您想要的是存储在int
中的.c
值,则需要创建一个实例Foo some_foo;
并接受some_foo.c
,否则需要声明{ {1}}是一个Foo::c
成员,因此整个课程中只有一个明确的static
。在这种情况下,请勿使用地址。
如果您想要获取某个Foo::c
的{{1}}成员的地址,则应按照上述步骤进行操作,以使.c
为Foo
并引用一个特定变量,或者声明一个实例并使用其Foo::c
成员,然后使用地址。对象指针的正确static
指定符是.c
,并且要打印带有printf()
的对象指针表示形式,请将其转换为%p
:
<iostream>
如果想要的是类void*
中printf( "%p\n", &some_foo.c );
std::cout << static_cast<void*>{&some_foo.c} << '\n';
的偏移量,则需要Foo::c
中的Foo
宏。由于它的返回值是offsetof()
,在64位平台上的返回值与<stddef.h>
的大小不同,因此您可能想显式地强制转换结果或将size_t
传递给{{1} }类型说明符:
int
无论您想做什么,如果编译器没有警告您类型不匹配,您都应该打开更多警告。对于经过反复试验直到程序编译的人尤其如此。