【问题标题】:Is it OK to reference an out-of-scope local variable within the same function?可以在同一个函数中引用超出范围的局部变量吗?
【发布时间】:2014-01-28 20:05:50
【问题描述】:

在这段代码中,我引用了局部变量b,即使它超出了范围。但我是在同一个函数中做的,所以它可能还在堆栈上,对吧?我运行了这个程序,它可以工作,但我想知道它是否可以保证在所有实现上都可以工作。

#include <iostream>

void main()
{
    int* a;
    {
        int b = 5;
        a = &b;
    }
    std::cout << *a;
}

【问题讨论】:

  • 这就是悬空指针的定义。
  • 你的例子太简单了。在 cout 语句之前的一个更现实的示例中,您可以做很多事情,这可能会改变堆栈,破坏值。非常糟糕的做法。
  • 不要误以为它“可以”工作的安全感。如果 b 是一个分配资源(内存、打开文件等)的对象,并且它的 dtor 释放了这些资源(它应该),那么 *a 引用的“对象”的状态将是真实的垃圾。
  • 如果你想保证某些东西会起作用,你应该遵守语言规则。这显然没有做到这一点。
  • 好吧,我没看清楚。在我看来,是的,b 的析构函数已经被调用了,但是编译器如何将该堆栈位置用于任何其他目的?它不是在进入函数时“分配”位置,在离开函数时释放它吗?

标签: c++ pointers scope


【解决方案1】:

不,这保证有效。 a 一旦退出内部作用域就会悬空,因此任何取消引用它都会导致未定义的行为,并且没有任何保证。

【讨论】:

  • 对语言律师的说明:不是取消引用 a 会导致 UB,而是 lvalue-to-rvalue 转换。声明sizeof(*a); 定义明确。
  • @MWid 是的。再一次,sizeof 的操作数未计算。我试图想办法在不涉及太多晦涩细节的情况下改写它,但我做不到。
【解决方案2】:

这里的问题不是b 超出范围。就是b生命周期已经结束。范围是关于对象的名称已知的位置。生命周期是关于对象存在(在计算模型中)的时间。

从技术上讲,当您使用 *a 引用对象 b 时,它并不存在。用于表示它的字节可能碰巧在内存中仍未更改,并且使用*a 访问它们有时可能会起作用,尤其是在未启用优化的情况下,但这是未定义的行为。

一个对象仍然可以被访问,即使它的名字不在范围内。这是一个在其生命周期内可访问的对象的示例,即使它不在范围内:

void foo(void)
{
    int b;
    bar(&b);
}

在这段代码中,函数bar 可以访问b,即使它在foo 中看不到b 的名称。尽管控制权离开了创建b 的块,但块的执行只是暂停,而不是终止。所以b 继续存在,即使函数bar 正在执行。所以b 将超出范围,但访问将在其生命周期内。

【讨论】:

    【解决方案3】:

    规范说

    具有自动存储持续时间 (3.7.3) 的每个对象的实例与其块中的每个条目相关联。这样的对象在块执行期间存在并保留其最后存储的值,同时块被挂起(通过调用函数或接收信号)。

    b 是一个具有自动存储期限的对象。因此,当您在对象的块之外时,该对象不再存在。你可以之前它,我相信,但如果你跳到它的块之外,就不行。这太过分了。

    【讨论】:

    • "jump before it"这里的意思是从块外跳到块内(在对象上声明之前)然后使用对象?我理解正确吗?
    【解决方案4】:

    我整理了这个示例来帮助演示典型的基于帧的堆栈中的内存分配发生了什么。请参阅:Is the stack variable deallocated when it goes out of scope?。该问题已作为重复问题关闭。

    testerA 中,我们捕获本地 变量a 的地址以供将来使用。我们对testerB 做了类似的事情,只是这次我们捕获了second 整数变量b 的地址。请注意,我们用值 44 覆盖了 a 的内存 - 在此函数中将其称为 f

    您可以在调用函数后立即检查这些值以观察发生了什么。我们实际上是在重复使用相同的内存地址 - 但不能保证数据的格式来自先前的函数调用。

    检查堆栈

    #include <iostream>
    
    int* aPtr = nullptr;
    int* bPtr = nullptr;
    
    int testerA()
    {
        int a = 3;
        aPtr = &a;
    }
    
    int testerB()
    {
        int f = 44;
        int b = 5;
        bPtr = &b;
    }
    
    int main()
    {
        using namespace std;
        testerA();
        int test1 = *aPtr;
        testerB();
        int test2 = *aPtr;
        int test3 = *bPtr;
    
        cout << "A: " << test1 << " -> " << test2 << endl;
        cout << "B: " << test3 << endl;
    
        cout << "aPtr: " << hex << aPtr << endl;
        cout << "bPtr: " << hex << bPtr << endl;
    
        return 0;
    }
    

    样本输出

    A: 3 -> 44
    B: 5
    aPtr: 0x7fff9e58609c
    bPtr: 0x7fff9e586098
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-12
      • 1970-01-01
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-24
      相关资源
      最近更新 更多