簡體   English   中英

qsort比較器分段錯誤

[英]qsort comparator segmentation fault

我正在用C語言編寫一個數據結構庫,我打算將其用於個人項目(我確實意識到有可用的通用庫,但是我認為這將是一次很棒的學習經歷)。 這樣,我創建了一個與內置Python list實現非常相似的數據結構(就公開操作而言)。

當我偶然發現qsort比較器函數遇到一些困難時,我正在為此結構編寫一些單元測試。 對於相同的問題,我已經搜集了大量的SO答復,但似乎沒有建議的修復程序起作用。

下面給出了相關代碼(由於長度原因,省略了其他代碼,但是如果需要,我很樂意將其引入):

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

正如我上面提到的,我在類似的問題中嘗試了許多建議(在Cast上引用指針,將我的調用更改為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的方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM