简体   繁体   中英

What's the difference between &C::c and &(C::c)?

Test code in below and I put the output info in comment. I was using gcc 4.8.5 and Centos 7.2.

#include <iostream>
#include <cstdio>

class C 
{
    public:
        void foo() {
            printf("%p, %p\n", &C::c, &(C::c)); // output value is 0x4, 0x7ffc2e7f52e8
            std::cout << &C::c << std::endl;    // output value is 1
        }
        int a;
        int c;
};

int main(void)
{
    C co;

    printf("%p\n", &C::c);              // output value is 0x4
    std::cout << &C::c << std::endl;    // output value is 1

//    printf("%p\n", &(C::c));   // compile error, invalid use of non-static data member 'C::c'

    co.foo();

    return 0;
}
  1. According to C++ operator Precedence ,the :: operator has higher precedence than the & operator. I think &C::c is equal to &(C::c) , but the output says otherwise. Why are they different?
  2. &(C::c) causes a compile error in main but not in the foo function,why is that?
  3. The value of &C::c is different in printf and std::cout , why is that?

C++ distinguishes two forms of operands to the & operator, lvalues in general and (qualified) identifiers specifically. In &C::c the operand of & is a qualified identifier (ie just a name) whereas in &(C::c) the operand is a general expression (because ( cannot be part of a name).

The qualified identifier form has a special case: If it refers to a non-static member of a class (like your C::c ), & returns a special value known as a "pointer to member of C". See here for more information about member pointers .

In &(C::c) there is no special case. C::c is resolved normally and fails because there is no object to get a c member of. At least that's what happens in main ; in methods of C (like your foo ) there is an implicit this object, so C::c actually means this->c there.

As for why the output is different for printf vs. cout : When you try to print a member pointer with << , it is implicitly converted to a bool , yielding false if it's a null pointer and true otherwise. false is printed as 0 ; true is printed as 1 . Your member pointer is not null, so you get 1 . This is different from normal pointers, which are implicitly converted to void * and printed as addresses, but member pointers cannot be converted to void * so the only applicable overload of operator<< is the one for bool . See https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt#Notes .

Note that technically your printf calls have undefined behavior. %p takes a void * and you're passing it pointers of different types. In normal function calls the automatic conversion from T * to void * would kick in, but printf is a variable-arguments function that provides no type context to its argument list, so you need a manual conversion:

printf("%p\n", static_cast<void *>(&(C::c)));

The relevant part of the standard is [expr.unary.op] , saying:

The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id . If the operand is a qualified-id naming a non-static or variant member m of some class C with type T , the result has type “pointer to member of class C of type T ” and is a prvalue designating C​::​m . Otherwise, if the type of the expression is T , the result has type “pointer to T ” [...]

While the expression &C::c result in a pointer to member c the expression &(C::c) yield the address of the member variable c . The difference you're seeing in the output is that std::cout involve a bool implicit conversion that tells you whether the pointer is null or not.

Since &C::c is actually not null is implicitly converted to bool with value true or 1

Q1: There is special meaning to the syntax of & followed by an unparenthesized qualified-id . It means to form a pointer-to-member. Furthermore, there is no other way to form a pointer-to-member. This is covered by C++17 [expr.unary.op]/4:

A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed in parentheses. [Note: That is, the expression &(qualified-id) , where the qualified-id is enclosed in parentheses, does not form an expression of type “pointer to member”. Neither does qualified-id [...]


Q3: In both cases where you write printf("%p\\n", &C::c); , &C::c is a pointer-to-member. The %p format specifier is only for void * so this causes undefined behaviour, and the program output is meaningless.

The code cout << &C::c; outputs a pointer-to-member via operator<<(bool val) , since there is implicit conversion from pointer-to-member to bool (with result true in all cases), see [conv.bool]/1.

For further discussion of how to print a pointer-to-member, see this answer .


Q2: The code &(C::c) does not form a pointer-to-member as explained above.

Now, the code C::c is in the grammatical category id-expression . (Which is qualified-id and unqualified-id ). An id-expression has some restrictions on its use, [expr.prim.id]/1:

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  • as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or
  • to form a pointer to member (7.6.2.1), or
  • if that id-expression denotes a non-static data member and it appears in an unevaluated operand.

When we are inside the C::foo function, the first of those bullet points applies. The code is the same as &c but with unnecessary qualification. This has type int * . You could output this with std::cout << &(C::c); which would show a memory address, the address of this->c .

When we are in the main function , none of the three bullet points apply and therefore the &(C::c) is ill-formed.

First, you can not access the int c by using &(C::c) outside a class. &(C::c) means the memory address of " c of instance C ", somewhat &(this->c) here. However your c is not a static member of class C and there is no C instance. You can not access int x = C::c outside, either.

So you see an error with:

//    printf("%p\n", &(C::c));   // compile error, invalid use of non-static data member 'C::c'

If you have a static int c , then C::c outside the class is OK, because no instance is needed here.

And let's run

#include <iostream>
#include <cstdio>

class C
{
    public:
        void foo() {
            printf("%p, %p, this=%p\n", &C::c, &(C::c), this);
        }
        int a;
        int c;
};

int main(void)
{
    C co;
    co.foo();
    return 0;
}

The output is:

0x4, 0x7ffee78e47f4, this=0x7ffee78e47f0
// 0x4 + this == 0x7ffee78e47f4
// see reference

And for std::out : the << &C::c is implicit-casted to bool , so true is 1 you saw. You can treat &C::c as an offset of c in C , it is unusable without a C instance.

That's all.

Some reference: C++: Pointer to class data member "::*"

Full description: https://en.cppreference.com/w/cpp/language/pointer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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