【问题标题】:qsort segfault in CC中的qsort段错误
【发布时间】:2012-04-09 23:58:17
【问题描述】:

我正在尝试按照手册页使用 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);
            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 时,我在 while 循环中看到 fprintf 目录中的文件列表,并看到最后一个条目。我在 compare 中放置了一个断点,该断点执行 N 次,其中 N 是文件数,然后我立即从 _qsort 获得 SEGFAULT。

在第 N 次从 qsort 调用 compare_dirent 时,它崩溃了。

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

【问题讨论】:

    标签: c segmentation-fault qsort


    【解决方案1】:

    你的一个问题是:

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

    应该是:

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

    第三个元素是width,每个对象的大小。因为tablestruct dirent * 的数组,所以这就是你想要的大小。

    您还滥用了 readdir 返回的值。来自the docs

    readdir() 返回的指针指向的数据可能是 被同一目录流上的另一个 readdir() 调用覆盖。

    这意味着很可能您表中的所有值都是相同的指针,具有相同的值。您可以使用readdir_r,或者只分配struct dirent(每个readdir 调用一个),然后将值保存到位。

    另一种方法是将其更改为struct dirent 的数组,在这种情况下,您将使用原来的qsort 调用。

    【讨论】:

      【解决方案2】:

      您的代码没有保存dirents!您分配内存来保存指向它们的指针,但是您永远不会分配任何内存来保存实际条目。因此,当您进行排序时,您的指针指向不再存在的对象。

      这行代码坏了:

      table[entry_num] = entries;
      

      这会保存指向dirent 现在的指针。但是后来当它不再指向dirent 时,该指针就毫无意义了。您不能保存指针以供以后使用,您必须保存实际条目。

      readdir() 返回的数据可能会被后续对同一目录流的 readdir() 调用覆盖。

      所以你需要这样的东西:

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

      完成后不要忘记释放它们。

      Matthew Flaschen 的回答也是正确的。您将错误的大小传递给qsort

      【讨论】:

      • 也谢谢你,这绝对是一个问题,但由于强制转换没有直接生成 SEGFAULT,但数据确实已经消失并且不正确。我将其更改为使用 memcpy。我稍后会切换到 malloc。
      • @lukecampbell,你打算把它保存到哪里?指针仍然必须指向您分配的位置。
      • 也许他做了一个struct dirent的数组?
      • 我编辑了这个问题以反映一个可行的解决方案,我已经开始在实际代码中使用动态数组,这只是一个块。
      • 这个 qsort 太痛苦了,有时我使用从源代码构建的 qsort 只是因为它不是一个黑盒子。你实际上可以看到里面有什么,数据类型,它是如何工作的,等等。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      • 2021-10-31
      • 2020-03-17
      • 2018-02-15
      • 2016-02-13
      • 2021-12-31
      • 1970-01-01
      相关资源
      最近更新 更多