简体   繁体   English

qsort比较器分段错误

[英]qsort comparator segmentation fault

I am writing a library of data structures in C which I plan to use for a personal project (I do realize there are generic libraries available, but I thought this would be a great learning experience). 我正在用C语言编写一个数据结构库,我打算将其用于个人项目(我确实意识到有可用的通用库,但是我认为这将是一次很棒的学习经历)。 In so doing, I have created a data structure which is quite similar to the built in Python list implementation (in terms of exposed operations). 这样,我创建了一个与内置Python list实现非常相似的数据结构(就公开操作而言)。

I was writing some unit tests for this structure when I stumbled on some difficulty with the qsort comparator function. 当我偶然发现qsort比较器函数遇到一些困难时,我正在为此结构编写一些单元测试。 I've crawled through tons of SO responses about the same issue and none of the recommended fixes seemed to work. 对于相同的问题,我已经搜集了大量的SO答复,但似乎没有建议的修复程序起作用。

Relevant code given below (other code omitted due to length, but I'd be happy to bring it in if it may be needed): 下面给出了相关代码(由于长度原因,省略了其他代码,但是如果需要,我很乐意将其引入):

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

As I mentioned above, I have tried a number of suggestions in similar questions (dereferencing pointers on cast, changing my call to qsort , etc). 正如我上面提到的,我在类似的问题中尝试了许多建议(在Cast上引用指针,将我的调用更改为qsort等)。 The dereferencing trick const char *l = *(const char**)left; 取消引用技巧const char *l = *(const char**)left; results in a segfault (and Valgrind entering an infinite loop). 导致段错误(Valgrind进入无限循环)。

I even got to the point (which you can see some of in the comparator above) where I started printing out pointer locations before and within the comparator and I found most of the pointer locations are not the same. 我什至到达了点(您可以在上面的比较器中看到一些),在比较器之前和之内开始打印指针位置,我发现大多数指针位置都不相同。

Partial output: 部分输出:

/* 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

My best guess for the problem involves the fact that somehow qsort isn't getting the right information about what's in my internal data structure, so it is giving me incorrect pointer offsets. 对于这个问题,我的最佳猜测涉及以下事实: qsort不能正确获取有关内部数据结构中内容的正确信息,因此它给了我错误的指针偏移量。

Other relevant information: 其他相关信息:

  • OS X 10.10.2 OS X 10.10.2
  • cc -v says 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 
  • All of my other unit tests pass. 我所有其他单元测试都通过了。 These cover things like basic insert and remove, as well as a function which returns the index for a given element (using the comparator above). 这些内容包括基本的插入和删除操作,以及返回给定元素的索引的函数(使用上面的比较器)。

Edit: Show some more code. 编辑:显示更多代码。

I think you're misunderstanding what the comparator receives pointers to. 我认为您误会了比较器接收到的指针。 It receives a pointer to the memory holding the data. 它接收指向保存数据的内存的指针。 So, if your data is void*, it receives really void** pointers masqueraded as void*. 因此,如果您的数据为void *,它将收到伪装为void *的真正void **指针。

To fix this, try the following code: 要解决此问题,请尝试以下代码:

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

I can't now test my code as glist_append and glist_get are missing from your code but that's the way qsort should be used. 我现在无法测试我的代码,因为代码中缺少glist_append和glist_get,但是这就是应该使用qsort的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM