Возможный дубликат:
Можно ли получить доступ к памяти локальной переменной вне ее области?
Я видел функцию, когда делал некоторый обзор кода.
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 [0] может быть недействительным.
Но, кажется, работает нормально, как это работает?
И это хороший дизайн?
Благодарю.
я думал ЬиЕ должен быть уничтожен сразу после возврата функции, поэтому
указатель&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, даже если он установлен на самом высоком уровне оптимизации, получите другой.
Нет, это серьезный недостаток. Возвращение указателя или ссылки на локальный объект неопределенное поведение. Это означает, что все может произойти, включая начало Второй мировой войны. В вашем случае неопределенное поведение точно так же, как оно «работает нормально». По счастливой случайности
Теоретически, это неопределенное поведение, что означает что-нибудь может случиться, в том числе иногда работать (по-видимому) правильно. Однако быть неопределенным означает, что нет способа гарантировать, что он всегда будет работать.
Причина, по которой он работает сейчас, заключается в том, что память, измененная GetString
не был изменен каким-либо другим кодом, выполняемым к тому времени, когда вы его прочитали. Другими словами, вам повезло.
Я думал, что buf должен быть уничтожен сразу после возврата функции, поэтому указатель &buf [0] может быть недействительным.
Правильно. Время жизни buf
кончено; его память могла или не могла быть сделана недоступной или повторно использована для другого объекта; и любой доступ к нему дает неопределенное поведение.
Но, кажется, работает нормально, как это работает?
Поскольку на многих платформах автоматические переменные хранятся в стеке, а память стека функции не становится недоступной, когда функция возвращается. Это означает, что (недействительные свисающие ссылки на) переменные часто будут по-прежнему иметь свои старые значения, пока вы не вызовете другую функцию и память не будет повторно использована.
И это хороший дизайн?
Конечно, нет. Вы полагаетесь на неопределенное поведение, и ваш код может перестать работать по многим причинам — изменение платформы или компилятора, добавление еще одного вызова функции в ваш код или сдвиг фазы Луны.
Я бы вернул std::wstring
,