【问题标题】:C - Hashtable Doesn't Work ProperlyC - 哈希表无法正常工作
【发布时间】:2017-05-23 21:20:20
【问题描述】:

这是我的代码:

#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);



    }

  • 我的代码在循环中运行良好,它进行身份验证 成功但有时它会崩溃,或者我给出相同的 详细信息(用户名和密码)以及身份验证失败
  • 我认为它有问题的部分代码:

        //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');
    

    如果我输入do..while(choice=='y'),这意味着运行循环while choice=='y'它会停止if choice=='y',这有点奇怪

问题:验证器在第一次尝试后无法正常工作

非常感谢您的宝贵时间!

【问题讨论】:

  • 建议您使用调试器来跟踪程序的执行并检查其状态。这是调试此类问题的最佳方式(不能总是求助于 SO 为您调试)。
  • 我已经尝试了几个星期,没有找到解决方案...我该如何使用调试器?
  • 尝试使用 fgets() 而不是 scanf()。在最后一个 scanf() 中,很可能有一个不需要的换行符被转移。在最后一个 scanf() 之后插入这一行 fgets( stdin, garbage, sizeof(garbage))。并声明char garbage[50];.
  • fgets() 我相信它的文件
  • 谷歌“如何使用调试器”。如果您停止现在正在编写的代码并投资于学习使用调试器,您将节省大量时间。这是必不可少的工具。

标签: c hashtable


【解决方案1】:

我突然想到的几个问题:

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

您是否打算让T 成为struct node 的序列(在这种情况下T 的类型是错误的),还是指向struct node指针 序列(在哪种情况,sizeof 的参数是错误的)?

如果你写的话,它会更干净(而且更不容易出错)

T = malloc( m * sizeof *T ); 

那么这只是决定T 的类型的问题。就个人而言,我希望您分配struct node 的序列,因此很可能会被声明

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

但是,您的代码的其余部分似乎确实假设 T 是指向 struct node 的指针序列,所以在这种情况下会是

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

这就是这个成语的美妙之处——你唯一需要改变的是T的类型。 malloc 调用本身不需要更改。如果T 的类型是struct node *,那么sizeof *T 等价于sizeof (struct node)。如果Tstruct node **,那么sizeof *T 等价于sizeof (struct node *)

然后是这个:

initializeHashTable(&(*T));

应该是这样的

intializeHashTable( T );

还有,

while(!feof(fp))

总是错误的 - feof 直到 您尝试读取文件末尾之后才会返回 true,因此您会经常循环一次。您应该检查输入操作的结果:

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

同样,参数&amp;(*T) 应该只是T

至于您的意见:

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

这里不需要fflush 调用 - 如果在输出操作之后立即有输入操作,则暗示刷新。

编辑

我很尴尬地说我误读了 fflush 调用 - 出于某种原因,我将其读取为 fflush( stdout),这意味着您需要确保在调用 scanf 之前将输出写入控制台。

在输入流上调用fflush 是错误的,这样做的行为是未定义。它不会清除任何未读输入的输入流(MSVC 除外,这是因为 Microsoft 决定对现有的不良行为进行编码)。

完全失去fflush 电话。

结束编辑

scanf 调用应该是

scanf( "%s", name );

这里不需要&amp; - name 将隐式地从“char 的 30 元素数组”类型转换为“指向char 的指针”。 pass 也一样。

编辑

正如所写,这些 scanf 调用是不安全的 - 如果您输入的字符串比目标缓冲区长,scanf 会很乐意将这些额外字符写入该缓冲区之后的内存中,从而导致数据损坏到段错误。你需要确保你没有读到太多的字符。您可以使用字段宽度说明符来做到这一点,例如

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

或使用fgets:

fgets( name, sizeof name, stdin );

fgets 调用将读取尾随换行符并将其存储到缓冲区(如果有空间),因此您需要做一些额外的工作:

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

如果缓冲区中没有换行符,那么您需要在下一次读取之前清除输入流:

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

结束编辑

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

您使用错误的格式说明符来读取choice - 您告诉scanf 期待十进制整数输入,而不是字符。 'y' 与格式不匹配,因此scanf 实际上并没有从输入流中读取它,choice 也没有更新。你应该把它改成

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

还有很多其他问题,但从这些开始。

【讨论】:

  • fflush 调用确实不必要因为it's erroneous!人们经常(错误地)使用 fflush(stdin)stdin 的光标带到下一个换行符(即丢弃它,因为否则 name 将被读取为空白字段);他们应该改用scanf("%*[^\n]"); getchar(); 之类的东西。
  • @Seb: derp。出于某种原因,我以fflush( stdout ); 的身份阅读 - 我将修复我的答案的那部分。
  • 好。我特别喜欢你使用乐观循环(期望返回值为 2,而不是像我经常看到的那样错误地期望它不等于 EOF)。我仍然有一些小问题。首先,scanf( "%29s", name );fgets( name, sizeof name, stdin ); 做了一些微妙的不同,因为它们使用了不同的分隔符集。您可以用strcspn 建议(不会)替换后面的strchr 建议(在没有找到换行符时取消引用NULL)。
  • ... while ( getchar() != '\n' ); 可能会无限循环;当EOF 发生时正确中断scanf("%*[^\n]"); getchar(); 会更安全。说到正确处理EOFgetchar 返回int:或者对应于负值的错误(EOF 表示读取错误或文件结束,而不是字符值)或正值(unsigned char 表示字符值而不是错误)。通过向下转换,您会丢失错误处理信息。 choice 应该是 intwhile ( choice != 'y' ); 应该是 while ( choice != 'y' &amp;&amp; choice != EOF );
猜你喜欢
  • 1970-01-01
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
  • 2011-08-31
  • 2020-08-01
  • 2013-08-26
  • 2010-11-29
  • 2015-02-04
相关资源
最近更新 更多