【问题标题】:Pointer of class local variable? [duplicate]类局部变量的指针? [复制]
【发布时间】:2011-12-01 12:59:07
【问题描述】:

可能重复:
Can a local variable's memory be accessed outside its scope?

做这样的代码(getIDs()返回一个指针)有什么令人担忧的事情:

class Worker{
private:
    int workerID;
    int departID;
    int supervisorID;
public:
    Worker() 
    {
        workerID=0;
        departID=0;
        supervisorID=0;
        name="anonymous";
        workerAddress="none";
    }

    void setIDs(int worker, int depart, int supervisor)
    {
        workerID=worker;
        departID=depart;
        supervisorID=supervisor;
    }

    int* getIDs()
    {
        int id[3];
        id[0]=workerID;
        id[1]=departID;
        id[2]=supervisorID;
        return id;
    }
};

然后,这样使用它:

Worker obj;
obj.setIDs(11,22,33);
cout<<(*obj.getIDs())<<endl;
cout<<++(*obj.getIDs())<<endl;
cout<<++(++(*obj.getIDs()))<<endl;

我对此感到疑惑,因为编译器显示:

警告 1 警告 C4172:返回局部变量的地址或 临时的

【问题讨论】:

  • 这并不是真正的重复——在那个问题中,发帖人明白他们所做的事情不应该起作用,并且对它为什么起作用感到困惑。在这个问题中,发帖人想知道这是否可行。

标签: c++ class pointers


【解决方案1】:

您的int id[3] 分配在堆栈上,并在您的int* getIDs() 返回时被销毁。

【讨论】:

    【解决方案2】:

    您将返回一个指向变量的指针,该变量在getIDs() 返回后立即被销毁。然后指针变得悬空并且实际上是无用的,因为对其进行任何操作都是未定义的行为。

    假设你这样定义你的类:

    class Worker{
    private:
        int IDs[3];
    public
        // ...
        int* getIDs() { return IDs; }
    };
    

    部分解决了您的问题,因为只要Worker 对象在范围内,指针仍然有效,但这仍然是不好的做法。示例:

    int* ptr;
    while (true) {
        Worker obj;
        obj.setIDs(11,22,33);
        ptr = obj.getIDs();
        cout << *ptr;   // ok, obj is still alive.
        break;
    }  // obj gets destroyed here
    cout << *ptr;   // NOT ok, dereferencing a dangling pointer
    

    解决此问题的更好方法是为您的班级实现自定义运算符

    class Worker {
    private:
        int workerID;
        int departID;
        int supervisorID;
    public:
        // ...
        friend ostream& operator<<(ostream& out, Worker w);
    };
    
    
    ostream& operator<<(ostream& out, const Worker& w)
    {
        out << w.workerID << "\n" << w.departID << "\n" << w.supervisorID;
        return out;
    }
    

    【讨论】:

      【解决方案3】:

      即使这样可行,在 c++ 中这样做也不是一个好习惯,除非有一些深刻的原因需要指向 int 的指针。原始 c-syle 数组比 std::vectors 更难处理,所以使用它们,比如

      std::vector<int> getIDs(){
        std::vector<int> id(3);
        id[0]=workerID; id[1]=departID; id[2]=supervisorID;
        return id;
      }
      

      如果您担心开销:现代编译器可能会完全优化它。

      【讨论】:

      • @: 使用静态 int id[3];能解决吗?
      • @Adban 既然还没有人回答你,我会的。是的,静态会有所帮助。但问题是静态是否真的是你想要的。 通常 static 用于保存函数的一些内部数据。在这里,您只想计算并返回一些东西。
      • 可能吗?怎么可能靠得住?我不会像这样复制向量...
      • 由于复制省略,任何最近符合现代标准的编译器“几乎肯定”会优化掉该副本。但是,正确的方法是(在我看来)为所有需要它们的类成员变量简单地提供访问器/修改器。
      • @Adban:正如 Roman B. 所说,它适用于静态,但这几乎肯定不是你想要的。然后,您将返回一个指向 Worker 的所有实例共享的数组的指针,因此如果您从外部调用,例如对于Worker w1, w2;,首先是int* w1IDs=w1.getIDs(),然后是int* w2IDs = w2.getIDs(),那么两个指针都指向同一个数组,现在只存储w2 的ID,而w1 的ID 再次被覆盖。
      【解决方案4】:

      一旦您离开定义它的函数,本地(也称为自动)变量将被销毁。所以你的指针会指向这个被破坏的位置,当然在函数之外引用这样的位置是不正确的,会导致未定义的行为。

      【讨论】:

        【解决方案5】:

        这里的基本问题是,当你进入一个函数调用时,你会在你的堆栈上得到一个新的框架(你所有的局部变量都将被保存在那里)。函数中未动态分配(使用 new/malloc)的任何内容都将存在于该堆栈帧中,并在函数返回时被销毁。

        您的函数返回一个指向您在该堆栈帧中声明的 3 元素数组开头的指针,该指针将消失。所以,这是未定义的行为

        虽然您可能会得到“幸运/不幸”,并且在使用它时您的数据仍然围绕指针指向的位置,但您也可能在此代码中遇到相反的情况。由于在销毁堆栈帧时会放弃空间,因此可以重用它 - 因此代码的另一部分可能会使用存储该数组中三个元素的内存位置,这意味着它们将具有完全不同的值您取消引用该指针的时间。

        如果你幸运的话,你的程序只会出现段错误/崩溃,所以你知道自己犯了一个错误。

        重新设计您的函数以返回 3 个整数、一个向量的结构,或者至少(我不推荐这样做),使用 new 动态分配数组内容,以便它在函数调用后仍然存在(但您最好稍后删除它,否则小精灵会来抓你...)。

        【讨论】:

          【解决方案6】:

          编辑:抱歉,我完全误读了这个问题。在我喝咖啡之前不应该回答 StackOverflow。

          当你想返回一个数组,或者一个指针时,有两种方法。

          一条路线:新的

          int* n = new int[3];
          n[0] = 0;
          // etc..
          return n;
          

          由于n现在是一个堆对象,你以后删除它,如果你不删除它,最终会导致内存泄漏。

          现在,我发现路线二是一种更容易的方法,但它的风险更大。这是你传入一个数组并复制值的地方。

          void copyIDs(int arr[3] /* or int* arr */)
          {
              arr[0] = workerID;
              /* etc */
          }
          

          现在您的数组已填充,并且没有堆分配,所以没问题。

          编辑:将局部变量作为地址返回是不好的。为什么?

          给定函数:

          int* foo() { 
              int x = 5;
              return &x; // Returns the address (in memory) of x
          } // At this point, however, x is popped off the stack, so its address is undefined
            // (Garbage)  
          
          // So here's our code calling it
          int *x = foo(); // points to the garbage memory, might still contain the values we need
          // But what if I go ahead and do this?
          int bar[100];        // Pushed onto the stack
          bool flag = true;    // Pushed onto the stack
          std::cout << *x << '\n'; // Is this guaranteed to be the value we expect?
          

          总的来说,风险太大。不要这样做。

          【讨论】:

            猜你喜欢
            • 2021-11-11
            • 1970-01-01
            • 2011-11-26
            • 2017-11-23
            • 2016-10-25
            • 2023-04-09
            • 2013-10-03
            相关资源
            最近更新 更多