C++ 未初始化記憶體出現 flashback

mkckr0發表於2021-12-22

在 C++ 中分配一個未初始化記憶體,然後讀取它,會讀取到這塊記憶體之前被使用所留下的值,這種現象我稱之為 flashback。

  1. 棧記憶體很容易出現這種現象,而且很容易觀測出某種規律。
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 的結果

  1. 堆記憶體也會出現這種現象,但是很難觀測出規律。
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 的次數也是隨機的。

相關文章