C - 使用strcmp进行分段错误?

时间:2011-09-06 07:23:35

标签: c segmentation-fault hashtable

我似乎在strcmp函数的某处出现了分段错误。 我还是C的新手,我不明白为什么它会给我错误。

int linear_probe(htable h, char *item, int k){
  int p;
  int step = 1;
  do {
    p = (k + step++) % h->capacity;
  }while(h->keys[p] != NULL && strcmp(h->keys[p], item) != 0);
  return p;
}

GDB:

Program received signal SIGSEGV, Segmentation fault.
0x0000003a8e331856 in __strcmp_ssse3 () from /lib64/libc.so.6

(gdb) frame 1
#1  0x0000000000400ea6 in linear_probe (h=0x603010, item=0x7fffffffde00 "ksjojf", k=-1122175319) at htable.c:52

编辑:插入代码和htable struct

int htable_insert(htable h, char *item){
  unsigned int k = htable_word_to_int(item);
  int p = k % h->capacity;

  if(NULL == h->keys[p]){
    h->keys[p] = (char *)malloc(strlen(item)+1);
    strcpy(h->keys[p], item);
    h->freqs[p] = 1;
    h->num_keys++;
    return 1;
  }

  if(strcmp(h->keys[p], item) == 0){
    return ++h->freqs[p];
  }

  if(h->num_keys == h->capacity){
    return 0;
  }

  if(h->method == LINEAR_P) p = linear_probe(h, item, k);
  else p = double_hash(h, item, k);

  if(NULL == h->keys[p]){
    h->keys[p] = (char *)malloc(strlen(item)+1);
    strcpy(h->keys[p], item);
    h->freqs[p] = 1;
    h->num_keys++;
    return 1;
  }else if(strcmp(h->keys[p], item) == 0){
    return ++h->freqs[p]; 
  }
  return 0;
}

  struct htablerec{
      int num_keys;
      int capacity;
      int *stats;
      char **keys;
      int *freqs;
      hashing_t method;
    };

由于

编辑: valgrind - 我输入随机值以添加到表

sdkgj
fgijdfh
dfkgjgg
jdf
kdjfg
==25643== Conditional jump or move depends on uninitialised value(s)
==25643==    at 0x40107E: htable_insert (htable.c:87)
==25643==    by 0x400AB7: main (main.c:75)
==25643== 
fdkjb
kjdfg
kdfg
nfdg
lkdfg
oijfd
kjsf
vmf
kjdf
kjsfg
fjgd
fgkjfg
==25643== Invalid read of size 8
==25643==    at 0x400E0E: linear_probe (htable.c:51)
==25643==    by 0x401095: htable_insert (htable.c:87)
==25643==    by 0x400AB7: main (main.c:75)
==25643==  Address 0x4c342a0 is not stack'd, malloc'd or (recently) free'd
==25643== 
==25643== Invalid read of size 8
==25643==    at 0x400E2B: linear_probe (htable.c:51)
==25643==    by 0x401095: htable_insert (htable.c:87)
==25643==    by 0x400AB7: main (main.c:75)
==25643==  Address 0x4c342a0 is not stack'd, malloc'd or (recently) free'd
==25643== 
==25643== Invalid read of size 1
==25643==    at 0x4A06C51: strcmp (mc_replace_strmem.c:426)
==25643==    by 0x400E3C: linear_probe (htable.c:51)
==25643==    by 0x401095: htable_insert (htable.c:87)
==25643==    by 0x400AB7: main (main.c:75)
==25643==  Address 0x210 is not stack'd, malloc'd or (recently) free'd
==25643== 
==25643== 
==25643== Process terminating with default action of signal 11 (SIGSEGV)
==25643==  Access not within mapped region at address 0x210
==25643==    at 0x4A06C51: strcmp (mc_replace_strmem.c:426)
==25643==    by 0x400E3C: linear_probe (htable.c:51)
==25643==    by 0x401095: htable_insert (htable.c:87)
==25643==    by 0x400AB7: main (main.c:75)
==25643==  If you believe this happened as a result of a stack
==25643==  overflow in your program's main thread (unlikely but
==25643==  possible), you can try to increase the size of the
==25643==  main thread stack using the --main-stacksize= flag.
==25643==  The main thread stack size used in this run was 8388608.
==25643== 
==25643== HEAP SUMMARY:
==25643==     in use at exit: 1,982 bytes in 28 blocks
==25643==   total heap usage: 28 allocs, 0 frees, 1,982 bytes allocated
==25643== 
==25643== LEAK SUMMARY:
==25643==    definitely lost: 0 bytes in 0 blocks
==25643==    indirectly lost: 0 bytes in 0 blocks
==25643==      possibly lost: 0 bytes in 0 blocks
==25643==    still reachable: 1,982 bytes in 28 blocks
==25643==         suppressed: 0 bytes in 0 blocks
==25643== Rerun with --leak-check=full to see details of leaked memory
==25643== 
==25643== For counts of detected and suppressed errors, rerun with: -v
==25643== Use --track-origins=yes to see where uninitialised values come from
==25643== ERROR SUMMARY: 7 errors from 4 contexts (suppressed: 6 from 6)
Segmentation fault (core dumped)

static unsigned int htable_word_to_int(char *word){
  unsigned int result = 0;
  while(*word != '\0'){
    result = (*word++ + 31 * result);
  }
  return result;
}

5 个答案:

答案 0 :(得分:5)

除了htable中的值可能是无效指针(即既不是NULL也不是指向正确的C字符串的指针)的可能性之外,您遇到了严重的问题如果它既不包含NULL也不包含您正在寻找的字符串,则为无限循环。

对于直接问题,请尝试将代码更改为:

#define FLUSH fflush (stdout); fsync (fileno (stdout))

int linear_probe (htable h, char *item, int k) {
    int pos = k;
    do {
        pos = (pos + 1) % h->capacity;
        printf ("========\n");                    FLUSH;
        printf ("inpk: %d\n",   k);               FLUSH;
        printf ("posn: %d\n",   pos);             FLUSH;
        printf ("cpct: %d\n",   h->capacity);     FLUSH;
        printf ("keyp: %p\n",   h->keys[pos]);    FLUSH;
        printf ("keys: '%s'\n", h->keys[pos]);    FLUSH;
        printf ("item: '%s'\n", item);            FLUSH;
        printf ("========\n");                    FLUSH;
    } while ((pos != k)
          && (h->keys[pos] != NULL)
          && (strcmp (h->keys[pos], item) != 0));
    return pos;
}

那些调试语句应该可以指示出现了什么问题。


因为你得到了:

inpk: -2055051140
posn: -30
cpct: 113
keyp: 0x100000001
在崩溃之前,很明显有人正在传递k的伪造价值。负数的模运算是在C标准中定义的实现,所以你也得到pos的负值。由于h->pos[-30]将成为未定义的行为,所以所有赌注都将被取消。

找到并修复传递该伪造值的代码(可能是未初始化的变量)或通过更改保护您的函数:

int pos = k;

成:

int pos;
if ((k < 0) || (k >= h->capacity))
    k = 0;
pos = k;

在你的功能开始时。我实际上两个都做了,但后来我很偏执: - )


并且,基于另一个更新(哈希密钥计算,如果您生成unsigned int然后盲目地将其用作签名int,那么您已经有了获得负面价值的好机会:

#include <stdio.h>

int main (void) {
    unsigned int x = 0xffff0000U;
    int y = x;
    printf ("%u %d\n", x, y);
    return(0);
}

输出:

4294901760 -65536

我的建议是使用无符号整数表示明确无符号的值。

答案 1 :(得分:2)

如果您使用的是Linux,请尝试valgrind。它可以告诉你无效访问,内存泄漏,未初始化的变量等。输出可能看起来很混乱,难以阅读,但如果你继续尝试,它会奖励你。发生了什么:

  1. 使用-g开关构建程序以包含调试信息
  2. 使用valgrind运行程序:valgrind ./myprogram
  3. 通过阅读产出获利
  4. 正如我所说,输出可能看起来非常混乱,所以可能首先尝试一些简单的程序(普通的空主)来看看当一切正常时它是什么样的,然后尝试故意使你的程序崩溃,如:

    int *bullet = 0;
    *bullet = 123;
    

    并查看输出。


    可以找到一个很好的基本介绍,例如here


    当你提供valgrind输出时,我会开始修复那里列出的问题。首先是Conditional jump or move depends on uninitialised value(s)错误。您可以使用--track-origins=yes重新运行valgrind,因为valgrind建议查看更多详细信息,然后修复它(您的代码段中没有行号,我无法帮助您更多)。

    ./valgrind --track-origins=yes ./myprogram      #don't switch parameters!
    

    然后Invalid read of size 1错误意味着您已经在访问不属于您的内存,但只读它,所以它“不介意”。但它仍然是一个不应该发生的错误,所以修复它(如果没有通过第一个错误修复修复)。

    最后,Access not within mapped region是对未分配的内存的写入。

    现在尝试按照valgrind建议修复错误(按照valgrind列出的顺序)(比如用开关重新运行)。

答案 2 :(得分:1)

你没有包含围绕htable的代码来填充这个哈希表等。 strcmp可能是segfaulted,因为你给它一个NULL字符串或一个字符数组没有正确结束0 ....

答案 3 :(得分:1)

h-&gt;键是否用NULL完全初始化?否则你内有随机指针。

顺便说一句,

h->keys[p] = (char *)malloc(strlen(item)+1);
strcpy(h->keys[p], item);

如果函数发出错误信号,请务必检查函数的返回是否有效,无论错误情况多么不可能发生。失败时malloc()返回NULL。

答案 4 :(得分:0)

乍一看,我的猜测是你的段错误来自p - 你永远不会初始化那个变量,因此不能保证从零开始;它可以从你所知道的-123456开始,然后你将访问一个无效的内存地址。编辑:误读了do-while循环。忽略这一段。

乍一看,我会检查h->keys[p]是否是以空字符结尾的字符串 - strcmp继续读取值,直到它达到零字节为止;如果没有这样的字节,它可以继续运行直到它到达无效的存储器地址。