下面的問題是從一個巨大的項目中提煉出來的,也是我能夠想到的問題的最小例子。尋找堆棧損壞錯誤的解釋
我知道,從std::string
得到的結果很糟糕,它已經在我們的代碼庫中發生了變化,但我試圖理解這裏隱藏的內容。
的代碼在釋放模式崩潰上的Visual C++ 2017
Microsoft Visual Studio Community 2017
Version 15.2 (26430.14) Release
Visual C++ 2017 00369-60000-00001-AA257
只(以速度優化)。沒有速度優化,它不會在發佈模式下崩潰。
#include <string>
#include <string_view>
#include <vector>
struct my_string : public std::string
{
__declspec(noinline)
my_string::my_string(const std::string_view& str) :
std::string(str.data(), str.size())
{}
template <typename T>
my_string& arg(T)
{
return *this;
}
};
struct my_string_view : public std::string_view
{
my_string_view(const std::string_view::value_type* val) :
std::string_view(val) {}
template <typename... PARAMS>
my_string arg(PARAMS&&... prms) {
return my_string(*this).arg(std::forward<PARAMS>(prms)...);
}
};
template <typename T>
struct basic_color
{
T r, g, b, a;
basic_color() : r(0), g(0), b(0), a(255) {}
template <typename U>
explicit basic_color(const basic_color<U>& c) :
r(c.r), g(c.g), b(c.b), a(c.a)
{}
};
using color = basic_color<std::uint8_t>;
using float_color = basic_color<float>;
__declspec(noinline)
void change_float_color(float_color& color)
{
color.r = 0.1f;
}
int main()
{
std::vector<float_color> colors = { {} };
float sum = 0;
for (std::uint32_t i = 0; i < 1; ++i)
{
float_color fc;
change_float_color(fc);
color c(fc);
std::vector<std::string> msgs;
msgs.push_back(my_string_view("").arg(c.r));
msgs.push_back(my_string_view("").arg(c.g));
sum += fc.b - colors[i].b;
}
return static_cast<int>(sqrt(sum));
}
在Visual Studio中的錯誤是這樣的(看看的msgs
和colors
破碎的尺寸在底部):
我的猜測是,std::vector<std::string>::push_back(std::string&&)
與通話my_string
有問題(切片行爲)。但是如何破壞堆棧(或堆棧指針)呢?
有沒有人有一個想法可能發生在這裏或我如何能找出?
Here是我的項目,以防萬一任何人有興趣重現問題。
使用調試器,數據斷點是這裏選擇的武器。 –
「因爲std :: string沒有虛擬析構函數,所以給push_back賦予的r值被切片了。」切片與虛擬析構函數無關。無論如何,這些值都會被切片。 「因此,派生類(my_string)的析構函數永遠不會被調用,一些垃圾仍然在堆棧中」這沒有任何意義。 'sizeof(my_string)== sizeof(std :: string)'這不會使一點區別。 「看看破損的大小」編譯器可以隨意對這些對象做任何事情,因爲它們將永遠不會再被使用。 –
>我的第一個猜測是,由於std :: string沒有虛擬析構函數,因此賦予push_back的r值被切片。因此,無論析構函數是否被調用,派生類(my_string)的析構函數都不會被調用<爲這個類分配的內存(在這種情況下爲堆棧)。而且,'〜my_string' **被**調用,因爲當'my_string'被複制到'std :: string'中時發生切片,並且原始字符串不是,也不能被切片。 – Hedede