【问题标题】:Returning a reference and returning a value返回引用并返回值
【发布时间】:2016-10-09 10:23:34
【问题描述】:

我不太确定我是否理解当我们返回局部随机变量的引用时为什么会出现问题。所以假设我们有这个例子。

int *myFunc() {
  int phantom = 4;
  return &phantom;
}

然后通常的说法是,在使用函数时,变量phantom的内存在代码行int phantom = 4;执行后不再可用所以无法返回(至少我是这样的)目前理解)。另一方面,对于函数,

int myFunc() {
  int phantom = 4;
  return phantom;
}

整数变量phantom 的值将返回。 (我将值的返回视为变量phantom 的基础指针的取消引用)。

我在这里想念什么?为什么在第一种情况下会出现编译错误,而在第二种情况下一切正常??

【问题讨论】:

    标签: c++ pointers reference


    【解决方案1】:

    第一个不返回引用,它返回一个指针。指向函数结束后将超出范围的局部变量的指针,给您留下一个指向不再存在的变量的杂散指针。这就是为什么您会收到编译器警告(通常不是实际错误)。

    第二个代码复制值。一旦return 语句完成,函数内部的局部变量将永远不需要被引用或使用。

    【讨论】:

    • @xaxxon 使用正确的术语很重要 :)
    • @xaxxon:从技术上讲,引用就像一个不可为空的常量指针。但它们具有不同的语义,不应混为一谈。我会说 Joachim Pileborg 有正确的答案,但也值得一提的是,在像这样的大多数简单情况下,NRVO 会启动并忽略副本。因此,值得仅按值返回,直到您发现自己需要返回必须在堆上分配的 BigHonkingObject。
    • @djrollins 不,从技术上讲,引用是指其他事物。就像指针如何引用内存地址一样。 c++ 参考 更具体。 merriam-webster.com/dictionary/reference
    • "第二个代码复制值。一旦 return 语句完成,函数内部的局部变量将永远不需要引用或使用。"它在哪里复制价值?我猜想某个地方可以在 return 语句中获取并返回它......你的意思是在新的 int 对象的堆中存在隐式内存分配,以便可以以某种方式访问​​该值?
    • 或者换句话说,在第二种情况下,当函数int myFunc()被执行时,按照你的逻辑,局部变量phantom的地址会丢失,那么它的值怎么能被退回???是否将内存隐式分配给可访问的内存路径?
    【解决方案2】:

    你不会错过太多。只有在第一种情况下不会出现编译器错误。

    [...] 执行代码行 int phantom = 4; 后变量 phantom 的内存不再可用所以无法退货

    不,它可以返回,编译器可能会为此发出警告,但 afaik 没有错误。但是,你不应该!

    顺便说一句,内存是可用的,但是在函数返回后访问它是未定义的行为(而不是在int phantom = 4;行之后)。

    第二种情况:

    我将值的返回视为对变量幻像的基础指针的取消引用

    你在这里想得太复杂了。从函数返回值可以通过使用指针来实现,但这是一个实现细节。这里你唯一需要关心的是返回的是value。所以第二种情况没有问题。

    【讨论】:

    • 实际问题是这样的:如果局部变量幻象的内存消失了,那么幻象的值在第二种情况下如何返回?不知何故,这个值应该存储在某个地方,以便在函数结束时返回......对吗?
    • @noob-mathematician 记忆不会消失。在第二种情况下,将值的副本返回给调用者。此机制与您可能在函数内声明的某些变量无关。你也可以有return 1;
    • @noob-mathematician 你可能想了解return value optimization。也许这有助于澄清一些事情
    【解决方案3】:

    我不太确定我是否理解为什么会出现问题 我们返回一个局部随机变量的引用

    因为 C++ 标准规定如果您使用这样的函数是未定义的行为,并且您希望避免程序中出现未定义的行为。你的程序做什么应该由C++语言的规则决定,而不是随意的。

    请注意,您返回的是指针而不是引用。但在这两种情况下都是未定义的行为。

    那么通常的说法是,当函数被使用时,内存 变量phantom 在执行后不再可用 代码行int phantom = 4; 所以它不能被返回(至少这个 是我目前所理解的)。

    这是一个以实现为中心的观点,可以帮助您理解问题。

    尽管如此,区分程序的可观察行为和编译器产生该行为的内部技巧很重要。您甚至都不知道变量是否占用了任何内存。考虑到“as-if”规则和编译器优化,整个函数可能已被删除,即使行为已定义。这只是幕后可能真正发生的事情的一个例子。

    但是,无论如何,这是未定义的行为,所以任何事情都可能发生。

    那么问题是,为什么 C++ 标准没有为当您返回这样的指针然后尝试访问指针对象时的情况定义行为?答案是没有意义。由局部变量 phantom 命名的对象在函数返回时结束其生命周期。所以你会有一个指向不再存在的东西的指针,但它仍然是一个int*,并且取消引用非nullptr int* 应该产生一个int。这是一个矛盾,而 C++ 标准只是懒得解决这种毫无意义的情况。

    注意这个观察是如何基于 C++ 语言规则的,不是编译器实现问题。

    为什么在第一种情况下会出现编译错误,而在第二种情况下 万一一切正常??

    这肯定是一个警告而不是一个错误,除非你的编译器选项使得每个警告都变成一个错误。编译器不能拒绝代码,因为it's not ill-formed

    尽管如此,您的编译器仍试图在第一种情况下提供帮助,因为它希望阻止您创建具有未定义行为的程序。

    在第二种情况下,行为不是未定义的。按值返回意味着 copy 由您要返回的对象组成。副本是在原始文件被销毁之前制作的,然后调用者会收到该副本。这不是毫无意义的,也不是任何方面的矛盾,所以它是安全且明确的行为。

    在第一种情况下,按值返回对您没有帮助,因为虽然指针本身安全复制,但它的内容最终会导致未定义的行为。

    【讨论】:

    • "局部变量 phantom 命名的对象在函数返回时结束生命。"所以它一直存在,直到并包括 return 语句(因此它的地址也是如此)。这个陈述不能让我满意,但我同意这个问题与 C++ 的构造所遵循的规则有关。
    【解决方案4】:

    第一种情况

    int* myFunc()
    {
       int phantom = 4;
       return &phantom;  // you are returning the address of phantom
    }                    // but phantom will not "exist" outside of myfunc
    

    不起作用,因为变量phantom 是一个局部变量,它只存在于myfunc 的执行期间。在那之后,它就消失了。

    您正在返回一个变量的地址,该地址实际上将不再“存在”。

    规则:永远不要返回对局部变量的指针或引用。

    没关系:

     int myFunc()
     {
        int phantom = 4;
        return phantom;  // you are returning by value;
                         // it doesn't matter where phantom "lives"
     }                  
    
     int main()
     {
        int x = myFunc(); // the value returned by myFunc will be copied to x
     }
    

    【讨论】:

    • 大多数说第二个没问题的人不知道为什么没问题。这就是我的问题的范围。如果第一个不行,为什么第二个可以?为了在第二种情况下返回整数,您需要将其“存储”在某个地方,否则,按照第一种情况的逻辑,局部变量会在 return 语句之前消失。
    • @noob-mathematician:C++ 保证它会工作;时期。您的特定编译器使用什么机制来分配/存储/保存该值并不重要。您只需要知道它确实如此。
    • @noob-mathematician:“大多数说第二个没问题的人都不知道为什么没问题。” - 这有点牵强! :)
    • 要得到答案,你需要稍微了解一下汇编和计​​算机体系结构。返回值将存储在寄存器中,然后根据您的代码移动到特定的内存位置( x=myFunc() ---在本例中为 x 所在的位置)。
    【解决方案5】:

    尝试返回指针并取消引用以获取值。

    #include <iostream>
    
    using namespace std;
    
    
    int *myFunc()
    {
    
        int number = 4;
        int *phantom = &number;
    
        return phantom;
    
    }
    
    int main()
    {
        cout << myFunc() << endl; //0x....
        cout << *myFunc() << endl; //4
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-02
      • 1970-01-01
      • 2018-07-29
      • 2017-12-03
      • 2011-11-10
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多