Ошибка сегментации при использовании qsort
Я работаю над программой c для чтения из txt
файл и сортировать строки.
data.txt:
jk ef ab cd bc gh fg ij hi de
Вот мой код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
int cmp(const void *p1, const void *p2) {
return strcmp(*(const char **)p1, *(const char **)p2);
}
int main() {
FILE *f = fopen("data.txt", "r");
char s[255][255];
char tmp[255];
int n = 0;
while (!feof(f)) {
fscanf(f, "%s", tmp);
strcpy(s[n], tmp);
n++;
}
fclose(f);
qsort(s, n, sizeof(char *), cmp);
int i = 0;
for (; i < n; i++) {
printf("%s ", s[i]);
}
return EXIT_SUCCESS;
}
Я запустил код на Ubuntu, и он сломался на Segfault. Поверьте, это произошло qsort
и я не мог понять, почему.
Кто-нибудь может дать мне несколько советов?
4 ответа
Функция сравнения неверна, так как вы сортируете массив массивов char
Вы можете передать указатели на элементы strcmp
непосредственно:
int cmp(const void *p1, const void *p2) {
return strcmp(p1, p2);
}
Однако обратите внимание, что ваш цикл синтаксического анализа также неверен: feof()
неверный способ проверить конец файла. Используйте это вместо:
n = 0;
while (n < 255 && fscanf(f, "%254s", s[n]) == 1) {
n++;
}
qsort
В вызове должен быть указан размер элемента массива:
qsort(s, n, sizeof(*s), cmp);
Вот исправленная версия:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cmp(const void *p1, const void *p2) {
return strcmp(p1, p2);
}
int main(void) {
FILE *f = fopen("data.txt", "r");
char s[255][255];
char tmp[255];
int n = 0;
if (f == NULL)
return EXIT_FAILURE;
while (n < 255 && fscanf(f, "%254s", s[n]) == 1) {
n++;
}
fclose(f);
qsort(s, n, sizeof(*s), cmp);
for (int i = 0; i < n; i++) {
printf("%s ", s[i]);
}
printf("\n");
return EXIT_SUCCESS;
}
Многие люди дали хороший ответ.
Вот как вы могли бы найти его самостоятельно, шаг за шагом, с помощью стандартных инструментов GNU:
Мы предполагаем, что исходный файл назван q.c
,
Скомпилируйте с отладочными символами (обратите внимание, что здесь не нужно иметь Makefile):
% make CFLAGS=-g q
cc -g q.c -o q
Теперь запустите программу с помощью отладчика (gdb):
% gdb q
(gdb) run
Starting program: /usr/home/fenyo/tmp/qs/q
Program received signal SIGSEGV, Segmentation fault.
0x00000008009607a6 in strcmp () from /lib/libc.so.7
Теперь посмотрим на кадр стека:
(gdb) where
#0 0x00000008009607a6 in strcmp () from /lib/libc.so.7
#1 0x00000000004009b5 in cmp (p1=0x7ffffffeeb60, p2=0x7ffffffeeb88) at q.c:8
#2 0x000000080093b834 in qsort () from /lib/libc.so.7
#3 0x0000000000400af5 in main () at q.c:26
Итак, ваша проблема в вызове вашей функции cmp
библиотекой qsort, которая вызывает strcmp с плохими указателями.
Итак, мы переходим от одного стекового фрейма к уровню функции cmp:
(gdb) up
#1 0x00000000004009b3 in cmp (p1=0x7ffffffeeb60, p2=0x7ffffffeeb88) at q.c:8
8 return strcmp( *(const char **) p1, *(const char **) p2);
Смотрим на тип р1:
(gdb) ptype p1
type = void *
Поскольку p1 является указателем, мы проверяем его содержимое, отображающее 10 первых байтов:
(gdb) print (*(char *) p1)@10
$43 = "jk\000\000\000\000\000\000\000"
Итак, мы обнаруживаем, что это строка с нулевым символом в конце, содержащая jk
,
Итак, ваш актерский состав недействителен: *(const char **) p1
,
Это должно было быть (const char*) p1
,
Мы меняем актерский состав, и тогда это работает.
qsort()
передает в функцию сравнения два указателя на элементы массива.
Элементы массива имеют тип char[255]
, Так qsort()
Функция сравнения передается в два char(*)[255]
,
Так должно выглядеть
int cmp(const void *p1, const void *p2)
{
const char (*ps1)[255] = p1;
const char (*ps2)[255] = p2;
return strcmp(*ps1, *ps2);
}
char s[255][255];
Это очень плохо пахнет. Используйте C динамическое распределение памяти.
Рассмотрим вместо этого указатель, выделенный кучей, на массив строк, выделенных кучей.
qsort(s, n , sizeof(char *), cmp);
Внимательно прочитайте документацию по qsort (3). С char s[255][255]
ваш звонок в qsort
очень неправильно.
Кто-нибудь может дать мне несколько советов?
Внимательно прочитайте хорошую книгу по программированию на C и документацию по каждой функции, которую вы используете (даже strcmp (3)). Посмотрите также некоторые ссылки на C и загляните в спецификацию C11 n1570.
Компилировать все предупреждения и отладочную информацию, т.е. gcc -Wall -Wextra -g
с GCC (подробнее о вызове GCC). Используйте отладчик gdb
(читайте об отладке с помощью GDB)
PS. Мы не будем делать вашу домашнюю работу.