【问题标题】:deallocation after scope has ended [duplicate]范围结束后解除分配[重复]
【发布时间】:2012-10-20 08:37:01
【问题描述】:

可能重复:
Returning the address of local or temporary variable
Can a local variable’s memory be accessed outside its scope?

假设我们有以下代码

int *p;//global pointer
void foo() {
  int a=10;//created in stack
  p = &a;
}//after this a should be deallocated and p should be now a dangling pointer
int main() {
  foo();
  cout << *p << endl;
}

我想知道为什么会这样……应该是段错误!

OK 未定义的行为似乎合适..你能再次验证它吗?我试图在下面的代码中模拟上面的东西,但现在它给出了 SIGSEGV。

int main() {
    int *p=NULL;
    {
        int i=5;
        int *q = &i;
        p=q;
        delete q;//simulates the deallocation if not deallocated by the destructor..so p becomes a dangling pointer
    }
    cout << *p << endl;
}

【问题讨论】:

  • 未定义的行为意味着任何事情都可能发生,包括看起来有效。
  • 也许这就是全局变量不好的原因之一
  • 清理可能不会立即进行。因此,这并不意味着一旦函数执行完成,存在“a”的堆栈就会展开。这取决于执行环境,但我们唯一可以确定的是“这样做不安全”。
  • @alestanis:不,这不是全局变量不好的原因之一,因为如果指针作为返回值传递也会发生同样的情况。当然,这并不意味着全局变量就不错。这只是意味着这不是一个例子。
  • @celtschk 是的,但是那里有一个很大的警告

标签: c++ memory-management segmentation-fault lifetime


【解决方案1】:

您编写了一个具有未定义行为的程序。 UB 并不意味着段错误,它意味着任何事情都可能发生。你运行了你的程序,但发生了一些事情,这不是你所期望的。故事的寓意是不要用 UB 编写程序,如果你这样做,事情就会变得非常难以理解。

【讨论】:

    【解决方案2】:

    您不应该使用全局变量。在您的示例中,您所做的是未定义的行为

    如果你做过类似的事情

    int* foo() {
      int a = 10; //created in stack
      return &a;
    }
    
    int main() {
      int *p = foo();
      cout << *p << endl;
    }
    

    你至少会收到警告:

    prog.cpp:5: warning: address of local variable ‘a’ returned
    

    您应该非常认真地对待这些警告。

    【讨论】:

      【解决方案3】:

      当您在函数中定义变量时,它将在堆栈上分配,并且当您从该函数返回时,该变量的析构函数将被自动调用(如果类型具有任何析构函数),但与堆分配的对象不同,堆栈的内存将此时未释放:

      void test1() {
          // assume on start of this function top of stack is 0x100
          int a = 10; // push 10 on to of stack and increase top of stack
                      // Now top of stack is 0x100 + sizeof(int)
          a = 20;     // when you do this you are actually changing variable on stack
          p = &a;     // Get address of variable in stack!
                      // So p is now 0x100
          // hidden return will restore top of stack to 0x100, pop return address, ...
      }
      int test2() {
          // again top of stack is 0x100
          int a2;     // a2 is in same address as a in previous function!
          a2 = 100;   // so this will overwrite value that pointed by p
                      // because it point to same location
          return (int)&a2;
      }
      
      void main() {
          test1();
          test2();
          std::cout << *p << std::endl;
      }
      

      但如果你的类型是一个有析构函数的类并且在使用前需要构造,那么你甚至可能会收到像分段错误这样的异常

      【讨论】:

      • 编译器不必按照您描述的方式工作。由于 OP 的错误是对他的编译器行为方式的错误期望,我认为给出一个具有另一组可能或可能不是真的期望的答案是没有帮助的。
      • @john 你调用函数变量,一个栈变量,它总是分配在栈上,这是栈的本质。如果您反对它,请找到一个执行除此之外的其他操作的编译器。但也许你对无效期望是正确的(它总是这样,但可能以无效的方式使用)
      • 不一定,例如编译器可以将变量a2 放入寄存器中。我刚刚在我的编译器(Visual C++,发布模式)上运行了你的程序,它打印出 20,而不是 100。所以你看到你所描述的只是一种可能性,它不必像那样工作。
      • 好的,你说得对,我只是忘了优化,但我想解释一下为什么它的程序不会产生错误。 UB仍然是我们不知道的某种行为!所以我想解释一下这种行为
      猜你喜欢
      • 1970-01-01
      • 2013-09-07
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-19
      • 1970-01-01
      相关资源
      最近更新 更多