简体   繁体   中英

C - how to modify an array of pointers inside of a function

I'm trying to implement a hash table in C, and almost have it. I'm implementing collision resolution via chaining using a linked list in each array slot, and I'd like to be able to chain inside a function call.

Now, the problem is that in order for the modifications to be permanent I believe that I need an additional level of indirection. This is a problem in that when I try to traverse the list the previous element is overwritten by the next (see my comment inside the insert() function). I've tried to pass this array with an additional level of indirection specified a few different ways but I get compiler warnings and seg faults.

This may seem simple to some of you, but it's had me scratching my head for quite some time now, and this scenario (passing array of pointers for modification) is treated nowhere in my text and I can't seem to find this exact question asked (although it may be in a form I don't recognize). I'm not necessarily looking for a 'quick fix' to my code, but I want to understand what is the best practice to accomplish what I'm looking to do.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
//#include "list.c"
struct node{
    int key;
    struct node* next;
};

void createTable(struct node *table[], int numEls, int numSlots);
int hash(int key, int numSlots);
void insert(struct node *table[], int key, int slot, int numSlots);
void display(struct node *table[], int numEls);

int main(void){
    srand(time(NULL));
    int numEls, numSlots;
    printf("Please enter the desired number of slots in the hash table: ");
    scanf("%d", &numSlots);
    struct node *table[numSlots];
    printf("\nPlease enter the desired number of elements: ");
    scanf("%d", &numEls);
    printf("\nYour load factor will be %f", (float)numEls/numSlots);
    createTable(table, numEls, numSlots);
}

void createTable(struct node *table[], int numEls, int numSlots){
    for(int i = 0; i < numSlots; i++)
        table[i] = NULL;
    for(int j = 0; j < numEls; j++){
        for(int k = 0; k < 99999999; k++){}//give the rand function time 
        int el = rand()%100;
        insert(table, el, hash(rand()%100, numSlots), numSlots);
    }
    display(table, numSlots);
}

int hash(int key, int numSlots){
    return((int)(pow(key, 2.819)) % numSlots);
}

void insert(struct node *table[], int key_, int slot, int numSlots){
    printf("\nInserting %d into slot %d", key_, slot);
    fflush(stdout);
    struct node* new = malloc(sizeof(struct node));
    (new)->key = key_;
    (new)->next = NULL;
    struct node** temp = &(table[slot]);
    if((*temp) == NULL){
        printf(" (*temp) == NULL");
        (*temp) = new;
    }
    else{
        printf(" %d", (*temp)->key);
        while((*temp)->next != NULL){
            printf(" %d", (*temp)->next->key);
                (*temp) = (*temp)->next;  //head is overwritten with head->next 
        }
        (*temp)->next = new;
        printf(" %d", (*temp)->next->key);
    }
}

void display(struct node *table[], int numSlots){
    for(int i = 0; i < numSlots; i++){
        printf("\nSlot %d:", i);
        struct node* temp = table[i];
        while(temp != NULL){
            printf(" %d", temp->key);
            temp = temp->next;
        }
    }
}

In the line with the comment, you are overwriting the pointer to the head (inside the outermost array) with the next element in the linked list, which was probably not your intent.

The correction is to walk down the list of pointers until you found the last pointer, without modifying the main data structure during the walk.

Here is a corrected version of insert() .

void insert(struct node *table[], int key_, int slot, int numSlots){
    printf("\nInserting %d into slot %d", key_, slot);
    fflush(stdout);
    struct node* new = malloc(sizeof(struct node));
    new->key = key_;
    new->next = NULL;

    // Here we make a copy of the pointer to the head node in the linked list.
    // This way, we never overwrite the original copy which lives in the array itself.
    struct node* head = table[slot];
    if(head == NULL){
        printf(" head == NULL");
        table[slot] = new;
    }
    else{
        while(head->next != NULL) {
            head = head->next;  //head is overwritten with head->next 
        }
        head->next = new;
    }
}

This isn't an answer but is too big for a comment... can you explain what this code is meant to be doing?

while((*temp)->next != NULL)
{
    printf(" %d", (*temp)->next->key);
    (*temp) = (*temp)->next;  //head is overwritten with head->next 
}
(*temp)->next = new;

I would expect that you want to append the new node at the end of the linked list of existing nodes in this slot. But this code actually updates the head to point to the last node in the list , just like your comment says (leaking memory - the earlier nodes in the list are now unreachable). Then it makes the last node (which is now the only node in the list) point to new .

So your "list" only ever has length 1 or 2, and it leaks memory each time you try to put a third entry in.

It seems to me that there is nothing wrong with your passing of table (which is a list of heads of linked lists) but the problem is that the code that maintains the list for which those are heads, is not right.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM