分割。 strcmp [C]

时间:2011-01-16 12:28:18

标签: c segmentation-fault strcmp

我有一个格式文件:[name] [number] [amount] number被视为一个字符串。并且我在strcmp中使用它。 问题是我得到了分段错误。我知道在大多数情况下,当strcmp标记分段错误时,它意味着其中一个参数为null或无法找到其“结束”('\ 0')。 我检查了gdb,我不能说这是不是问题。看看:

> (gdb) bt full
> #0  0x08048729 in lookup (hashtable=0x804b008, hashval=27, 
>     number=0x804b740 "6900101001") 
>         list = 0xffffffff
> #1  0x080487ac in add (hashtable=0x804b008, 
>     number=0x804b740 "9900101001", name=0x804b730 "Smithpolow",
> time=6943) 
>         new_elem = 0xffffffff
>         hashval = 27
> #2  0x08048b25 in main (argc=1, argv=0xbffff4b4) 
>         number = 0x804b740 "9900101001"
>         name = 0x804b730 "Smithpolow"
>         time = 6943
>         i = 2

代码:

        typedef struct  HashTable
        {
            int length;
            struct  List *head; 

        } HashTable;

        //(resolving collisions using chaining)
        typedef struct  List
        {
            char *number;
            char *name;
            int time;
            struct List *next;
        } List;

    int primes[]={17,29,51,79,163,331,673,1361,2729,5471,10949,21911,43853,87719,175447,350899};
    *int PrimesIndex=1;* **int PrimesIndex=0;**  **//changed.**


     HashTable *createHashTable(size)
    {
         HashTable *new_table = malloc(sizeof(*new_table)*size);

        if (new_table == NULL)
        {   return NULL;
        }

        int i=0;
        for(i; i<size; i++)
        {   new_table[i].length=0;
            new_table[i].head=NULL;
        }
        return new_table;
    }

    int hash ( HashTable *hashtable,char* number)
    {
        int hashval = 0;
        int i = 0;
        for ( i = 0; i < 10; i++)
        {   hashval = (hashval << 5)|(hashval >> 27);
            hashval += ( int)number[i];
        }

        return hashval % primes[PrimesIndex];
    }

         List *lookup ( HashTable *hashtable,int hashval,char number[10])
        {
         printf("NUMBER:%s\n",number);
          List *list=hashtable[hashval].head;
         for(list; list!=NULL; list=list->next){
           if (strcmp(number,list->number)==0)    
            return list;

         }
         return NULL;
        }


        int add ( HashTable* hashtable,char number[10],char* name,int time)
        {
             List *new_elem;
            int hashval=hash (hashtable,number);

            new_elem=hashtable[hashval].head;
            if(hashtable[hashval].length>0)
            {                   
                  if ((lookup (hashtable,hashval,number))!=NULL) {return 0;}    
            }

            if (!(new_elem=malloc(sizeof(struct  List)))){ return -1;}

            //insert values for the new elem
            new_elem->number=strdup(number);    
            new_elem->name=strdup(name);
            new_elem->time=time;

            hashtable[hashval].head=new_elem;
            new_elem->next=NULL;
            hashtable[hashval].length++;

            /* rehash existing entries if necessary */
            if(hashTableSize(hashtable)>= 2*primes[PrimesIndex])    
            {    
                 hashtable = expand(hashtable);
                 if (hashtable ==NULL){
                   return 0;
                 }

            }

            return 1;
        }

 HashTable* expand( HashTable* h )
{   printf("EXPAND \n");
     HashTable* new;
     List *temp;
    int n;
     List *node,*next;
    PrimesIndex++;
    int new_size= primes[PrimesIndex];      /* double the size,odd length */

    if (!(new=malloc((sizeof(  List*))*new_size))) return NULL;

    for(n=0; n< h->length; ++n) {
        for(node=h[n].head; node; node=next) {
            add (new, node->number, node->name,node->time);
            next=node->next;
            //free(node);
        }
    }
    free(h);
    return new;
}

和主要:

  int main(int argc, char *argv[])  
    {
        char **token;
        FILE *delimitedFile;
        /*Here's an example of tokenizing lines from an actual file*/
        /*Open file for reading ("r"), and take a FILE pointer, 
          which you can use to fetch lines using fgets()*/

        my_hash_table = createHashTable(17);
        if(my_hash_table==NULL)
        {   return 1;
        }

        FILE * File2;
            if ( ( File2=fopen(" File.txt","r")) !=NULL ) 
            { // File.txt format:  [name number time]
                int li = 0;
                char *lin = (char *) malloc(MAX_LINE * sizeof(char));

                while(fgets(lin, MAX_LINE, File2) != NULL)
                {
                    token = my_linetok(lin, " ");
                    if(token != NULL)
                    {
          char* number ;
          char* name;
          int time;
          int i;
                        for(i = 0; token[i] != NULL; i++)
                        {
           name=strdup(token[0]);
           number=strdup(token[1]);
           time=atoi(token[2]);

           if (i==2)
           { int insertDone=0;
                 insertDone =add(my_hash_table,number,name,time);   

           } 
          }
          free(name); 
          free(number);
          free(token);

                    }
                    else 
             {
                        printf("Error reading line %s\n", lin);
                        exit(1);   
                    }
                }

            } 
            else 
            {
                printf("Error opening file \nEXIT!");
         exit(0);
            }

        return 1;
    }

3 个答案:

答案 0 :(得分:3)

这里的根本问题是你创建了一个包含17个桶的哈希表:

my_hash_table = createHashTable(17);

但是C数组是从0开始的,PrimesIndex从1开始,而不是0,所以在add()内,调用hash()

int hashval=hash (hashtable,number);

将返回0到28之间的数字,而不是0到16之间的数字。因此,在某些时候,超出范围的值将被分配给hashval,并且其中一个后续访问将被索引hashval,例如

new_elem=hashtable[hashval].head;

将读取未初始化的内存,最终会导致像0xffffffff稍后出现的疯狂指针值。

解决方案:将int PrimesIndex = 1;更改为int PrimesIndex = 0;

但老实说,我认为可能还有其他一些我不知道的问题。有:

  • 我在评论中指出的forwhile循环内的main()循环问题;
  • number参数lookup_on_Clients()的可疑声明;
  • 有时候这个函数被称为lookup(),有时候是lookup_on_Clients()(正如Oli所注意到的);
  • 我不相信my_linetok()(你没有显示源代码)正常工作 - 至少,除非它使用静态缓冲区,否则它必须分配{{{ 1}}为了保存指向永不释放的单个令牌的指针 - 内存泄漏。

答案 1 :(得分:2)

number中没有空终止符的空间。您将number的大小设置为等于10个字符,但您的数字中有10位数字,\ 0没有空格。

编辑:

我看了你更新的代码。您创建了初始大小= 17的哈希表,但是您的hasval = 27.但是您没有代码来正确扩展哈希表的大小。

new_elem=hashtable[hashval].head;
if(hashtable[hashval].length>0) // <-- when hashval is out of array 
                                // hashtable[hashval] can have any value of length and head (not NULL)

答案 2 :(得分:0)

您实际上并未显示add()的来源,可能会调用lookup_on_Clients(),而回溯会提及lookup()而不是lookup_on_Clients(),所以我无法确定,但这是我的诊断:

  • 回溯说list = 0xffffffff - 这绝对不是一个有效的地址,所以它可能是导致SIGSEGV的list->name访问。
  • 我也被number的{​​{1}}参数声明为lookup_on_Clients()并且gdb显示它包含一个10位数的事实所困扰 - 这表明保持参数的变量以相同的方式声明,这意味着没有空间来终止0字节。而且你在它上面调用char number[10]这一事实意味着你将strcmp()视为以空字符结尾的字符串,因此保存参数的变量将作为number传递给lookup_on_Clients() (可能是在number中声明的局部变量?)应声明为大小至少为11的数组,以避免崩溃。如果add()直接传递了自己的add()参数,那么您就是安全的,因为number通过strdup()动态分配它足够大,但我会改变main()上的声明。