C - Ошибка сегментации с помощью strcmp? - PullRequest
3 голосов
/ 06 сентября 2011

Я, кажется, получаю ошибку сегментации где-то с функцией 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;
}

Ответы [ 5 ]

5 голосов
/ 06 сентября 2011

Помимо возможности того, что значения в вашем 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

Я предлагаю использовать целые числа без знака для значений, которые явно предназначены для беззнаковых.

2 голосов
/ 06 сентября 2011

Если вы используете Linux, попробуйте valgrind.Он может рассказать вам о недопустимых обращениях, утечках памяти, неинициализированных переменных и т. Д. Вывод может показаться грязным и трудным для чтения, но если вы продолжите попытки, он вознаградит вас.Что происходит:

  1. сборка вашей программы с переключателем -g для включения отладочной информации
  2. запуск программы с использованием valgrind: valgrind ./myprogram
  3. прибыль, читаяoutput

Как я уже сказал, выходные данные могут показаться очень грязными, поэтому, возможно, сначала попробуйте какую-нибудь простую программу (обычный пустой основной), чтобы посмотреть, как она выглядит, когда все в порядке, а затем попытайтесь преднамеренно завершить работу.Программа, например:

int *bullet = 0;
*bullet = 123;

и посмотреть вывод.


Хорошее базовое введение с примерами можно найти здесь .


Поскольку вы предоставили вывод valgrind, я бы начал исправлять перечисленные там проблемы.Сначала ошибка Conditional jump or move depends on uninitialised value(s).Вы можете перезапустить valgrind с помощью --track-origins=yes, так как valgrind предлагает увидеть больше деталей, а затем исправить это (у вас нет номеров строк в фрагментах кода, я не могу вам больше помочь).

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

ТогдаInvalid read of size 1 ошибка означает, что вы уже обращаетесь к памяти, которая не принадлежит вам, а только читает ее, поэтому она "не возражает".Но это все еще ошибка, которая не должна возникать, поэтому исправьте ее (если она не была исправлена ​​первым исправлением ошибки).

И, наконец, Access not within mapped region - это запись в память, которая не выделяется.

Теперь попробуйте исправить ошибки (в порядке их перечисления в valgrind), следуя указаниям valgrind (например, перезапуск с переключателями).

1 голос
/ 06 сентября 2011

h-> ключи полностью инициализируются с NULL? Иначе у вас есть случайные указатели внутри.

КСТАТИ

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

Всегда проверяйте возвращаемость функции на достоверность, если она сигнализирует об ошибке, независимо от того, насколько маловероятной может быть ошибка. malloc() возвращает NULL при ошибке.

1 голос
/ 06 сентября 2011

ну, вы не включили код вокруг htable для заполнения этой хеш-таблицы и т. Д. Strcmp, вероятно, segfaults, потому что вы либо дали ему NULL-строку, либо массив символов, которые не заканчиваются на 0 ....

0 голосов
/ 06 сентября 2011

На первый взгляд, я предполагаю, что ваш segfault происходит от p - вы никогда не инициализируете эту переменную, поэтому она не гарантированно начинается с нуля;он может начинаться с -123456, насколько вам известно, и тогда вы получите доступ к неверному адресу памяти.РЕДАКТИРОВАТЬ: неправильно читать цикл do-while.Игнорировать этот абзац.

На второй взгляд, я бы проверил, является ли h->keys[p] строкой с нулевым символом в конце - strcmp продолжает читать значения, пока не достигнет нулевого байта;если такого байта нет, он может продолжать работу, пока не достигнет неверного адреса памяти.

...