【问题标题】:C - Segmentation Fault with strcmp?C - strcmp 的分段错误?
【发布时间】:2011-11-11 02:12:48
【问题描述】:

我似乎在 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结构

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;
}

【问题讨论】:

  • 是否成功通过了打印语句?你确定你的字符串是空终止的吗?
  • h->keys[p]item 是无效指针。这将有助于在调试器下运行它,这样您就可以在故障点打印这些值。
  • 您发布了 GDB sn-p,但这还不够。一个建议是转到包含您的代码的堆栈框架并开始四处寻找。
  • @asveikau 很抱歉刚接触 C,但我该怎么做呢?
  • #asveikau:我想我明白了。检查 gdb sn -p 下的编辑。但是即使我的 k 是负数,由于 mod 的原因,p 不能保证正值吗?

标签: c segmentation-fault hashtable


【解决方案1】:

除了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

我的建议是对明显意味着无符号的值使用无符号整数。

【讨论】:

  • 我在段错误之前得到了这些值:inpk:-2055051140 posn:-30 cpct:113 keyp:0x100000001。这里有什么问题?
  • 嘿,我是这里的偏执狂。 :P 但无论如何,我从一个将字符串更改为索引的函数中获取 k。帖子编辑底部,你能看一下吗?
  • htable_insert 函数顶部的第一个函数调用,获取 k 值并将其原样传递给 linear_probe。
  • @paranoid,您可能想考虑在任何地方使用unsigned ints。计算 unsigned int 然后将其分配给 int 的行为可能会出现问题。请参阅我的进一步更新。
  • 这很有意义!谢谢+1
【解决方案2】:

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

  1. 使用-g 开关构建您的程序以包含调试信息
  2. 使用 valgrind 运行程序:valgrind ./myprogram
  3. 通过阅读输出获利

正如我所说,输出可能看起来很乱,所以也许先尝试一些简单的程序(纯空的 main),看看一切正常时的样子,然后尝试故意让你的程序崩溃,比如:

int *bullet = 0;
*bullet = 123;

并查看输出。


可以在here找到一个很好的基本介绍和示例。


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

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

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

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

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

【讨论】:

    【解决方案3】:

    h->keys 是否完全用 NULL 初始化?否则你里面有随机指针。

    顺便说一句,

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • do循环中使用前已初始化。
      • 如果它立即被覆盖,这有关系吗?
      • arzenik:他没有初始化它,而是在使用变量之前(在终止循环中)分配一个值(在循环中)
      • @paxdiablo 啊,我的错。我的第二个担忧仍然存在; h-&gt;keys[p] 长什么样子?
      • @paranoid-android 哦,漂亮。我能想到的唯一能解决这个问题的是item 为空,但这会更早地崩溃,并且可能已由调用者测试。我难住了。在 do-while 测试之前设置 GDB 断点并手动检查该行中引用的所有变量可能是下一步。
      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-25
        • 2017-09-13
        • 1970-01-01
        相关资源
        最近更新 更多