1

可能重复:
可以在其范围之外访问局部变量的内存吗?

我在进行代码审查时看到了一个函数。

wchar_t* GetString(HINSTANCE hInstance, UINT SID)
{
    wchar_t buf[2048] = {0}; 
    LoadStringW(hInstance, SID, buf, sizeof(buf)/sizeof(wchar_t));
    return &buf[0];
}

void SomeWork()
{
    std::wstring str( GetString(hInst, 123) );
}

我认为buf应该在函数返回后立即销毁,因此指针 &buf[0] 可能无效。但它似乎工作正常,它是如何工作的?这是一个很好的设计吗?谢谢。

4

4 回答 4

5

我认为buf应该在函数返回后立即销毁,因此指针&buf[0]可能无效。

你的想法是 100% 正确的。

但它似乎工作正常,它是如何工作的?

它“有效”是因为保存buf数组的调用堆栈部分恰好在您实际使用它时没有写入其内容。

但这是未定义的行为,未定义的行为意味着任何事情都可能发生,这可能包括“召唤世界末日”和/或“工作正常”。这次你运气不错。

这是一个很好的设计吗?

不,这是一个糟糕的设计。幸运的是,它有一个简单的解决方法:只需返回std::wstring自身。

std::wstring GetString(HINSTANCE hInstance, UINT SID) 
{ 
    wchar_t buf[2048] = {0};  
    LoadStringW(hInstance, SID, buf, sizeof(buf)/sizeof(wchar_t)); 
    return std::wstring(buf);
} 

void SomeWork()   
{   
    std::wstring str = GetString(hInst, 123);   
}  

在这种情况下,所有非愚蠢的 C++ 编译器都会优化掉临时变量,因此该代码在实践中没有性能损失。事实上,即使您关闭所有优化,Visual C++ 编译器也会优化这种情况。

这种特定的优化称为返回值优化 (RVO)。如果您的 C++ 编译器即使设置为最高优化级别也不执行 RVO,请获取另一个。

于 2012-09-05T07:04:46.857 回答
3

不,这是严重的缺陷。返回本地对象的指针或引用是未定义的行为。这意味着一切都可能发生,包括开始第三次世界大战。在您的情况下,未定义的行为就像“工作正常”一样。侥幸。

于 2012-09-05T07:04:53.863 回答
1

从理论上讲,这是未定义的行为,这意味着任何事情都可能发生,包括有时(显然)正常工作。但是,未定义意味着无法保证它始终有效。

它现在似乎可以工作的原因是因为在GetString您阅读它时,所修改的内存尚未被正在执行的任何其他代码修改。换句话说,你很幸运。

于 2012-09-05T07:05:45.353 回答
1

我认为 buf 应该在函数返回后立即销毁,因此指针 &buf[0] 可能无效。

这是正确的。的寿命buf已经结束;它的内存可能会或可能不会变得不可访问或被其他对象重用;并且对它的任何访问都会给出未定义的行为。

但它似乎工作正常,它是如何工作的?

因为,在许多平台上,自动变量存储在堆栈中,并且函数的堆栈内存不会在函数返回时变得不可访问。这意味着(无效的悬空引用)变量通常仍会显示其旧值,直到您调用另一个函数并重用内存。

这是一个很好的设计吗?

当然不是。您依赖于未定义的行为,并且您的代码可能由于多种原因停止工作 - 更改平台或编译器,向您的代码添加另一个函数调用,或月亮的移动阶段。

我会返回一个std::wstring.

于 2012-09-05T07:13:22.420 回答