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

哈希表插入数据后打印异常问题排查与修复求助

问题分析与解决方案

你的哈希表出现数据丢失、重复显示的问题,核心原因是多个严重的语法错误、内存管理错误以及哈希逻辑错误,下面逐个拆解并给出修复方案:

1. 结构体定义完全错误

你的代码里把utilizador和Hash结构体的定义混在一起,这是严重的语法错误,编译器其实已经在悄悄报错了,导致结构体的内存布局完全混乱。

错误代码片段:

typedef struct utilizador{
 char nick[6];
 char nome[26];
} struct hash{
 int quantidade, table_size;
 utilizador **itens;
} Hash* criateHash(int table_size){
 // ... 函数内容
}

修复方式:
分开定义两个结构体,正确声明类型,同时补上你代码里用到但未定义的activo字段:

// 先定义用户结构体
typedef struct utilizador {
    char nick[6];
    char nome[26];
    bool activo; // 补上缺失的字段
} utilizador;

// 再定义哈希表结构体
typedef struct hash {
    int quantidade;
    int table_size;
    utilizador **itens;
} Hash;

2. 插入时复用了同一个临时内存块

你用全局数组ar_temp[1]存储每次输入的用户数据,然后直接把这个数组的指针赋值给哈希表条目:ha->itens[i] = a;。

这意味着所有哈希表的非空条目都指向同一块内存,每次输入新用户都会覆盖ar_temp的内容,最后所有显示的都是最后一次输入的用户信息(也就是zxcvb),之前插入的数据自然被覆盖看不到了。

修复方式:
每次插入时,为新用户分配独立的内存空间,复制输入的数据:

int insereHash(Hash* ha, utilizador *a, int table_size){
    // ... 省略前面的检查逻辑
    if (i != -1) {
        // 分配新内存存储用户数据
        utilizador *new_user = malloc(sizeof(utilizador));
        if (new_user == NULL) {
            printf("内存分配失败");
            return -1;
        }
        // 复制nick和nome到新内存,确保字符串终止
        strncpy(new_user->nick, a->nick, sizeof(new_user->nick)-1);
        new_user->nick[sizeof(new_user->nick)-1] = '\0';
        strncpy(new_user->nome, a->nome, sizeof(new_user->nome)-1);
        new_user->nome[sizeof(new_user->nome)-1] = '\0';
        new_user->activo = true;
        
        ha->itens[i] = new_user; // 指向新分配的内存
        ha->quantidade++;
        printf("%d %d ",check, i);
        printf("+ utilizador %s criado\n", ha->itens[i]->nick );
    }
    // ... 省略其他逻辑
}

3. 哈希冲突处理的线性探测逻辑错误

你的hash_function里的线性探测实现完全错误:

  • position = position + j会导致步长越来越大,跳过大量位置甚至越界;
  • position>table_size的判断错误,应该用position >= table_size,且需要取模回到哈希表范围内。

修复后的哈希函数:

int hash_function(int chave, int table_size, Hash *ha){
    int position = chave % table_size;
    // 处理负数取模的情况
    if (position < 0) position += table_size;
    
    // 线性探测:步长为1,循环查找空位置
    for (int j = 0; j < table_size; j++) {
        int current_pos = (position + j) % table_size;
        if (ha->itens[current_pos] == NULL) {
            return current_pos;
        }
    }
    return -1; // 哈希表已满
}

4. 查找函数的空指针访问错误

searchHash里直接访问ha->itens[position]->nick,但如果ha->itens[position]是NULL,这会导致空指针解引用,触发未定义行为,可能导致错误的查找结果甚至崩溃。

修复后的查找函数:

int searchHash(Hash* ha, utilizador *a, int table_size){
    int chave = string_value(a->nick);
    int position = chave % table_size;
    if (position < 0) position += table_size;
    
    for (int j = 0; j < table_size; j++) {
        int current_pos = (position + j) % table_size;
        if (ha->itens[current_pos] == NULL) {
            continue; // 空位置,继续查找
        }
        // 使用标准库strcmp替代错误的自定义比较函数
        if (strcmp(ha->itens[current_pos]->nick, a->nick) == 0) {
            return current_pos;
        }
    }
    return -1; // 未找到
}

5. 其他次要问题修复

  • 字符串比较函数:删除你错误的str_compare,直接用标准库strcmp,它会正确比较整个字符串;
  • string_value溢出问题:用逐步累加替代pow,避免浮点运算和整数溢出:
    int string_value(char *string){
        int valor = 0;
        int tam = strlen(string);
        for(int i=0; i< tam; i++){
            valor = valor * 31 + (int)string[i];
            valor = valor % 1000000; // 防止溢出
        }
        return valor;
    }
    
  • 打印哈希表的空指针处理:打印前先判断条目是否为NULL,避免空指针访问:
    for(int g=0; g<table_size; g++){
        if(ha->itens[g] != NULL){
            printf("%s ", ha->itens[g]->nick);
        } else {
            printf("(null) ");
        }
    }
    
  • 主循环初始化:初始化comando为空字符串,避免第一次循环读取未初始化的垃圾数据;
  • input函数的sscanf格式:修正指针错误和溢出问题,改为sscanf(aux, "%s %s %25[^\n]", comando, a->nick, a->nome);。

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

// 定义用户结构体
typedef struct utilizador {
    char nick[6];
    char nome[26];
    bool activo;
} utilizador;

// 定义哈希表结构体
typedef struct hash {
    int quantidade;
    int table_size;
    utilizador **itens;
} Hash;

Hash* createHash(int table_size){
    Hash* ha = (Hash*)malloc(sizeof(Hash));
    if(ha == NULL) return NULL;

    ha->table_size = table_size;
    ha->itens = (utilizador **) malloc(table_size * sizeof(utilizador *));
    if(ha->itens == NULL) {
        free(ha);
        return NULL;
    }

    ha->quantidade = 0;
    for(int i=0; i< ha->table_size; i++){
        ha->itens[i] = NULL;
    }
    return ha;
}

int string_value(char *string){
    int valor = 0;
    int tam = strlen(string);
    for(int i=0; i< tam; i++){
        valor = valor * 31 + (int)string[i];
        valor = valor % 1000000; // 防止溢出
    }
    return valor;
}

int hash_function(int chave, int table_size, Hash *ha){
    int position = chave % table_size;
    if (position < 0) position += table_size;

    for (int j = 0; j < table_size; j++) {
        int current_pos = (position + j) % table_size;
        if (ha->itens[current_pos] == NULL) {
            return current_pos;
        }
    }
    return -1;
}

int searchHash(Hash* ha, utilizador *a, int table_size){
    int chave = string_value(a->nick);
    int position = chave % table_size;
    if (position < 0) position += table_size;

    for (int j = 0; j < table_size; j++) {
        int current_pos = (position + j) % table_size;
        if (ha->itens[current_pos] == NULL) {
            continue;
        }
        if (strcmp(ha->itens[current_pos]->nick, a->nick) == 0) {
            return current_pos;
        }
    }
    return -1;
}

void input(utilizador *a, char *comando, char *aux) {
    fgets(aux, 35, stdin);
    sscanf(aux, "%s %s %25[^\n]", comando, a->nick, a->nome);
}

int insereHash(Hash* ha, utilizador *a, int table_size){
    int check = searchHash(ha, a, table_size);
    if(check == -1) {
        int chave = string_value(a->nick);
        int i = hash_function(chave, table_size, ha);
        if (i != -1) {
            utilizador *new_user = malloc(sizeof(utilizador));
            if (new_user == NULL) {
                printf("内存分配失败\n");
                return -1;
            }
            strncpy(new_user->nick, a->nick, sizeof(new_user->nick)-1);
            new_user->nick[sizeof(new_user->nick)-1] = '\0';
            strncpy(new_user->nome, a->nome, sizeof(new_user->nome)-1);
            new_user->nome[sizeof(new_user->nome)-1] = '\0';
            new_user->activo = true;

            ha->itens[i] = new_user;
            ha->quantidade++;
            printf("%d %d ", check, i);
            printf("+ utilizador %s criado\n", ha->itens[i]->nick );
        } else {
            printf("hash não tem espaço\n");
        }
    } else {
        printf("%d + nick %s usado previamente\n", check, a->nick);
    }
    return check;
}

int table_size = 23;
utilizador ar_temp[1];
char comando[2] = ""; // 初始化避免未定义行为
char aux[35];
Hash *ha;

int main() {
    ha = createHash(table_size);
    if(ha == NULL) {
        printf("哈希表创建失败\n");
        return 1;
    }

    while(strcmp(comando, "X") != 0 && strcmp(comando, "x") != 0) {
        input(ar_temp, comando, aux);
        if (strcmp(comando, "U") == 0) {
            insereHash(ha, ar_temp, table_size);
        }
    }

    // 打印哈希表,处理空指针
    for(int g=0; g<table_size; g++){
        if(ha->itens[g] != NULL){
            printf("%s ", ha->itens[g]->nick);
        } else {
            printf("(null) ");
        }
    }
    printf("\n");

    // 释放内存,避免泄漏
    for(int g=0; g<table_size; g++){
        if(ha->itens[g] != NULL){
            free(ha->itens[g]);
        }
    }
    free(ha->itens);
    free(ha);

    return 0;
}

测试结果

用你提供的输入数据测试,修复后的代码会正确显示所有插入的用户,不会出现重复覆盖的问题,查找功能也能正常检测重复nick。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:13:06