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

C语言哈希表实现中lookup_size函数触发Seg Fault问题排查

问题背景

实现动态内存分配器作业,需要自主实现m61_malloc和m61_free函数,要求每次分配释放都更新统计结构体里的active_size(当前活跃分配总大小),因此配套实现了一个哈希表,存储分配指针和对应块大小,free时通过lookup_size查询指针对应大小来更新统计值。目前运行时在lookup_size的return tmp->payload;行触发段错误。
完整实现代码如下:

#define M61_DISABLE 1
#include "m61.hh"
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cinttypes>
#include <cassert>

#define TABLE_SIZE 10

static m61_statistics stat_count = {0, 0, 0, 0, 0, 0, 0, 4294967295};

typedef struct ht_item {
    void* address;
    unsigned long long payload;
    struct ht_item* next;
} ht_item;

ht_item* hash_table[TABLE_SIZE];

int hash_func(void* address) {
    uintptr_t hash_value = (uintptr_t) address % TABLE_SIZE;
    return hash_value;
}

void init_hash_table() {
    for (int i = 0; i < TABLE_SIZE; i++) {
        hash_table[i] = NULL;
    }
}

bool insert_item(ht_item* item) {
    if (item == NULL) {
        return false;
    }
    int index = hash_func(item->address);
    item->next = hash_table[index];
    hash_table[index] = item;
    return true;
}

unsigned long long lookup_size(void* p) {
    if (p == NULL) {
        return 0;
    }

    int index = hash_func(p); 
    ht_item* tmp = hash_table[index];
    while (tmp != NULL && tmp->address != p) {
        tmp = tmp->next;
    }
    return tmp->payload;
}

void* m61_malloc(size_t sz, const char* file, long line) {
    (void) file, (void) line;
    if (!base_malloc(sz)){
        ++stat_count.nfail;
        stat_count.fail_size += sz;
    }

    else {
        ++stat_count.nactive;
        ++stat_count.ntotal;
        stat_count.active_size += sz;
        stat_count.total_size += sz;

        init_hash_table();
        void* p = base_malloc(sz);
        ht_item* malloc_data = (ht_item*) malloc(sizeof(ht_item));
        malloc_data->address = p;
        malloc_data->payload = sz;
        malloc_data->next = NULL;
        insert_item(malloc_data);
    }
    return base_malloc(sz);
}

void m61_free(void* ptr, const char* file, long line) {
    (void) file, (void) line;
    if (ptr){
        --stat_count.nactive;
        stat_count.active_size -= lookup_size(ptr);
    }
    base_free(ptr);
}
故障原因

段错误的直接原因是遍历哈希链表到末尾tmp == NULL时,仍然访问tmp->payload,根本原因是代码存在多个逻辑错误,导致传入lookup_size的指针根本不在哈希表中:

  • m61_malloc重复调用底层分配函数,存入哈希表的指针和返回给用户的指针不是同一个
    代码里先后三次调用base_malloc(sz):第一次用来判断分配是否成功,第二次的结果存入哈希表,第三次的结果返回给调用方。用户拿到的是第三次分配的指针,free时传入这个指针,哈希表里根本没有对应记录,遍历到最后tmp为NULL,触发段错误。
  • 每次malloc都重置哈希表
    init_hash_table()被放在malloc成功分支里,每次新分配都会把之前所有的哈希记录全部清空,之前分配的指针记录全部丢失,free时自然查不到。这个初始化函数全局只需要执行一次。
  • 哈希节点分配函数用错
    分配哈希表节点时调用了malloc而非base_malloc,要么会递归调用自己实现的m61_malloc导致死循环,要么会和底层分配逻辑冲突。
  • free时未删除哈希表项
    内存释放后没有删除哈希表中对应的记录,会造成内存泄漏,后续如果新分配的内存复用同一地址,还会查询到错误的大小值。
修复方案
  1. 把哈希表初始化逻辑移到全局初始化流程,保证只执行一次:
// 程序启动时自动执行,初始化哈希表
__attribute__((constructor))
void global_init() {
    init_hash_table();
}
  1. 重写m61_malloc逻辑,只调用一次base_malloc,用base_malloc分配哈希节点:
void* m61_malloc(size_t sz, const char* file, long line) {
    (void) file, (void) line;
    void* ret = base_malloc(sz);
    if (!ret) {
        stat_count.nfail++;
        stat_count.fail_size += sz;
        return NULL;
    }
    // 更新统计值
    stat_count.nactive++;
    stat_count.ntotal++;
    stat_count.active_size += sz;
    stat_count.total_size += sz;
    // 分配哈希节点,存入记录
    ht_item* node = (ht_item*)base_malloc(sizeof(ht_item));
    node->address = ret;
    node->payload = sz;
    node->next = NULL;
    insert_item(node);
    return ret;
}
  1. 修改lookup_size,增加查找失败的判断逻辑:
unsigned long long lookup_size(void* p) {
    if (!p) return 0;
    int idx = hash_func(p);
    ht_item* tmp = hash_table[idx];
    while (tmp) {
        if (tmp->address == p) {
            return tmp->payload;
        }
        tmp = tmp->next;
    }
    // 查找失败返回特殊标记值,对应非法指针free场景
    return (unsigned long long)-1;
}
  1. 重写m61_free逻辑,增加非法指针判断,释放后删除哈希节点:
void m61_free(void* ptr, const char* file, long line) {
    (void) file, (void) line;
    if (!ptr) return;
    unsigned long long sz = lookup_size(ptr);
    if (sz == (unsigned long long)-1) {
        // 这里可以按作业要求打印"invalid free"类错误信息
        return;
    }
    // 更新统计值
    stat_count.nactive--;
    stat_count.active_size -= sz;
    // 删除哈希表对应节点
    int idx = hash_func(ptr);
    ht_item* tmp = hash_table[idx];
    ht_item* prev = NULL;
    while (tmp) {
        if (tmp->address == ptr) {
            if (prev) {
                prev->next = tmp->next;
            } else {
                hash_table[idx] = tmp->next;
            }
            base_free(tmp);
            break;
        }
        prev = tmp;
        tmp = tmp->next;
    }
    // 释放用户内存
    base_free(ptr);
}

内容的提问来源于stack exchange,提问作者azimr24

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:36:26