2013-10-30 2 views
-1

버킷 포인터의 배열 인 해시 테이블이 있습니다 (이 프로젝트에서는 버킷이라고 함). 해시 테이블은 충돌을 피하기 위해 연결된 목록 체이닝을 사용합니다.Valgrind에서 잘못된 오류 메시지가 나타납니다.

typedef struct bucket { 
    char *key; 
    void *value; 
    struct bucket *next; 
} Bucket; 

typedef struct { 
    int key_count; 
    int table_size; 
    void (*free_value)(void *); 
    Bucket **buckets; 
} Table; 

Valgrind의 라인에서 나에게 잘못된 무료() 오류 메시지를주고있다 : : 방법에 table->free_value(curr->value); :

/* Removes a bucket consisting of a key and value pair */ 
int remove_entry(Table * table, const char *key) { 
    unsigned int hc = 0; 
    int found = 0; 
    Bucket *curr; 
    Bucket *prev; 
    if (table == NULL || key == NULL) { 
    return FAIL; 
    } 
    hc = hash_code(key)%(table->table_size); 
    if (table->buckets[hc] != NULL) { 
    curr = table->buckets[hc]; 
    prev = NULL; 
    while (curr != NULL) { 
     if (strcmp(curr->key, key) == 0) { 
     found = 1; 
     if (table->free_value != NULL && curr->value != NULL) { 
      table->free_value(curr->value); 
      if (curr == table->buckets[hc]) { 
      table->buckets[hc] = curr->next; 
      free(curr->key); 
      free(curr); 
      curr = NULL; 
      (table->key_count)--; 
      return SUCC; 
      } 
      prev->next = curr->next; 
      free(curr->key); 
      free(curr); 
      curr = NULL; 
      (table->key_count)--; 
      return SUCC; 
     } else { 
      if (curr == table->buckets[hc]) { 
      table->buckets[hc] = curr->next; 
      free(curr->key); 
      free(curr); 
      curr = NULL; 
      (table->key_count)--; 
      return SUCC; 
      } 
      prev->next = curr->next; 
      free(curr->key); 
      free(curr); 
      curr = NULL; 
      (table->key_count)--; 
      return SUCC; 
     } 
     } 
     prev = curr; 
     curr = curr->next; 
    } 
    } 
    if (found == 0) { 
    return FAIL; 
    } 
    return SUCC; 
} 

내가 그 말을 왜 확실하지 오전 여기 내 데이터 구조입니다. put() 메서드는 다음과 같습니다.

/* Puts a key value pair in. If the key exists, the value is updated, otherwise the pair is added. */ 
int put(Table *table, const char *key, void *value) { 
    unsigned int hc = 0; 
    Bucket *curr; 
    Bucket *new_bucket; 
    char *copy_key; 

    if (table == NULL || key == NULL) { 
    return FAIL; 
    } 

    copy_key = malloc(sizeof(strlen(key) + 1)); 
    if (copy_key == NULL) { 
    return FAIL; 
    } 
    strcpy(copy_key, key); 

    hc = hash_code(key)%(table->table_size); 
    if (table->buckets[hc] != NULL) { 
    curr = table->buckets[hc]; 
    while (curr != NULL) { 
     if (strcmp(curr->key, key) == 0) { 
     if (curr->value != NULL && value != NULL) { 
      table->free_value(curr->value); /* Getting the invalid free error here again */ 
     } 
     curr->value = value; 
     free(copy_key); 
     return SUCC; 
     } 
     curr = curr->next; 
    } 
    curr = table->buckets[hc]; 
    new_bucket = malloc(sizeof(*new_bucket)); 
    if (new_bucket == NULL) { 
     free(copy_key); 
     return FAIL; 
    } 
    new_bucket->value = value; 
    new_bucket->key = copy_key; 
    new_bucket->next = curr; 
    table->buckets[hc] = new_bucket; 
    (table->key_count)++; 
    return SUCC; 
    } else if (table->buckets[hc] == NULL) { 
    new_bucket = malloc(sizeof(*new_bucket)); 
    if (new_bucket == NULL) { 
     free(copy_key); 
     return FAIL; 
    } 
    new_bucket->value = value; 
    new_bucket->key = copy_key; 
    table->buckets[hc] = new_bucket; 
    table->buckets[hc]->next = NULL; 
    (table->key_count)++; 
    return SUCC; 
    } 
    free(copy_key); 
    return FAIL; 
} 

아무 도움이됩니다. 고맙습니다.

+0

나는 완전한 valgrind 오류를 주신 것으로 생각하지 않습니다. Invalid Free는 여러 가지 종류가 있습니다 (결코 할당되지 않은 포인터는 두 번 사용하지 말고 머리글/예고편은 손상됨). – abelenky

답변

0

코드를 살펴본 결과 "이 방법이 너무 어렵습니다"라고 생각했습니다. 나는 똑똑한 엉덩이가 되려고하지는 않지만 훨씬 적은 코드로 동일한 기능을 수행하는 아래의 코드를 고려하십시오. C 코드의 목표 중 하나는 중복 된 코드 블록을 최소화하는 것입니다.

#include <stdio.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <string.h> 
#include <unistd.h> 

#define SUCC 0 
#define FAIL 1 

typedef struct bucket { 
    char *key; 
    void *value; 
    struct bucket *next; 
} Bucket; 

typedef struct { 
    int key_count; 
    int table_size; 
    void (*free_value)(void *); 
    Bucket **buckets; 
} Table; 

unsigned int hash_code(const char *key) { // dummy stupid hash function 
    unsigned int hash = 0; 
    while(*key) { 
     hash += *key++; 
    } 
    return hash; 
} 

/* Puts a key value pair in. If the key exists, the value is updated, otherwise the pair is added. */ 
int put(Table *table, const char *key, void *value) { 

    if(table == 0 || key == 0) { 
     return FAIL; 
    } 

    unsigned int hc = hash_code(key)%(table->table_size); 
    Bucket **prev = &table->buckets[hc]; 
    Bucket *curr = *prev; 
    while(curr && strcmp(curr->key, key)) { 
     prev = &curr->next; 
     curr = curr->next; 
    } 

    if(curr) { 
     if(table->free_value && curr->value) { 
      table->free_value(curr->value); 
     } 
     curr->value = value; 
    } else { 
     Bucket *p = malloc(sizeof(*p)); 
     if(p == 0) { 
      return FAIL; 
     } 
     if((p->key = strdup(key)) == 0) { 
      free(p); 
      return FAIL; 
     } 
     *prev = p; 
     p->next = 0; 
     p->value = value; 
     table->key_count++; 
    } 
    return SUCC; 
} 

/* Removes a bucket consisting of a key and value pair */ 
int remove_entry(Table * table, const char *key) { 

    if (table == NULL || key == NULL) { 
     return FAIL; 
    } 

    unsigned int hc = hash_code(key)%(table->table_size); 
    Bucket **prev = &table->buckets[hc]; 
    Bucket *curr = *prev; 
    while(curr && strcmp(curr->key, key)) { 
     prev = &curr->next; 
     curr = curr->next; 
    } 

    if(curr == 0) { 
     return FAIL; 
    } 

    if(table->free_value && curr->value) { 
     table->free_value(curr->value); 
    } 
    *prev = curr->next; 
    free(curr->key); 
    free(curr); 
    table->key_count--; 
    return SUCC; 
} 

void print_table(Table *table, FILE *file) { 
    printf("table key_count=%d {\n", table->key_count); 
    for(int i = 0; i != table->table_size; i++) { 
     if(table->buckets[i]) { 
      printf("[%d]", i); 
      for(Bucket *b = table->buckets[i]; b; b = b->next) { 
       printf(" %s", b->key); 
      } 
      printf("\n"); 
     } 
    } 
    printf("}\n"); 
} 

int main(int argc, char **argv) { 
    Bucket *buckets[100] = { 0 }; 
    Table table; 
    table.table_size = 100; 
    table.key_count = 0; 
    table.free_value = 0; 
    table.buckets = buckets; 

    int ch; 
    while((ch = getopt(argc, argv, "p:r:x")) != -1) { 
     switch(ch) { 
      case 'p': 
       printf("put %s\n", optarg); 
       put(&table, optarg, optarg); 
       break; 
      case 'r': 
       printf("remove %s\n", optarg); 
       remove_entry(&table, optarg); 
       break; 
      case 'x': 
       print_table(&table, stdout); 
       break; 
     } 
    } 
    printf("done\n"); 
    print_table(&table, stdout); 
    return 0; 
} 

"똑똑한"부분은 Bucket **prev으로 발생합니다. 그것은 약간의 연구가 필요합니다. 나머지는 똑바로 앞으로해야합니다.

그리고 여기에 몇 가지 테스트 케이스를 실행하는 데 약간의 쉘 스크립트입니다 :

#!/bin/csh -f 

set valgrind = valgrind 
set valgrind = "" 

cc -Wall hash.c -o hash 
$valgrind ./hash -pa ; # add "a" 
$valgrind ./hash -pa -ra ; # add "a" rm "a" 
$valgrind ./hash -pab -pba ; # same bucket 
$valgrind ./hash -pab -pba -rab ; # same bucket 
$valgrind ./hash -pab -pba -rba ; # same bucket 
$valgrind ./hash -pab -pab ; # add "ab" twice 
$valgrind ./hash -pba -pab -pab -pab ; 

어쩌면 내가 당신의 숙제를위한 몇 가지 플랙을 얻을 것이다, 나도 몰라. 어쨌든 코드에서 뭔가를 배울 수 있고 문제를 훨씬 더 작게 만드는 방법을 알 수 있습니다.

관련 문제