繁体   English   中英

&C :: c和&(C :: c)之间有什么区别?

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

测试下面的代码,我将输出信息放在注释中。 我使用的是gcc 4.8.5和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. 根据C ++运算符Precedence::运算符的优先级高于&运算符。 我认为&C::c等于&(C::c) ,但输出则另有说明。 他们为什么不同?
  2. &(C::c)在main函数中导致编译错误但在foo函数中没有,为什么会这样?
  3. &C::c的值在printfstd::cout是不同的,为什么呢?

C ++将两种形式的操作数区分为&运算符,一般是lvalues,特别是(限定的)标识符。 &C::c中, &的操作数是限定标识符(即只是名称),而在&(C::c) ,操作数是一般表达式(因为(不能是名称的一部分)。

的限定的标识符形式具有一种特殊情况:如果它是指一类的非静态成员(如您C::c ), &返回一个称为“指针到C的成员”的特殊值。 有关成员指针的更多信息,请参见此处

&(C::c)中没有特殊情况。 C::c正常解析并失败,因为没有对象来获取c成员。 至少那是main发生的事情; C方法(比如你的foo )中有一个隐含的this对象,所以C::c实际上意味着this->c

至于为什么输出是不同printfcout :当您尝试打印带有成员指针<< ,它是隐式转换为bool ,产生false如果它是一个空指针和true ,否则。 false打印为0 ; true打印为1 您的成员指针不为null,因此您获得1 这与普通指针不同,普通指针隐式转换为void *并作为地址打印,但成员指针不能转换为void *因此, operator<<唯一适用的重载是bool 请参阅https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt#Notes

请注意,从技术上讲,您的printf调用具有未定义的行为。 %p采用void *并且你传递不同类型的指针。 在正常函数调用中,从T *void *的自动转换将启动,但printf是一个变量参数函数,它不为其参数列表提供类型上下文,因此您需要手动转换:

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

标准的相关部分是[expr.unary.op] ,说:

一元&运算符的结果是指向其操作数的指针。 操作数应为左值或限定ID 如果操作数是一个合格的-ID命名非静态或变体构件m的一些类C类型T ,结果类型“指针类的成员C类型的T ”,是一个指定prvalue C​::​m 否则,如果表达式的类型为T ,则结果的类型为“指向T指针”[...]

当表达式&C::c导致指向成员c的指针时,表达式&(C::c)产生成员变量c的地址。 您在输出中看到的差异是std::cout涉及bool隐式转换,它告诉您指针是否为null。

由于&C::c实际上不为null,因此隐式转换为值为true1 bool

Q1:有特殊意义的语法&后跟加括号的合格-ID。 它意味着形成一个指向成员的指针。 此外,没有其他方法可以形成指向成员的指针。 这由C ++ 17 [expr.unary.op] / 4涵盖:

仅当使用显式&时,才会形成指向成员的指针,并且其操作数是未括在括号中的qualified-id [注意:也就是说,表达式&(qualified-id) ,其中qualified-id括在括号中,不形成“指向成员的指针”类型的表达式。 也不是qual-id [...]


Q3:在你写printf("%p\\n", &C::c);两种情况下printf("%p\\n", &C::c); &C::c是指向成员的指针。 %p格式说明符仅适用于void *因此会导致未定义的行为,并且程序输出无意义。

代码cout << &C::c; 通过operator<<(bool val)输出指向成员的指针,因为存在从指针到成员到bool隐式转换(在所有情况下结果都为true ),请参见[conv.bool] / 1。

有关如何打印指向成员的指针的进一步讨论, 请参阅此答案


Q2:代码&(C::c)不形成如上所述的指向成员的指针。

现在,代码C::c在语法类别id-expression中 (哪个是qual-idnonqualified-id )。 id-expression对其使用有一些限制,[expr.prim.id] / 1:

只能使用表示非静态数据成员或类的非静态成员函数的id表达式:

  • 作为类成员访问的一部分,其中对象表达式引用成员的类或从该类派生的类,或
  • 形成指向成员的指针(7.6.2.1),或
  • 如果该id-expression表示非静态数据成员,则它出现在未评估的操作数中。

当我们进入C::foo函数时,第一个子弹点适用。 代码与&c相同,但具有不必要的资格。 这个类型为int * 您可以使用std::cout << &(C::c); 这将显示一个内存地址,这个地址 - this->c

当我们处于main函数时,三个子弹点都不适用,因此&(C::c)是不正确的。

首先,您不能通过在类外使用&(C::c)来访问int c &(C::c)表示“ c实例C ”的存储器地址,在某种程度上&(this->c)在这里。 但是,您的c不是C类的静态成员,并且没有C实例。 你也不能在外面访问int x = C::c

所以你看到一个错误:

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

如果你有一个static int c ,那么类之外的C::c就可以了,因为这里不需要实例。

让我们跑吧

#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;
}

输出是:

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

而对于std::out :在<< &C::c是隐式浇铸bool ,所以true1 ,你所看到的。 您可以将&C::c视为&C::c an offset of c in C ,如果没有C实例,它将无法使用。

就这样。

一些参考: C ++:指向类数据成员“:: *”的指针

完整描述: https//en.cppreference.com/w/cpp/language/pointer

暂无
暂无

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

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