我正在创建这个简单的测试程序,以演示使用标准new ...
分配内存时对齐的工作方式#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分配的第二个块不是在下一个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 :(得分:6)
你会惊讶地发现你的程序不仅仅是做一些内存分配。
std::cout << "BLOCK " << num << ": ";
您的程序还会生成格式化输出到std::cout
,并使用其内置的std::streambuf
。
显而易见的是,std::cout
的第一个输出为内部std::streambuf
分配了1024字节的缓冲区。这是在您第一次new
分配之后,在第二次分配之前发生的。缓冲区只需要在第一次使用时分配一次。
虽然不言而喻,内部内存分配的细节是高度实现定义的,但这似乎是您案例中最可能的解释。
答案 1 :(得分:1)
new
并不保证内存块是连续的。
此外,我建议您阅读Fragmentation,这很可能是正在发生的事情,系统会尝试填补漏洞。
答案 2 :(得分:0)
很多事情,包括<<
的流重载,都使用与`new&#39;相同的堆。使用。
一些运行时间将分配随机化,以减缓尝试缓冲区溢出恶作剧的破解程序。