Получение ошибки сегментации с использованием qsort и простой функции strcmp в C?

Команды печати GDB предполагают, что мой массив и его содержимое отформатированы правильно ('asdf', '\00000'), но я получаю segfault в своем вызове qsort, который отслеживает функцию сравнения, которую я передаю. Все, что я могу предположить, это то, что я каким-то образом передаю qsort нулевой указатель или что мои строки отформатированы неправильно, что мне кажется не так.

/** Word type, used to store elements of the word list,
    with room for a word of up to 20 characters. */
typedef char Word[ WORD_MAX + 1 ];

edit: добавлено определение Word в вопрос ^^

typedef struct {
/** Number of words in the wordlist. */
int len;
  /** Capacity of the WordList, so we can know when we need to resize. */
  int capacity;
  /** List of words.  Should be sorted lexicographically once the word list
      has been read in. */
  Word *words;
} WordList;

Функция readWordList принимает файл .txt в формате:

3 the
5 hello
3 foo
... 

Целое число представляет количество символов в строке, следующей за ним. validChar — это просто логическая проверка, чтобы увидеть, находится ли входящий символ в определенном диапазоне. Я удалил некоторый код, не относящийся непосредственно к проблеме, такой как fopen и несколько инициализаций.

/**
 * comparison function for qsort in readWordList
 */
static int cmp(const void *p1, const void *p2){
  return strcmp(* (char * const *)  p1, * (char * const *) p2);
}

WordList *readWordList( char const *fname ){
  //malloc space for newList
  WordList *newList = ( WordList * ) malloc( sizeof( WordList ) );
  //set capacity to 10
  newList->capacity = START_CAPACITY;
...
  newList->words = ( Word * ) calloc(newList->capacity, sizeof( Word ) );
...
  while( fscanf( toRead, "%d ", &numChars ) == 1 )
  {
    //realloc space for Word *words if necessary
    if(newList->len >= newList->capacity)
    {
      newList->capacity *= 2;
      //check dereferencing
      newList->words = (Word *)realloc(newList->words, newList->capacity * sizeof(Word));
    }
    //if word is longer than 20 chars skip it
    if(numChars > WORD_MAX)
      continue;
    else
    {
      for(int i = 0; i < numChars; i++)
      {
        ch = fgetc(toRead);
          if(validChar(ch)){
            newList->words[newList->len][i] = ch;
            if(i == numChars-1){
              newList->words[(newList->len)][numChars] = '\0';
            }
          }else{
            continue;
          }
      }
      //debug
      printf("%s\n",newList->words[newList->len]);
      //increase length of wordlist
      newList->len += 1;
    }
  }
  qsort(newList->words, newList->len, sizeof(Word), cmp);
  return newList;
}

Вот моя ошибка valgrind: (wordlist.c:76) относится к вызову qsort;

==59199== Invalid read of size 1
==59199==    at 0x10000AAC9: _platform_strcmp (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==59199==    by 0x100000C92: cmp (wordlist.c:23)
==59199==    by 0x1001F2BDE: _qsort (in /usr/lib/system/libsystem_c.dylib)
==59199==    by 0x100000C5E: readWordList (wordlist.c:76)
==59199==    by 0x1000007A8: main (pack.c:52)
==59199==  Address 0x656874 is not stack'd, malloc'd or (recently) free'd
==59199== 
==59199== 
==59199== Process terminating with default action of signal 11 (SIGSEGV)
==59199==  Access not within mapped region at address 0x656874
==59199==    at 0x10000AAC9: _platform_strcmp (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==59199==    by 0x100000C92: cmp (wordlist.c:23)
==59199==    by 0x1001F2BDE: _qsort (in /usr/lib/system/libsystem_c.dylib)
==59199==    by 0x100000C5E: readWordList (wordlist.c:76)
==59199==    by 0x1000007A8: main (pack.c:52)
==59199==  If you believe this happened as a result of a stack
==59199==  overflow in your program's main thread (unlikely but
==59199==  possible), you can try to increase the size of the
==59199==  main thread stack using the --main-stacksize= flag.
==59199==  The main thread stack size used in this run was 8388608.

person Harrison    schedule 12.11.2015    source источник
comment
Где определение Word?   -  person Paul Roub    schedule 12.11.2015
comment
Адрес 0x656874 Читает строку "\x74\x68\x65" ("the"), включая конечный '\0', как адрес. Другими словами, это дважды разыменовывается char*. Вы уверены, что эти * в вызове strcmp должны быть там?   -  person    schedule 12.11.2015
comment
В C вы не должны приводить результат malloc или любой другой функции возвращает void * (например, realloc).   -  person Some programmer dude    schedule 12.11.2015
comment
Я добавил определение слова - извините за это. Это просто массив символов.   -  person Harrison    schedule 12.11.2015
comment
else { continue; } является лишним, но также не влияет на проблему.   -  person Jonathan Leffler    schedule 12.11.2015


Ответы (1)


Ваш код выполняет недопустимое приведение типов: qsort возвращает вам указатель на Word, но ваша функция cmp рассматривает его как постоянный указатель на указатель char.

Поскольку Word является typedef для массива фиксированного размера из char, это не то же самое, что указатель на указатель. Вы должны выполнить приведение к указателю на Word в вашем cmp, а не к указателю на указатель:

static int cmp(const void *p1, const void *p2){
    const Word *lhs = p1;
    const Word *rhs = p2;
    return strcmp((const char*)*lhs, (const char*)*rhs);
}
person Sergey Kalinichenko    schedule 12.11.2015
comment
Разве qsort не передает указатели на элементы в функцию сравнения? Если Word является указателем на char, то cmp должен получить указатель на указатель на char - person Kevin; 12.11.2015
comment
@Кевин Ты прав. Теперь, когда ОП предоставил определение Word, мне нужно было отредактировать исправление. - person Sergey Kalinichenko; 12.11.2015
comment
Это была проблема, однако я не уверен в том, что вы используете lhs и rhs, вы хотели использовать их в вызове strcmp? Несмотря ни на что... спасибо! - person Harrison; 12.11.2015
comment
@Harrison Да, я хотел использовать их в вызове strcmp. Спасибо, что указали на это. - person Sergey Kalinichenko; 12.11.2015