首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >&C::c和&(C::c)有什么区别?

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

Stack Overflow用户
提问于 2018-10-23 02:19:19
回答 4查看 2.8K关注 0票数 48

测试代码在下面,我把输出信息在注释中。我用的是gcc 4.8.5和Centos 7.2。

代码语言:javascript
复制
#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++算子优先::运算符比&运算符具有更高的优先级。我认为&C::c等于&(C::c),但是输出不一样。他们为什么不一样?
  2. &(C::c)会导致main中的编译错误,而不是foo函数中的编译错误,为什么呢?
  3. &C::c的价值在printfstd::cout中是不同的,为什么呢?
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-10-23 03:04:33

C++对&操作符的两种操作数进行了区分,通常是lvalue,具体是(合格的)标识符。在&C::c中,&的操作数是一个限定标识符(即名称),而在&(C::c)中,操作数是一个通用表达式(因为(不能是名称的一部分)。

限定标识符表单有一个特例:如果它引用一个类的非静态成员(如您的C::c),则&返回一个称为“指向C成员的指针”的特殊值。见有关成员指针的更多信息,请参见

&(C::c)中没有特例。C::c通常被解析并失败,因为没有对象可以获取c成员。至少在main中是这样的;在C的方法(如您的foo)中,有一个隐式this对象,因此C::c实际上在那里表示this->c

至于为什么printfcout的输出是不同的:当您尝试用<<打印成员指针时,它被隐式转换为bool,如果它是空指针,则生成false,否则生成truefalse被打印为0true被打印为1。您的成员指针不是空的,所以您得到了1。这与普通指针不同,后者被隐式转换为void *并打印为地址,但成员指针不能转换为void *,因此operator<<唯一适用的重载是用于bool的重载。见ltlt#Notes

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

代码语言:javascript
复制
printf("%p\n", static_cast<void *>(&(C::c)));

该标准的相关部分是expr.unary.op,,即:

一元&运算符的结果是指向其操作数的指针。操作数应该是一个值或一个限定id.如果操作数是一个限定id,它用类型m命名某个类C的非静态成员或变体成员T,则结果类型为“指向T类型的类C成员的指针”,并且是指定C​::​m的实际值。否则,如果表达式的类型为T,则结果的类型为“指向T的指针”.

票数 47
EN

Stack Overflow用户

发布于 2018-10-23 02:48:10

表达式&C::c导致指向成员c的指针,而表达式&(C::c)则生成成员变量c的地址。您在输出中看到的区别是,std::cout包含一个bool隐式转换,它告诉您指针是否为空。

由于&C::c实际上不是空的,所以隐式地将值true1转换为bool

票数 4
EN

Stack Overflow用户

发布于 2018-10-23 03:18:14

Q1:&的语法有特殊的含义,后面跟着一个无括号的限定-id。它的意思是形成一个指针到成员.此外,没有其他方法可以形成指针到成员.C++17 expr.unary.op/4介绍了这一点:

指向成员的指针只有在使用显式&且其操作数是限定id而不是括在括号内时才会形成。[注意:也就是说,表达式&(qualified-id) (限定-id被括在括号中)并不构成“指向成员的指针”类型的表达式。合格的身份证明也不.

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

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

有关如何打印指向成员的指针的进一步讨论,见这个答案

Q2:代码&(C::c)并不像上面解释的那样形成指向成员的指针。

现在,代码C::c是在语法范畴id-表达式.(即限定-id和不合格-id)。id-表达式对其使用有一些限制,例如expr.prim.id/1:

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

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

当我们在C::foo函数中时,应用第一个要点。代码与&c相同,但具有不必要的限定条件。它的类型为int *。您可以使用std::cout << &(C::c);输出该地址,这将显示一个内存地址,this->c的地址。

当我们使用main函数时,这三个要点都不适用,因此&(C::c)格式不正确。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52940237

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档