malloc的默認行為
大家都知道C++中可以直接調用malloc請求內存被返回分配成功的內存指針,該指針指向的地址就是分配得到的內存的起始地址。比如下面的代碼
int main()
{
void *p = malloc(1024);
printf("0x%p\n", p);
free(p);
}
請求了一個大小為1024的內存塊並打印出來,一切都很完美。
我們看看這塊內存的地址。
可以看到,在64bit機器上,malloc默認行為會將分配的地址以16-byte對齊,如果我們想改變這種默認行為,提供32-byte或者64-byte對齊,應該怎么做呢?
實現aligned_malloc
源代碼
從C++17開始,可以使用aligned_alloc函數達到這個目的,但是如果使用較老的C++版本,如C++14,C++11,我們需要手動寫一個實現。
話不多說,先貼代碼如下,aligned_malloc和aligned_free,需要配合使用,否則會有內存泄漏問題。
#include <memory>
void* aligned_malloc(size_t size, size_t alignment)
{
size_t offset = alignment - 1 + sizeof(void*);
void * originalP = malloc(size + offset);
size_t originalLocation = reinterpret_cast<size_t>(originalP);
size_t realLocation = (originalLocation + offset) & ~(alignment - 1);
void * realP = reinterpret_cast<void*>(realLocation);
size_t originalPStorage = realLocation - sizeof(void*);
*reinterpret_cast<void**>(originalPStorage) = originalP;
return realP;
}
void aligned_free(void* p)
{
size_t originalPStorage = reinterpret_cast<size_t>(p) - sizeof(void*);
free(*reinterpret_cast<void**>(originalPStorage));
}
int main()
{
void * p = aligned_malloc(1024, 64);
printf("0x%p\n", p);
aligned_free(p);
return 0;
}
添加一個測試程序,
#include <assert.h>
void TestAlignedMalloc()
{
const int size = 100;
const int alignment = 64;
void* testArray[size];
for (int i = 0; i < size; ++i)
{
void * p = aligned_malloc(1024, alignment);
assert((reinterpret_cast<size_t>(p) & (alignment - 1)) == 0);
printf("0x%p\n", p);
testArray[i] = p;
}
for (int i = 0; i < size; ++i)
{
aligned_free(testArray[i]);
}
}
int main()
{
TestAlignedMalloc();
return 0;
}
看看結果,
分配的內存地址都是以64-byte為邊界,並且分配的內存最后也被成功釋放了,函數是正確的。
源代碼說明
本小段主要向不大了解解決思路的小伙伴做一些簡單解釋,程序大佬可以一笑而過哈。
首先我們要明確我們的解決方案,既然malloc分配的指針地址不能達到我們想要的字節對齊效果,我們就自己來調整這個指針。所以我們的做法是
- 比用戶實際需要的多分配一些內存,多分配的部分等於對齊大小減一再加上指針大小。加上對齊大小減一很好理解,是為了之后的對齊做准備,而加上指針大小是為了之后有空間保存原始指針,對應分配函數中的前2行
- 在malloc返回的原始指針的基礎上,加上指針大小,再對齊(采用的方法就是加上對齊大小減1再做位運算),這個運算結果就是我們想要的對齊后的指針,也是我們返回給用戶的指針,對應分配函數中的3~5行
- 我們還需要保存malloc返回的原始指針,否則free的時候會出問題。這時我們之前多分配的一個指針大小就有用武之地了,保存原始指針在那個地址,分配函數的最后幾行就在做這個事
- 當free的時候,我們知道原始指針存放在我們使用的指針的前一個指針大小偏移的內存里面,通過一些運算取得這個內存地址,再根據里面存放的原始指針調用free完成內存釋放
這就是在C++中手動實現aligned_malloc的方法,希望大家在使用較老版本的C++的時候,有需要可以用上。如果使用的版本是C++17以上,那么還是推薦使用系統自帶的方法。