C++内存池实现出现free()无效指针错误,求排查及对齐优化建议
我正在学习Stroustrup所著《Programming Principles and Practice Using C++》一书,在第25章嵌入式系统编程章节中,为避免内存碎片,书中介绍了内存池(Pool)和栈两种数据结构。尝试实现内存池时遇到运行错误,同时希望优化内存对齐的实现逻辑。
错误原因分析
1. 错误使用delete释放placement new创建的对象
在Pool::free函数中调用delete p;是核心错误:p是通过placement new在malloc分配的内存块上构造的对象,delete会同时触发对象析构并尝试释放底层内存,但这块内存属于内存池的统一分配块,并非来自自由存储区。直接delete会导致free()接收到非法指针,触发SIGSEGV信号。
2. 内存对齐计算逻辑错误
原代码中unit = sizeof(Item<T>) + sizeof(Item<T>) % alignof(Item<T>);的计算方式有误:如果sizeof(Item<T>)已经是alignof(Item<T>)的整数倍,会额外添加多余字节,导致内存块总大小超出预期,可能引发越界访问。正确的向上取整对齐公式应为(size + align - 1) & ~(align - 1)。
修复后的代码示例
#include <iostream> #include <stdexcept> #include <vector> #include <cstdlib> template<typename T> struct Item { T value; Item* next; }; template<typename T, unsigned long N> class Pool { public: using size_type = unsigned long; Pool() : sz{N} { const auto item_size = sizeof(Item<T>); const auto align = alignof(Item<T>); // 正确的向上对齐计算:确保每个Item<T>的起始地址符合对齐要求 const auto unit = (item_size + align - 1) & ~(align - 1); // 使用aligned_alloc直接分配对齐后的内存块(C11及以上支持) avail = static_cast<Item<T>*>(std::aligned_alloc(align, unit * N)); if (!avail) throw std::runtime_error("Failed to allocate pool memory"); for (size_type i = 0; i < N; ++i) { auto p = avail + i; auto n = (i == N - 1) ? nullptr : avail + (i + 1); p->next = n; } } // 析构函数:释放malloc分配的内存池 ~Pool() { std::free(avail); } // 禁止拷贝和移动,避免内存重复释放 Pool(const Pool&) = delete; Pool& operator=(const Pool&) = delete; Pool(Pool&&) = delete; Pool& operator=(Pool&&) = delete; Item<T>* get() { if (sz == 0) throw std::runtime_error("no available space!"); auto next = avail->next; // 使用placement new构造Item<T>对象 auto p = new (avail) Item<T>; avail = next; p->next = nullptr; --sz; return p; } void free(Item<T>* p) { if (p == nullptr) return; // 手动调用析构函数,而非delete p->~Item<T>(); // 将对象重新链入空闲链表 p->next = avail; avail = p; ++sz; } size_type available() const { return sz; } private: Item<T>* avail; size_type sz; }; // 500 byte object struct Five { char chunk[500]; ~Five() { std::cout << "Five destructor called" << '\n'; } }; int main() { try { constexpr int test_size = 2; Pool<Five, test_size> fh_pool; std::cout << "Pool allocation (reverse deallocate):\n"; std::vector<Item<Five>*> vpf1(test_size); for (auto i = 0; i < test_size; ++i) vpf1[i] = fh_pool.get(); for (auto i = test_size - 1; i >= 0; --i) { std::cout << vpf1[i] << '\n'; fh_pool.free(vpf1[i]); } return 0; } catch(std::exception& e) { std::cerr << "Exception: " << e.what() << '\n'; return 1; } catch(...) { std::cerr << "Unknown exception\n"; return 2; } }
内存对齐优化方法
1. 使用std::aligned_alloc(C11+)
直接通过std::aligned_alloc(alignment, size)分配符合指定对齐要求的内存,无需手动计算对齐后的大小,简化代码逻辑。注意:size必须是alignment的整数倍。
2. 使用std::aligned_storage(C++11+)
对于需要存储未构造对象的场景,std::aligned_storage可以生成一个符合对齐要求的类型,适合作为内存池的底层存储,无需手动调用malloc/free:
template<typename T, unsigned long N> class Pool { private: using Storage = typename std::aligned_storage<sizeof(Item<T>), alignof(Item<T>)>::type; Storage storage[N]; // 后续逻辑将storage的地址转换为Item<T>*使用 };
3. 利用alignas显式指定对齐(C++11+)
使用alignas说明符直接指定结构体或变量的对齐要求,确保Item<T>的对齐符合需求:
template<typename T> struct alignas(alignof(T)) Item { T value; Item* next; };
通常Item<T>的自然对齐已经满足T的要求,但若Item*的对齐要求高于T,显式指定会更稳妥。
内容的提问来源于stack exchange,提问作者a1mersnow

