You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 09:53:12