简体   繁体   English

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

[英]Why are the outputs of printf and std::cout different?

I tried the following code of C++. 我尝试了以下C ++代码。 However, the outputs of printf and std::cout are different. 但是, printfstd::cout的输出是不同的。 Why? 为什么?

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
}

printf("%d\\n", &Foo::c) : this is undefined behavior, as &Foo::c is not an integer, but a pointer to member (but, actually, it is usual that the compiler stores pointer to data member as offset, and as 8 is the offset of Foo::c , 8 is printed). printf("%d\\n", &Foo::c) :这是未定义的行为,因为&Foo::c不是整数,而是指向成员的指针(但实际上,编译器通常存储指向数据的指针成员的偏移量,并且8Foo::c的偏移量,则输出8 )。

std::cout << &Foo::c : this prints the value &Foo::c . std::cout << &Foo::c :这将打印值&Foo::c As iostream doesn't have a pointer to member printer, it chooses the closest one: it converts it to bool , and prints it as integer. 由于iostream没有指向成员打印机的指针,因此它选择最接近的成员:将其转换为bool ,并将其打印为整数。 As &Foo::c converted to bool is true , 1 is printed. 因为&Foo::c转换为booltrue ,所以将打印1

The output is different because the behavior of your printf is undefined. 输出是不同的,因为您的printf的行为是不确定的。

A pointer to member (like the one produced from &Foo::c ) is not an integer. 指向成员的指针(如&Foo::c产生的指针)不是整数。 The printf function expects an integer, since you told it too with the %d specifier. printf函数需要一个整数,因为您也使用%d说明符告诉了它。

You can amend it by adding a cast to bool , like this: 您可以通过在bool添加演员表来修改它,如下所示:

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

A pointer to member may be converted to a bool (which you do with the cast), and the bool then undergoes integral promotion to an int on account of being an integral variadic argument to a variadic function. 指向成员的指针可能会转换为布尔型(您可以使用强制转换),然后bool会成为可变参数的完整可变参数,因此将整数进行整数提升为int

Speaking of the conversion to bool , it's exactly the conversion that is applied implicitly by attempting to call std::ostream 's operator<< . 说到bool转换,正是通过尝试调用std::ostreamoperator<<隐式地应用了转换。 Since there isn't an overload of the operator that supports pointers to members, overload resolution selects another that is callable after implicitly converting &Foo::c to a boolean. 由于不存在支持成员指针的运算符的重载,因此重载解析会在将&Foo::c隐式转换为布尔值之后选择另一个可调用的。

In addition to the more literal answer about why the compiler interpreted your code the way it did: you seem to have an XY problem You're trying to format a pointer-to-member as an integer, which strongly suggests you meant to do something different. 除了关于为什么编译器以这种方式解释代码的更确切的答案之外:您似乎还有一个XY问题您正试图将一个指向成员的指针格式化为整数,这强烈表明您打算做某事不同。

If what you wanted was an int value stored in .c , you either need to create an instance Foo some_foo; 如果您想要的是存储在.cint值,则需要创建一个实例Foo some_foo; and take some_foo.c , or else you need to declare Foo::c a static member, so there's one unambiguous Foo::c across the entire class. 并使用some_foo.c ,否则您需要将Foo::c声明为static成员,因此整个类中只有一个明确的Foo::c Do not take the address in this case. 在这种情况下,请勿使用该地址。

If what you wanted was to take an address of the .c member of some Foo , you should do as above so that Foo::c is static and refers to one specific variable, or else declare an instance and take its .c member, then take the address. 如果您想要获取某个Foo.c成员的地址,则应按照上述步骤进行操作,以使Foo::cstatic并引用一个特定变量,或者声明一个实例并采用其.c成员,然后取地址。 The correct printf() specifier for an object pointer is %p , and to print an object pointer representation with <iostream> , convert it to void* : 对象指针的正确printf()说明符为%p ,并使用<iostream>打印对象指针表示形式,将其转换为void*

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

If what you want is the offset of Foo::c within class Foo , you want the offsetof() macro in <stddef.h> . 如果想要的是类FooFoo::c的偏移量,则需要<stddef.h>offsetof()宏。 Since its return value is size_t , which is not the same size as int on 64-bit platforms, you would want to either cast the result explicitly or pass printf() the z type specifier: 由于它的返回值是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';

Whatever you were trying to do, if your compiler didn't warn you about the type mismatch, you ought to turn on more warnings. 无论您尝试做什么,如果编译器没有警告您类型不匹配,您都应该打开更多警告。 This is especially true for someone coding by trial and error until the program compiles. 对于经过反复试验直到程序编译的人来说尤其如此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM