c - qsort 比较器段错误

标签 c pointers segmentation-fault qsort

我正在用 C 语言编写一个数据结构库,我计划将其用于个人项目(我确实意识到有可用的通用库,但我认为这将是一次很棒的学习体验)。通过这样做,我创建了一个与内置的 Python list 实现非常相似的数据结构(就公开的操作而言)。

当我偶然发现 qsort 比较器函数遇到一些困难时,我正在为此结构编写一些单元测试。我已经浏览了大量关于同一问题的回复,但推荐的修复似乎都不起作用。

下面给出了相关代码(由于长度原因省略了其他代码,但如果需要的话我很乐意将其引入):

typedef struct glist glist;

struct glist {
    void **data;
    size_t len;
    size_t cap;
    int (*cmp)(void const*, void const*);
    void (*free)(void*);
};

static int list_test_comparator(const void *left, void const *right);

glist* glist_new(int (*cmpfn)(const void*, const void*), void (*freefn)(void*)) {
    glist *list = malloc(sizeof(glist));
    if (!list) {
        return NULL;
    }

    size_t cap = sizeof(void*) * 10;
    list->data = calloc(cap, cap);
    if (!list->data) {
        free(list);
        return NULL;
    }

    list->len = 0;
    list->cap = 10;
    list->cmp = cmpfn;
    list->free = freefn;
    return list;
}

void glist_sort(glist *list) {
    if ((!list) || (list->len == 0)) {return;}
    qsort(list->data, list->len, sizeof(void *), list->cmp);
}

static int list_test_comparator(const void *left, const void *right) {
    const char *l = left;
    const char *r = right;
    printf("Left: %s %p\n", l, left);
    printf("Right: %s %p\n", r, right);
    int res = strcmp(l, r);
    printf("Res: %d\n", res);
    return res;
}

/* Run by CUnit before and after each test case */
void list_test_setup(void) {
    list_test = glist_new(list_test_comparator, free);
}

void list_test_sort(void) {
    for (int i = 0; i < 15; i++) {
        char *some = "%d";
        char *next = malloc(20);
        CU_ASSERT_FATAL(next != NULL);

        sprintf(next, some, rand());
        CU_ASSERT(glist_append(list_test, next) == true);
    }

    /* Note that this is a CUnit test with setup function creating 
     * the structure with the above defined list_test_comparator func */
    glist_sort(list_test);

    int val;
    int prev = -1; /* rand() should always return value between 0 and RAND_MAX */
    for (int i = 0; i < 15; i++) {
        char *test = glist_get(list_test, i);
        sscanf(test, "%*s %d", &val);
        CU_ASSERT(val <= prev);
        prev = val;
    }
}

正如我上面提到的,我在类似的问题中尝试了一些建议(取消引用强制转换上的指针、更改对 qsort 的调用等)。解引用技巧 const char *l = *(const char**)left; 会导致段错误(并且 Valgrind 进入无限循环)。

我什至达到了这样的程度(您可以在上面的比较器中看到一些),我开始在比较器之前和内部打印出指针位置,并且我发现大多数指针位置都不相同。

部分输出:

/* Before sort */
282475249 0x7fb629c04f30
1622650073 0x7fb629c04f50
984943658 0x7fb629c04f70
1144108930 0x7fb629c04f90
470211272 0x7fb629c04fb0
101027544 0x7fb629c04fd0
1457850878 0x7fb629c04ff0
1458777923 0x7fb629c05010
2007237709 0x7fb629c05030
823564440 0x7fb629c05050
1115438165 0x7fb629c05200
1784484492 0x7fb629c053b0
74243042 0x7fb629c053d0
114807987 0x7fb629c053f0

/* In comparator */
Left: O?)? 0x7fb629c05070
Right: O?)? 0x7fb629c050a8
Res: -224
Left: ?O?)? 0x7fb629c050a8
Right: ?O?)? 0x7fb629c050e0
Res: -4
Left: 0O?)? 0x7fb629c05078
Right: 0O?)? 0x7fb629c05070
Res: -192

我对该问题的最佳猜测涉及这样一个事实:qsort 无法获取有关我的内部数据结构中内容的正确信息,因此它给了我不正确的指针偏移量。

其他相关信息:

  • OS X 10.10.2
  • cc -v

    Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
    Target: x86_64-apple-darwin14.1.0
    Thread model: posix
    
  • 我的所有其他单元测试都通过了。这些涵盖了基本的插入和删除等内容,以及返回给定元素索引的函数(使用上面的比较器)。

编辑:显示更多代码。

最佳答案

我认为您误解了比较器接收的指针。它接收一个指向保存数据的内存的指针。因此,如果您的数据是 void*,它会收到伪装成 void* 的真正 void** 指针。

要解决此问题,请尝试以下代码:

static int list_test_comparator(const void *left, const void *right) {
    const void *leftptr = *(const void**)left;
    const void *rightptr = *(const void**)right;
    const char *l = leftptr;
    const char *r = rightptr;
    printf("Left: %s %p\n", l, left);
    printf("Right: %s %p\n", r, right);
    int res = strcmp(l, r);
    printf("Res: %d\n", res);
    return res;
}

我现在无法测试我的代码,因为您的代码中缺少 glist_append 和 glist_get,但这就是应该使用 qsort 的方式。

关于c - qsort 比较器段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29145912/

相关文章:

c - 如何在字符串中搜索特定字符、数字或标点符号

c - OpenMP 中 private() 子句中变量的最大数量

c - 哪些可移植性问题与 C 语言中指针的字节级访问相关?

c - C 中的奇怪段错误

c - 了解 C 中的线程

c++ - 停止在 C 中刷新 io 缓冲区

c++ - clang 8.0 的编译器错误 -- 段错误 -- 在 Macos Sierra 10.12.3 上

c - 如何在while循环中清除 'segmentation fault (core dumped)'

java - 为什么数组赋值不兼容,尽管它们的数据类型是兼容的?

c++ - 匹配 bool vs const void* 重载的函数的地址