哈希表插入数据后打印异常问题排查与修复求助
问题分析与解决方案
你的哈希表出现数据丢失、重复显示的问题,核心原因是多个严重的语法错误、内存管理错误以及哈希逻辑错误,下面逐个拆解并给出修复方案:
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
相关产品推荐
相关产品推荐

