我对解除引用运算符,运算符地址和一般指针有相当好的理解。
然而,当我看到这样的东西时,我感到很困惑:int* returnA() {
int *j = &a;
return j;
}
int* returnB() {
return &b;
}
int& returnC() {
return c;
}
int& returnC2() {
int *d = &c;
return *d;
}
returnA()
我要求返回一个指针;只是为了澄清这是有效的,因为j
是一个指针?returnB()
我要求返回一个指针;由于指针指向一个地址,returnB()
工作的原因是因为我正在返回&b
? returnC()
我要求返回int
的地址。当我返回c
时,&
运算符会自动“追加”c
?returnC2()
我再次要求提交int
的地址。 *d
是否有效,因为指针指向一个地址?假设a,b,c被初始化为整数。
有人可以验证我的所有四个问题是否正确吗?
答案 0 :(得分:72)
虽然彼得回答了你的问题,但有一点让你感到困惑的是符号*
和&
。了解这些问题的难点在于它们都有两个与间接有关的不同含义(甚至不包括乘法的*
的第三含义和按位的&
和。) / p>
*
,用作类型的一部分
表示该类型是指针:
int
是一种类型,因此int*
是一种类型
指向int类型,int**
是一个
指向指针到指针类型的指针。
&
用作类型的一部分时表示该类型是引用。 int
是一个类型,因此int&
是一个引用到int(没有引用引用这样的东西)。引用和指针用于类似的事情,但它们是完全不同的,不可互换。最好将引用视为现有变量的别名或备用名称。如果x
是int
,那么您只需指定int& y = x
即可为y
创建新名称x
。后,x
和y
可以互换使用,以指代相同的整数。这样做的两个主要含义是引用不能为NULL(因为必须有一个原始变量来引用),并且您不需要使用任何特殊运算符来获取原始值(因为它只是一个替代名称,不是指针)。参考文献也不能重新分配。
*
用作一元运算符执行名为 dereference 的操作(与参考类型无关!)。此操作仅对指针有意义。当您取消引用指针时,您将返回它指向的内容。因此,如果p
是指向int的指针,则*p
是指向的int
。
&
用作一元运算符时执行名为 address-of 的操作。这是非常不言自明的;如果x
是变量,则&x
是x
的地址。可以将变量的地址分配给指向该变量类型的指针。因此,如果x
是int
,则可以将&x
分配给类型为int*
的指针,该指针将指向x
。例如。如果您指定int* p = &x
,则*p
可用于检索x
的值。
请记住,类型后缀&
用于引用,与一元操作&
无关,后者与获取指针使用的地址有关。这两种用途完全不相关。 *
作为类型后缀声明一个指针,而*
作为一元运算符对指针执行操作。
答案 1 :(得分:27)
在returnA()中我要求返回一个指针;只是为了澄清这是有效的,因为j是一个指针?
是的,int *j = &a
初始化j
以指向a
。然后返回的值j
,即地址a
。
在returnB()中我要求返回一个指针;因为指针指向一个地址,returnB()工作的原因是因为我正在返回& b?
是。在这里,同样的事情发生在上面,只需一步。 &b
提供b
的地址。
在returnC()中,我要求返回一个int的地址。当我回来时,c是&运算符自动附加?
不,它是对返回的int的引用。引用不是与指针相同的地址 - 它只是变量的替代名称。因此,您无需应用&
运算符来获取变量的引用。
在returnC2()中,我再次询问要返回的int的地址。 * d是否有效,因为指针指向一个地址?
同样,它是对返回的int的引用。 *d
是指原来变量c
(无论其可以是),通过指向c
。这可以隐含地转换为引用,就像在returnC
中一样。
指针未在一般指向一个地址(尽管它们可以 - 例如int**
是指向int的指针的指针)。指针是某事物的地址。在声明指针像something*
,即something
是映入指针指向。所以在上面的例子中,int**
声明了一个指向int*
的指针,它恰好是指针本身。
答案 2 :(得分:5)
int sample = 90;
int& alias = sample;
int* pointerToSample = &sample;
Name Address Type
&alias 0x0112fc1c {90} int *
&sample 0x0112fc1c {90} int *
pointerToSample 0x0112fc1c {90} int *
*pointerToSample 90 int
alias 90 int &
&pointerToSample 0x0112fc04 {0x0112fc1c {90}} int * *
PointerToSample Sample/alias
_______________......____________________
0x0112fc1c | | 90 |
___________|___.....__|________|_______...
[0x0112fc04] ... [0x0112fc1c
答案 3 :(得分:2)
在returnC()和returnC2()中,你不是要求返回地址。
这两个函数都返回对象的引用 引用不是任何东西的地址,它是某种东西的替代名称(这可能意味着编译器可能(或可能不依赖于情况)使用地址来表示对象(或者它也可能知道将其保留在寄存器中))
您只知道参考指向特定对象 虽然引用本身不是对象,但只是一个替代名称。
答案 4 :(得分:2)
您的所有示例都会产生未定义的运行时行为。您正在返回指针或对在执行离开函数后消失的项的引用。
让我澄清一下:
int * returnA()
{
static int a; // The static keyword keeps the variable from disappearing.
int * j = 0; // Declare a pointer to an int and initialize to location 0.
j = &a; // j now points to a.
return j; // return the location of the static variable (evil).
}
在您的函数中,变量j
被指定为指向a
的临时位置。退出函数后,变量a
消失,但它以前的位置是通过j
返回的。由于a
指向的位置不再存在j
,因此访问*j
时会发生未定义的行为。
函数内部的变量不应该通过引用或指针修改其他代码。它可能会发生,虽然它会产生未定义的行为。
迂腐,返回的指针应声明为指向常量数据。返回的引用应该是const:
const char * Hello()
{
static const char text[] = "Hello";
return text;
}
上面的函数返回一个指向常量数据的指针。其他代码可以访问(读取)静态数据,但不能修改。
const unsigned int& Counter()
{
static unsigned int value = 0;
value = value + 1;
return value;
}
在上面的函数中,value
在第一个条目上初始化为零。此函数的所有下一次执行都会导致value
递增1。该函数返回对常量值的引用。这意味着其他函数可以使用该值(来自远方),就好像它是一个变量(无需取消引用指针)。
在我看来,指针用于可选参数或对象。当对象必须存在时传递引用。在函数内部,引用的参数意味着该值存在,但是在解除引用之前必须检查指针是否为null。此外,通过引用,可以更加保证目标对象有效。指针可能指向无效的地址(非空)并导致未定义的行为。
答案 5 :(得分:1)
从语义上讲,引用确实充当地址。但是,从语法上讲,它们是编译器的工作,而不是你的工作,并且你可以将引用视为它指向的原始对象,包括绑定对它的其他引用并让它们引用原始对象。在这种情况下告别指针算术。
缺点是你无法修改他们所指的内容 - 他们在构造时受到约束。