2017-06-25 10 views
3

私は標準の新しい...なぜ新しいものが最初に1040の余分なバイトを割り当てるのですか?

#include <iostream> 
#include <iomanip> 
#include <cstdint> 

// 
// Print a reserved block: its asked size, its start address 
//  and the size of the previous reserved block 
// 
void print(uint16_t num, uint16_t size_asked, uint8_t* p) { 
    static uint8_t* last = nullptr; 

    std::cout << "BLOCK " << num << ": "; 
    std::cout << std::setfill('0') << std::setw(2) << size_asked << "b, "; 
    std::cout << std::hex << (void*)p; 
    if (last != nullptr) { 
     std::cout << ", " << std::dec << (uint32_t)(p - last) << "b"; 
    } 
    std::cout << "\n"; 
    last = p; 
} 

int main(void) { 
    // Sizes of the blocks to be reserved and pointers 
    uint16_t s[8] = { 8, 8, 16, 16, 4, 4, 6, 6 }; 
    uint8_t* p[8]; 

    // Reserve some consecutive memory blocks and print 
    // pointers (start) and their real sizes 
// std::cout << "   size start prev.size\n"; 
// std::cout << "-----------------------------------\n"; 
    for(uint16_t i = 0; i < 8; ++i) { 
     p[i] = new uint8_t[s[i]]; 
     print(i, s[i], p[i]); 
    } 

    return 0; 
} 

を使用してメモリを割り当てるとき、アライメントの動作方法を示すために、この簡単なテストプログラムを作成していた。しかし、私はプログラムを実行したときに、私はこの奇妙な振る舞いを見つけた:

[memtest]$ g++ -O2 mem.cpp -o mem 
[memtest]$ ./mem 
BLOCK 0: 08b, 0xa0ec20 
BLOCK 1: 08b, 0xa0f050, 1072b 
BLOCK 2: 16b, 0xa0f070, 32b 
BLOCK 3: 16b, 0xa0f090, 32b 
BLOCK 4: 04b, 0xa0f0b0, 32b 
BLOCK 5: 04b, 0xa0f0d0, 32b 
BLOCK 6: 06b, 0xa0f0f0, 32b 
BLOCK 7: 06b, 0xa0f110, 32b 

ご覧のように、newによって割り当てられた2番目のブロックは、次の32bのメモリ割り当てアドレスではなく、遠く離れています(1040バイト離れた場所)。これは十分に奇数でない場合、テーブルのヘッダをプリントアウト2のstd :: COUT行のコメントを外し、この結果をもたらす:

[memtest]$ g++ -O2 mem.cpp -o mem 
[memtest]$ ./mem 
      size start prev.size 
----------------------------------- 
BLOCK 0: 08b, 0x1f47030 
BLOCK 1: 08b, 0x1f47050, 32b 
BLOCK 2: 16b, 0x1f47070, 32b 
BLOCK 3: 16b, 0x1f47090, 32b 
BLOCK 4: 04b, 0x1f470b0, 32b 
BLOCK 5: 04b, 0x1f470d0, 32b 
BLOCK 6: 06b, 0x1f470f0, 32b 
BLOCK 7: 06b, 0x1f47110, 32b 

これは、通常予想される結果です。最初に走ったときに新しいことが変わったのは何故ですか?私はg ++(GCC)7.1.1 20170516を使用しています。最適化せずにコンパイルしても同じ結果になります。

+0

これはヒープですが、起動すると(メインの前に)断片化される可能性があります。「新」はおそらくいくつかの穴を埋めるでしょう。あなたは、明確な結論に達するために、そのスタートアップコードをトレースする必要があります。 –

+3

newによって割り当てられたメモリブロックは連続しているとは言えません。実際には、あなたが新しいものから戻ってくるアドレスはそうでないことがかなり保証されています。 –

+4

プログラムは未定義のビヘイビアを呼び出します。■同じ配列の要素を指していないポインタは減算できません。とにかく、最初の例では、割り当て、印刷、割り当て、印刷を行い、2番目の例はprint、allocate、print、allocateを行います。これは、 'operator <<'の最初の呼び出しがバッファなど)。 –

答えて

6

あなたのプログラムは、ちょうどいくつかのメモリ割り当てをするだけではないということに驚いています。

std::cout << "BLOCK " << num << ": "; 

あなたのプログラムはまた、内蔵のstd::streambufを利用して、std::coutにフォーマットされた出力を生成します。

最初の出力がstd::coutになると、std::streambufに1024バイトのバッファが割り当てられることは明らかです。これはあなたの最初のnew配分の後、そしてあなたの2番目の配当の前に起こります。バッファは、初めて使用されるときに一度だけ割り当てられる必要があります。

内部メモリの割り当ての詳細は、実装定義が高度であることは言うまでもないが、これはあなたの場合に最も可能性の高い説明であると思われる。

+0

はい、私はそれを考慮しませんでしたが、あなたはかなり正しいようです。あなたの素早い答えをありがとう:) – ronaldo

0

<<のストリームオーバーロードを含む多くのものは、 `new 'が使用するものと同じヒープを使用します。

いくつかの実行時には、バッファオーバーランの嫌がられを試しているクラッカーの速度を落とす方法として、ランダム化が割り当てられます。

1

newは、メモリブロックが連続していることを保証しません。

また、あなたはおそらく起こっている可能性の高いFragmentationについて読むことをお勧めします。システムは穴を埋めようとします。

関連する問題