qsort比较器分段错误
qsort comparator segmentation fault
我正在用 C 语言编写一个数据结构库,我打算将其用于个人项目(我确实知道有可用的通用库,但我认为这将是一次很好的学习经历)。通过这样做,我创建了一个与内置 Python list
实现非常相似的数据结构(就公开操作而言)。
当我在使用 qsort
比较器函数时遇到一些困难时,我正在为此结构编写一些单元测试。我已经浏览了大量关于同一问题的 SO 响应,并且 none 的推荐修复似乎有效。
相关代码如下(其他代码因篇幅省略,如有需要我会很乐意带入):
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
我所有的其他单元测试都通过了。这些包括基本的插入和删除,以及 returns 给定元素的索引(使用上面的比较器)的函数。
编辑:显示更多代码。
我认为您误解了比较器接收到的指针。它接收指向保存数据的内存的指针。因此,如果您的数据是 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 语言编写一个数据结构库,我打算将其用于个人项目(我确实知道有可用的通用库,但我认为这将是一次很好的学习经历)。通过这样做,我创建了一个与内置 Python list
实现非常相似的数据结构(就公开操作而言)。
当我在使用 qsort
比较器函数时遇到一些困难时,我正在为此结构编写一些单元测试。我已经浏览了大量关于同一问题的 SO 响应,并且 none 的推荐修复似乎有效。
相关代码如下(其他代码因篇幅省略,如有需要我会很乐意带入):
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
我所有的其他单元测试都通过了。这些包括基本的插入和删除,以及 returns 给定元素的索引(使用上面的比较器)的函数。
编辑:显示更多代码。
我认为您误解了比较器接收到的指针。它接收指向保存数据的内存的指针。因此,如果您的数据是 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 的方式。