在 C++ 中分配一個未初始化記憶體,然後讀取它,會讀取到這塊記憶體之前被使用所留下的值,這種現象我稱之為 flashback。
- 棧記憶體很容易出現這種現象,而且很容易觀測出某種規律。
for (int i = 0; i < 10; ++i) {
int a;
std::cout << a << " ";
a = i;
}
這段程式碼可能輸出
0 0 1 2 3 4 5 6 7 8
除了第一個 0,其餘的 0 1 2 3 4 5 6 7 8 都是 flashback 的結果
- 堆記憶體也會出現這種現象,但是很難觀測出規律。
struct A
{
int8_t m1[13];
int x;
};
for (int i = 0; i < 10; ++i) {
A* a = new A;
std::cout << a->x << " ";
a->x = i;
delete a;
}
std::cout << std::endl;
這段程式碼仍然可能輸出
0 0 1 2 3 4 5 6 7 8
除了第一個 0,其餘的 0 1 2 3 4 5 6 7 8 都是 flashback 的結果。
在實際的業務邏輯程式碼中,new 操作可能深埋在複雜程式碼之中,並且不同物件的 new 操作也會相互影響。
struct A
{
int8_t m1[13];
int x;
};
struct B
{
int8_t m1[13];
int x;
};
// cs1
A* a1 = new A;
a1->x = 66;
delete a1;
// cs2
/*
B* b1 = new B;
b1->x = 22;
delete b1;
*/
// cs3
A* a2 = new A;
delete a2;
std::cout << a1 << " " << a2 << " " << std::boolalpha << (a1 == a2) << " " << a2->x << std::endl;
這段程式碼可能輸出
0x1b05eb0 0x1b05eb0 true 66
成功觀測到了 flashback
把 cs2 的註釋解開,可能輸出
0x1718eb0 0x1718eb0 true 22
假設 cs2 的執行次數是隨機的,或者 b1->x = 22
的 22 是隨機的,並且只觀測 a1 和 a2 的關係,那麼觀測到 flashback 的次數也是隨機的。