2

So all I'm trying to do is free a pointer and it just gives me the error 'invalid address'; though the address is clearly valid, as illustrated by the prints I put in. It tries to free the address of the pointer, but still fails. Through valgrind, it gives the error invalid free() saying the address is on thread 1's stack? The code below is runnable; can anyone help?

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

#define SUCC 1
#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;


extern unsigned int hash_code(const char *key)  {
    unsigned int hash = 0, i = 0;
    while(i < strlen(key))  {
        hash ^= ((hash << 3) | (hash >> 29)) + key[i++];
    }
    return hash;
}

/*Makes copy of string and returns pointer to it*/
char * cpy(const char *str) {
    char *new = malloc(sizeof(char *));
    if(new)
        strcpy(new, str);
    return new;
}   


int create_table(Table ** table, int table_size, void (*free_value)(void *))    {
    *table = malloc(sizeof(Table));
    if(table && table_size != 0)    {
        int i = 0;
        (*table)->key_count = 0;   
        (*table)->table_size = table_size;
        (*table)->free_value = free_value;
        (*table)->buckets = calloc(table_size, sizeof(Bucket *));
        while(i < table_size)   
            (*table)->buckets[i++] = NULL;
        return SUCC;
    }
    return FAIL;
}


int put(Table * table, const char *key, void *value)    {
    if(table && key)    {
        int hash = hash_code(key)%table->table_size;
        Bucket *curr = table->buckets[hash];
        while(curr) {
            if(strcmp(curr->key, key) == 0)  {
                if(table->free_value)
                    table->free_value(curr->value);
                printf("addr of ptr: %p\n", value);
                curr->value = value;
                printf("addr of curr ptr: %p\n", curr->value);
                return SUCC;
            }
           curr = curr->next;
        }
        curr = malloc(sizeof(Bucket));
        curr->key = cpy(key);
        printf("addr of ptr: %p\n", value);
        curr->value = value; 
        printf("addr of curr ptr: %p\n", curr->value);
        curr->next = table->buckets[hash];
        table->buckets[hash] = curr;
        table->key_count++;

        return SUCC;
    }
    return FAIL;
}


int remove_entry(Table * table, const char *key)    {
    if(table && key)    {
        int hash = hash_code(key)%(table->table_size);
        Bucket *curr = table->buckets[hash], *prev = table->buckets[hash];
        while(curr) {
            printf("attempt");
            if(strcmp(curr->key, key) == 0)  {
                void * test = curr->value;
                printf("at addr %p\n", test);
                table->free_value(test);
                printf("freed");

                if(table->free_value){
                   table->free_value(curr->value);
                }
                free(curr->key);
                curr->key = NULL;
                curr->value = NULL;
                table->key_count--;
                if(prev == curr)
                    table->buckets[hash] = curr->next;
                else
                    prev->next = curr->next;
                free(curr);
                curr = NULL;
                return SUCC;
            }
            prev = curr;
            curr = curr->next;
        }
    }
    return FAIL;
}

And the test file that shows the error:

#include <stdio.h>
#include <stdlib.h>
#include "htable.h"


int main()  {
    Table *t;
    int num2 = 3;
    printf("create: %d\n",create_table(&t, 2, free));
    printf("addr of ptr: %p\n",(void *)&num2);
    printf("put %s: %d\n","test",put(t, "test", &num2));
    printf("rem key: %d\n",remove_entry(t, "test"));
    return 0;
}
4

2 回答 2

5

This is broken:

char *new = malloc(sizeof(char *));

The amount of memory you need is based on what you need to store, which is the string. You want:

char *new = malloc(strlen(str) + 1);

Or, better yet, just use strdup.

于 2012-10-24T00:18:32.130 回答
3

You are trying to free() a stack variable: num2 (in main()):

int num2 = 3;

Later, you have this call:

printf("put %s: %d\n","test",put(t, "test", &num2));

You're passing the address of num2 to put(), which means that remove_entry() will try to free it later. This is illegal. You cannot free a variable allocated on the stack. You should dynamically allocate num2 instead:

int* num2 = malloc(sizeof(int));
*num2 = 3;

There's another problem as well though. In this code:

void * test = curr->value;
printf("at addr %p\n", test);
table->free_value(test);
printf("freed");

if(table->free_value){
    table->free_value(curr->value);
}

You are freeing curr->value twice, because you're freeing test which is just a copy of the pointer.

于 2012-10-24T00:24:51.190 回答