Mr.Nois
Mr.Nois

Reputation: 35

C - Hashtable Doesn't Work Properly

Here is my code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define m 9



int flag1=1;
//Creating memory for username and password
struct node // The list that is being used to store username and password
{
    char username[30];
    char password[30];
    struct node *next;
};

int getkey(char[30]); //a method to get "randomly" a key
int hash(int);  // a method to create the hashcode
void insert(struct node **,char[30],char[30]); //adding a node struct to a specific possition in hashtable
void initializeHashTable(struct node **);
void search(struct node **,char[30]);//check if password exists
void searchuser(struct node **,char[30]); //check if username exists
void print(struct node**);//printing the hashtable

void print(struct node **T)
{
     struct node *tmp;
     int i,flag=0;
     printf("-------Data Base-------\n");
     for(i=0;i<m;i++)
     {
         tmp=*(T+i);
         while(tmp!=NULL)
         {
             printf("Username: %s  Password: %s\n",tmp->username,tmp->password);
             tmp=tmp->next;
         }

     }
}
void search(struct node **T,char password[30])
{
    struct node *tmp;
    int i,flag=0;
    for(i=0; i<m; i++)
    {
        tmp=*(T+i);
        while(tmp!=NULL)
        {
            if(strcmp(tmp->password,password)==0)
            {
                flag=1;
                break;
            }
            tmp=tmp->next;

        }
        if(flag==1)
            break;

    }
    if(flag==1)
         printf("Authentication Successfull\n");
    else
       printf("Authentication Failed\n");

}

void searchuser(struct node **T,char user[30])
{
    struct node *tmp;
    int i,flag=0;
    for(i=0; i<m; i++)
    {
        tmp=*(T+i);
        while(tmp!=NULL)
        {
            if(strcmp(tmp->username,user)==0)
            {
                flag=1;
                break;
            }
            tmp=tmp->next;

        }
        if(flag==1)
            break;

    }
    if(flag==0)
      {
           printf("Username NOT Found\n");
           flag1=0;

      }
}

void initializeHashTable(struct node **T)
{
    int i;
    for (i=0; i<m; i++)
        *(T+i)=NULL;

}
int getkey(char name[30])
{


    int i=0;
    int key=0;
    for (i=0; i<15; i++)
    {
        key+=name[i];

    }
    return key;

}

int hash(int key)
{
    return key%m;
}

void insert (struct node **T,char name[30],char password[30])
{

    struct node *newnode;
    newnode=(struct node*)malloc(sizeof(struct node));
    strcpy(newnode->username,name);
    strcpy(newnode->password,password);
    int key;
    key=getkey(name);
    int hashcode;
    hashcode=hash(key);
    //printf("Key:%d\n",key);
   // printf("Hashcode:%d\n",hashcode);

    if(*T+hashcode==NULL)
    {
        (*(T+hashcode))->next=newnode;
        newnode->next=NULL;
    }
    else
    {

        newnode->next=(*(T+hashcode));
        (*(T+hashcode))=newnode;
    }



}



int main()
{//possible content of file: phill 1234
    char choice;
    struct node **T;
    struct node **head;
    head==NULL;
    T=(struct node**)malloc(m*sizeof(struct node));//creating the hashmap
    FILE *fp;
    char name[30];
    char pass[30];

    fp=fopen("passwords.txt","r");

    if (fp==NULL)
    {
        printf("File Not Found");
        return 0;
    }
    else
    {
        initializeHashTable(&(*T));
        while(!feof(fp)) // end of file
        {
            fscanf(fp,"%s %s",name,pass); //reads until first wild space,
            //one each loop, you get 1 username and 1 password from the file
            //adding them on hashmap
            insert(&(*T),name,pass);


        }
    }

    //user authentication
    do{

        printf("Enter Username:\n");
        fflush(stdin);
        scanf("%s",&name);
        searchuser(&(*T),name);
        if(flag1==1)
        {
        printf("Enter Password:\n");
        scanf("%s",&pass);
        search(&(*T),pass);

        }
        printf("Try Again? (y/n)\n");
        fflush(stdin);
        scanf("%d",&choice);
    }while(choice!='y');

    free(T);
    free(fp);
    free(head);



    }

Issue: Authenticator doesn't work properly after 1st try

Thanks A Lot For Your Time!

Upvotes: 0

Views: 81

Answers (1)

John Bode
John Bode

Reputation: 123458

Several issues that jump out at me:

struct node **T;
...
T=(struct node**)malloc(m*sizeof(struct node));

Do you intend for T to be a sequence of struct node (in which case the type of T is wrong), or a sequence of pointers to struct node (in which case, the argument to sizeof is wrong)?

It would be a lot cleaner (and much less error-prone) if you wrote

T = malloc( m * sizeof *T ); 

then it's just a matter of deciding the type of T. Personally, I would expect you to be allocating a sequence of struct node, so that would likely be declared

struct node *T = malloc(  m * sizeof *T );

However, the rest of your code really seems to assume that T is a sequence of pointers to struct node, so in that case that would be

struct node **T = malloc( m * sizeof *T );

That's the beauty of this idiom - the only thing you need to change is the type of T. The malloc call itself doesn't need to change. If the type of T is struct node *, then sizeof *T is equivalent to sizeof (struct node). If T is struct node **, then sizeof *T is equivalent to sizeof (struct node *).

Then there's this:

initializeHashTable(&(*T));

which should really just be

intializeHashTable( T );

Also,

while(!feof(fp))

is always wrong - feof won't return true until after you attempt to read past the end of the file, so you'll wind up looping once too often. You should check against the result of the input operation instead:

while ( fscanf(fp,"%s %s",name,pass) == 2 )
{
   insert(T,name,pass);
}

Again, the argument &(*T) should just be T.

As for your input:

printf("Enter Username:\n");
fflush(stdin);
scanf("%s",&name);

The fflush call is unnecessary here - if there's an input operation immediately following an output operation, a flush is implied.

Edit

I'm embarrased to say that I misread that fflush call - for some reason I read that as fflush( stdout), meaning you'd want to make sure your output was written to the console before calling scanf.

Calling fflush on an input stream is erroneous, and the behavior on doing so is undefined. It won't clear the input stream of any unread input (except in MSVC, and that's because Microsoft decided to codify existing bad behavior).

Lose the fflush call altogether.

End edit

The scanf call should be

scanf( "%s", name );

The & is unnecessary here - name will implicitly be converted from type "30-element array of char" to "pointer to char". Same for pass.

Edit

As written, these scanf calls are unsafe - if you type in a string that's longer than the target buffer, scanf will happily write those extra characters to the memory immediate following that buffer, resulting in anything from corrupted data to a seg fault. You need to make sure you don't read too many characters. You can do that either with a field width specifier, such as

scanf( "%29s", name );  // leave at least one element for the 0 terminator

or use fgets:

fgets( name, sizeof name, stdin );

The fgets call will read and store the trailing newline to the buffer if there's room, so you'll need to do some extra work:

char *newline = strchr( name, '\n' );
if ( *newline )
  *newline = 0;

If there's no newline in the buffer, then you will want to clear the input stream before the next read:

while ( getchar() != '\n' )
  ; // empty loop

End edit

  scanf("%d",&choice);
}while(choice!='y');

You're using the wrong format specifier to read choice - you're telling scanf to expect a decimal integer input, not a character. 'y' doesn't match up with the format, so scanf doesn't actually read it from the input stream, and choice is not updated. You should change that to

  choice = getchar();
} while ( choice != 'y' );

There are plenty of other issues, but start with those.

Upvotes: 2

Related Questions