qsort segfault в C

Я пытаюсь использовать qsort в соответствии с справочной страницей, но независимо от того, что я пытаюсь, я продолжаю получать segfault

Вот часть кода, которая имеет значение

int compare_dirent(const void *a, const void *b)
{
    const struct dirent *first = (const struct dirent *) a;
    const struct dirent *second = (const struct dirent *) b;

    return first->d_ino - second->d_ino;
}


int process(FILE* output,const char *dirname, int flags)
{
    struct dirent *entries = NULL;
    struct dirent *table[256];
    int entry_num = 0;
    DIR *directory = NULL;
    char cwd[1024];

    getcwd(cwd,1024);
    bzero(table,256);

    directory = opendir(dirname);
    while((entries = readdir(directory))!=NULL)
    {
        if(entries->d_type == DT_REG)
        {
            fprintf(output,"%s\t\n",entries->d_name);
            table[entry_num] = entries;
            entry_num++;
        }
    }
    fprintf(stderr,"last entry: %s\n", table[entry_num-1]->d_name);

    /* RIGHT HERE */
    qsort(table, entry_num, sizeof(struct dirent), &compare_dirent);

    return entry_num;
}

При запуске gdb я вижу список файлов в каталоге по fprintf в цикле while и вижу последнюю запись. Я поставил точку останова в сравнении, которая выполняется N раз, где N — количество файлов, а затем я сразу же получаю SEGFAULT от _qsort.

При N-м вызове compare_dirent из qsort происходит сбой.

Вот вывод gdb

Starting program: /Users/luke/Documents/Dev/code/cs647/prog2/bin/prog2 ./
Reading symbols for shared libraries +........................ done
.main.c.swp 
get_pdf.sh  
main.c  
main.o  
Makefile    
program2.pdf    
test.txt    
last entry: test.txt

Breakpoint 1, compare_dirent (a=0x7fff5fbff018, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) n
89      const struct dirent *second = (const struct dirent *) b;
(gdb) n
91      return first->d_ino - second->d_ino;
(gdb) c
Continuing.

Breakpoint 1, compare_dirent (a=0x7fff5fbff430, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) c
Continuing.

Breakpoint 1, compare_dirent (a=0x7fff5fbff848, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) c
Continuing.

Breakpoint 1, compare_dirent (a=0x7fff5fbffc60, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) c
Continuing.

Breakpoint 1, compare_dirent (a=0x7fff5fc00078, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) c
Continuing.

Breakpoint 1, compare_dirent (a=0x7fff5fc00490, b=0x7fff5fbfec00) at main.c:88
88      const struct dirent *first = (const struct dirent *) a;
(gdb) c
Continuing.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5fc00490
0x00007fff8e238540 in _qsort ()

Решение (полное) было немного обоих ответов

int compare_dirent(const void *a, const void *b)
{
    const struct dirent *first = (const struct dirent *) a;
    const struct dirent *second = (const struct dirent *) b;

    return first->d_ino - second->d_ino;
}


int process(FILE* output,const char *dirname, int flags)
{
    struct dirent *entries = NULL;
    struct dirent table[256];
    int entry_num = 0;
    DIR *directory = NULL;
    char cwd[1024];

    getcwd(cwd,1024);
    bzero(table,256);

    directory = opendir(dirname);
    while((entries = readdir(directory))!=NULL)
    {
        if(entries->d_type == DT_REG)
        {
            fprintf(output,"%s\t\n",entries->d_name);
            memcpy(table+entry_num, entries, sizeof(struct dirent));
            entry_num++;
        }
    }
    fprintf(stderr,"size: %lu\n", sizeof(struct dirent));
    qsort(table, entry_num, sizeof(struct dirent) , compare_dirent);

    fprintf(output,"\n\n");
    for(int i=0;i<entry_num;i++)
    {
        fprintf(output,"%s\n", table[i].d_name);
    }

    return entry_num;
}

person lukecampbell    schedule 09.04.2012    source источник


Ответы (2)


Одна из ваших проблем:

qsort(table, entry_num, sizeof(struct dirent), &compare_dirent);

должно быть:

qsort(table, entry_num, sizeof(struct dirent *), &compare_dirent);

Третий элемент — width, размер каждого объекта. Поскольку table представляет собой массив struct dirent *, это то, что вам нужно.

Вы также неправильно используете значение, возвращаемое readdir. Из документов:

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

Это означает, что вполне возможно, что все значения в вашей таблице являются одним и тем же указателем с одним и тем же значением. Вы можете использовать readdir_r или просто выделить struct dirent (по одному для каждого вызова readdir) и запомнить значение на месте.

В качестве альтернативы можно изменить его на массив struct dirent, и в этом случае вы будете использовать исходный вызов qsort.

person Matthew Flaschen    schedule 10.04.2012

Ваш код не сохраняет dirents! Вы выделяете память для хранения указателей на них, но никогда не выделяете память для хранения фактических записей. Поэтому, когда вы переходите к сортировке, ваши указатели указывают на объекты, которых больше не существует.

Эта строка кода не работает:

table[entry_num] = entries;

Это сохраняет указатель, указывающий на dirent сейчас. Но этот указатель становится бессмысленным позже, когда он больше не указывает на dirent. Вы не можете сохранить указатель для последующего использования, вы должны сохранить фактическую запись.

Данные, возвращаемые readdir(), могут быть перезаписаны последующими вызовами readdir() для того же потока каталога.

Итак, вам нужно что-то вроде:

table[entry_name] = malloc(sizeof(struct dirent));
memcpy(table[entry_name], entries, sizeof(struct dirent));

Не забудьте освободить их, когда закончите.

Ответ Мэтью Флашена также правильный. Вы передали неправильный размер в qsort.

person David Schwartz    schedule 10.04.2012
comment
Также спасибо, это определенно была проблема, но SEGFAULT не генерировался напрямую из-за приведения, но данные действительно были утеряны и неверны. Я изменил его, чтобы использовать memcpy. Я собираюсь переключиться на malloc позже. - person lukecampbell; 10.04.2012
comment
@lukecampbell, куда ты собираешься записать его в? Указатель по-прежнему должен указывать на место, которое вы выделили. - person Matthew Flaschen; 10.04.2012
comment
Может быть, он сделал массив из struct dirent? - person David Schwartz; 10.04.2012
comment
Я отредактировал вопрос, чтобы отразить рабочее решение, я уже начал использовать динамические массивы в реальном коде, это всего лишь кусок. - person lukecampbell; 10.04.2012
comment
Эта qsort такая боль, что иногда я использую qsort, созданный из исходного кода, только потому, что это не черный ящик. Вы можете увидеть, что внутри, типы данных, как это работает и т.д. - person Alan Corey; 15.12.2020