【问题标题】:C++ Returning Pointers/ReferencesC++ 返回指针/引用
【发布时间】:2011-02-19 16:52:17
【问题描述】:

我对解引用操作符、操作符的地址和一般的指针有相当好的了解。

但是,当我看到这样的东西时,我会感到困惑:

int* returnA() {
    int *j = &a;
    return j;
}

int* returnB() {
    return &b;
}

int& returnC() {
    return c;
}

int& returnC2() {
    int *d = &c;
    return *d;
}
  1. returnA() 我要求返回一个指针;只是为了澄清这个工作,因为j 是一个指针?
  2. returnB() 我要求返回一个指针;由于指针指向一个地址,returnB() 起作用的原因是因为我要返回&b
  3. returnC() 中,我要求返回int 的地址。当我返回 c 时,& 运算符是否会自动“附加”c
  4. returnC2() 中,我再次要求返回int 的地址。 *d 是否因为指针指向地址而起作用?

假设a、b、c被初始化为全局整数。

有人可以验证我的四个问题是否都正确吗?

【问题讨论】:

  • 这些问题的答案完全取决于代码中似乎缺少的 a、b 和 c 的声明。
  • 假设 a、b、c 已初始化。

标签: c++ pointers return


【解决方案1】:

虽然 Peter 回答了您的问题,但显然让您感到困惑的一件事是符号 *&。理解这些的难点在于它们都有两个与间接有关的不同含义(甚至不包括用于乘法的* 和用于位与的& 的第三个含义)。

  • *,当用作类型的一部分时 表示类型是指针: int 是一个类型,所以 int* 是一个 指向 int 类型的指针,int** 是 指向 int 类型的指针。

  • & 当用作 type 的一部分时表示该类型是一个引用。 int 是一个类型,所以 int& 是一个对 int 的引用(没有引用对引用之类的东西)。引用和指针用于类似的事情,但它们完全不同且不可互换。最好将引用视为现有变量的别名或备用名称。如果xint,那么您可以简单地分配int& y = xx 创建一个新名称y。后记,xy 可以互换使用来指代同一个整数。这样做的两个主要含义是引用不能为 NULL(因为必须有一个原始变量要引用),并且您不需要使用任何特殊运算符来获取原始值(因为它只是一个备用名称,不是指针)。引用也不能重新分配。

  • * 用作 一元运算符 时执行称为 dereference 的操作(与引用 types 无关!)。此操作仅对指针有意义。当你取消引用一个指针时,你会得到它指向的东西。所以,如果 p 是一个指向 int 的指针,那么 *p 就是被指向的 int

  • & 在用作一元运算符 时执行称为address-of 的操作。这是不言自明的;如果x是一个变量,那么&x就是x的地址。变量的地址可以分配给指向该变量类型的指针。因此,如果xint,那么&x 可以分配给int* 类型的指针,并且该指针将指向x。例如。如果你分配了int* p = &x,那么*p可以用来获取x的值。

所以请记住,类型后缀& 用于引用,与一元运算符& 无关,后者与获取用于指针的地址有关。这两种用途完全不相关。 * 作为类型后缀声明一个指针,而* 作为一元运算符对指针执行操作。

【讨论】:

  • 感谢我在这些运算符上看到的最清晰的解释之一。
  • 终于看到了一个解决方案,可以帮助我解决这两件事
  • 非常好的答案应该是被接受的。
  • 这是我读过的对*&最好最清晰的解释!请收下我的 +50 赏金 :)
【解决方案2】:

在 returnA() 中,我要求返回一个指针;只是为了澄清这行得通,因为 j 是一个指针?

是的,int *j = &aj 初始化为指向 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* 的指针,而这恰好是一个指针本身。

【讨论】:

  • 您能否对第三个问题(returnC() 函数)提供一个连贯的答案?即使 c 不是引用变量,为什么函数接受 c 作为它的返回值,即使它需要引用并且返回一个整数变量?
  • @ChosenTorture,我认为 Tyler 在下面的回答中很好地解释了这一点,所以我赞成并坚持 DRY 原则 :-)
  • 我仔细阅读了 Tyler 写的内容。要么他没有回答我的问题,要么我不明白什么。我请求你阅读我正在写的东西。您写道不,它是对返回的 int 的引用。那么当我将函数类型设为int& 时,这是否会告诉程序员应该返回一个引用,或者必须并且将返回一个引用?如果前一种情况是真的,那么代码不应该工作。但正因为如此,后者才是正确的。我只想问为什么?这是一个隐式转换过程还是什么?
  • @ChosenTorture,通过声明函数类型int&,你说它返回了一个引用,句号。引用只是变量的别名,因此不需要转换。就像,虽然 ChosenTorture 很可能不是您的真实姓名,但您在注册 StackOverflow 时仍然不需要进行任何转换,对吗? ;-) 这里的人称你为 ChosenTorture,但你妈妈可能用完全不同的名字称呼你,但你一直都是同一个。同样,在执行int& x = returnC()之后,x将引用与c相同的变量。
【解决方案3】:

Tyler,这是非常有帮助的解释,我使用 Visual Studio 调试器做了一些实验来进一步阐明这种差异:-

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

【讨论】:

    【解决方案4】:

    在 returnC() 和 returnC2() 中,您不要求返回地址。

    这两个函数都返回对对象的引用。
    引用不是任何东西的地址,它是某物的替代名称(这可能意味着编译器可能(或可能不取决于情况)使用地址来表示对象(或者它也可能知道将其保存在寄存器中)) .

    您都知道,参考点指向特定对象。
    虽然引用本身并不是一个对象,但它只是一个替代名称。

    【讨论】:

    • 这就是我感到困惑的地方;我以为我在返回地址;不是“别名”。
    【解决方案5】:

    您的所有示例都会产生未定义的运行时行为。您正在返回对在执行离开函数后消失的项目的指针或引用。

    让我澄清一下:

    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 加一。该函数返回对常量值的引用。这意味着其他函数可以(从远处)使用该值,就像它是一个变量一样(无需取消引用指针)。

    在我看来,指针用于可选参数或对象。当对象必须存在时传递一个引用。在函数内部,引用的参数意味着该值存在,但是在取消引用之前必须检查指针是否为空。此外,通过引用,可以更好地保证目标对象是有效的。指针可能指向无效地址(非空)并导致未定义的行为。

    【讨论】:

    • 如果您假设abc 是全局变量,那么示例代码就可以了。如果它们在函数中被声明为静态,那也没问题。静力学不会移入和移出临时位置。它们的位置是静态的。您始终可以返回指针或对静态变量的引用。这并不总是一个好主意(重入和多线程),但它确实有效。
    【解决方案6】:

    在语义上,引用确实充当地址。但是,从语法上讲,它们是编译器的工作,而不是您的工作,您可以将引用视为它指向的原始对象,包括将其他引用绑定到它并让它们也引用原始对象。在这种情况下,告别指针运算。

    这样做的缺点是您无法修改它们所指的内容 - 它们在构造时被绑定。

    【讨论】:

      猜你喜欢
      • 2013-04-30
      • 1970-01-01
      • 1970-01-01
      • 2012-04-18
      • 1970-01-01
      • 1970-01-01
      • 2011-12-10
      • 2021-01-11
      • 2012-08-01
      相关资源
      最近更新 更多