我有一个格式文件:[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;
}
答案 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;
。
但老实说,我认为可能还有其他一些我不知道的问题。有:
for
中while
循环内的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()
上的声明。