[英]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;
}
::
运算符的优先级高于&
运算符。 我认为&C::c
等于&(C::c)
,但输出则另有说明。 他们为什么不同? &(C::c)
在main函数中导致编译错误但在foo
函数中没有,为什么会这样? &C::c
的值在printf
和std::cout
是不同的,为什么呢? C ++将两种形式的操作数区分为&
运算符,一般是lvalues,特别是(限定的)标识符。 在&C::c
中, &
的操作数是限定标识符(即只是名称),而在&(C::c)
,操作数是一般表达式(因为(
不能是名称的一部分)。
的限定的标识符形式具有一种特殊情况:如果它是指一类的非静态成员(如您C::c
), &
返回一个称为“指针到C的成员”的特殊值。 有关成员指针的更多信息,请参见此处 。
在&(C::c)
中没有特殊情况。 C::c
正常解析并失败,因为没有对象来获取c
成员。 至少那是main
发生的事情; 在C
方法(比如你的foo
)中有一个隐含的this
对象,所以C::c
实际上意味着this->c
。
至于为什么输出是不同printf
与cout
:当您尝试打印带有成员指针<<
,它是隐式转换为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
”,是一个指定prvalueC::m
否则,如果表达式的类型为T
,则结果的类型为“指向T
指针”[...]
当表达式&C::c
导致指向成员c
的指针时,表达式&(C::c)
产生成员变量c
的地址。 您在输出中看到的差异是std::cout
涉及bool隐式转换,它告诉您指针是否为null。
由于&C::c
实际上不为null,因此隐式转换为值为true
或1
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-id和nonqualified-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
,所以true
是1
,你所看到的。 您可以将&C::c
视为&C::c
an offset of c in C
,如果没有C
实例,它将无法使用。
就这样。
一些参考: C ++:指向类数据成员“:: *”的指针
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.