简体   繁体   English

如何制作一个 qsort function 并对指向结构的指针数组进行排序

[英]how to make a qsort function and sort array of pointers to structs

i'm trying to make a qsort function from scratch that sorts an array of pointers to structs我正在尝试从头开始制作一个 qsort function 来对指向结构的指针数组进行排序

this is the code i have right now这是我现在拥有的代码

static void swap(int *a, int *b) {
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

void _qsort(void* list, int list_len, int left, int right, 
            int(*comp)(const struct shpg_item *a, const struct shpg_item *b)) {
  void *vt, *v3; 
  int i, last, mid = (left + right) / 2; 
  if (left >= right) 
    return; 

  void* vl = (char*)(list + (left * list_len)); 
  void* vr = (char*)(list + (mid * list_len)); 
  swap(vl, vr); 
  last = left; 
  for (i = left + 1; i <= right; i++) { 

    // vl and vt will have the starting address  
    // of the elements which will be passed to  
    // comp function. 
    vt = (char*)(list + (i * list_len)); 
    if ((*comp)(vl, vt) > 0) { 
      ++last; 
      v3 = (char*)(list + (last * list_len)); 
      swap(vt, v3); 
    } 
  } 
  v3 = (char*)(list + (last * list_len)); 
  swap(vl, v3); 
  _qsort(list,list_len, left, last - 1, comp);
  trace_int(1);
  _qsort(list, list_len, last + 1, right, comp); 
}

void list_sort(struct shpg_item **list, int list_len,
               int(*comp)(const struct shpg_item *a, const struct shpg_item *b)) {
  _qsort(*list,list_len,0,(list_len-1),comp);
}

but this gives a segmentation fault error, can any one tell me why and help me?但这给出了分段错误错误,任何人都可以告诉我为什么并帮助我吗?

void * pointer addition void *指针加法

void * pointer addition is undefined behavior. void *指针添加是未定义的行为。 But since the usual UB is OK, this may or may not be OP's trouble.但是由于通常的UB是可以的,这可能是也可能不是OP的麻烦。

void _qsort(void* list, int list_len, int left, ...
    ...
    (list + (left * list_len))  // UB

Instead recommend casting before addition.相反,建议在添加之前进行强制转换。

// void* vl = (char*)(list + (left * list_len)); 
void* vl = ((char*) list) + (left * list_len); 

Other issues may exist可能存在其他问题

I haven't check the entire code but your swap function seems wrong.我没有检查整个代码,但你的交换 function 似乎是错误的。 Depending on the comment lines in your code;取决于代码中的注释行;

// vl and vt will have the starting address  
// of the elements which will be passed to  
// comp function. 

if (list + (left * list_len)) and (list + (last * list_len)) are pointers to be swapped (pointers to a string or a struct, for example), your swap function decoration & your caller line should read as:如果(list + (left * list_len))(list + (last * list_len))是要交换的指针(例如指向字符串或结构的指针),则您的交换 function 装饰和您的调用者行应读为:

  1. Swapping two integers, floats, doubles, etc (in general swapping values only ):交换两个整数、浮点数、双精度数等(一般只交换值):
void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}
...
int x = 5;
int y = 3;
swap(&x, &y);
  1. If you need to swap two pointers (a char * string or another type of pointer pointing to a struct), you can just swap pointer values without swapping the content pointed in the actual memory:如果您需要交换两个指针(一个char *字符串或指向结构的其他类型的指针),您可以只交换指针值而不交换实际 memory 中指向的内容:
void swap(void **a, void **b) {
    void *t = *a;
    *a = *b;
    *b = t;
}
...
char *x = "some string";
char *y = "some other string";
swap(&x, &y);

I've included a working example in the middle part of this answer, and also added an example using qsort.我在这个答案的中间部分包含了一个工作示例,并且还添加了一个使用 qsort 的示例。

Taking a quick look at the code I see problem here:快速查看我在这里看到问题的代码:

void _qsort(void* list, ...

Since list is an array of pointers it should be:由于 list 是一个指针数组,它应该是:

void _qsort(void** list, ...

or或者

void _qsort(void* list[], ...

With this declaration, pointer arithmetic will not be an issue, for example, list+3 == &list[3] == pointer to the 3rd pointer in the array.有了这个声明,指针运算就不会成为问题,例如,list+3 == &list[3] == 指向数组中第三个指针的指针。 There's no need to cast list, as void** list will work fine in the main part of the code.不需要强制转换列表,因为 void** list 在代码的主要部分可以正常工作。 The only code that will do any casting is the caller's compare function.唯一可以进行任何转换的代码是调用者的比较 function。

You can choose to emulate qsort's compare function parameters using type void **: compare(list+i, list+j), but it would be simpler to use type void *: compare(list[i], list[j]).您可以选择使用类型 void **: compare(list+i, list+j) 来模拟 qsort 的比较 function 参数,但使用类型 void *: compare(list[i], list[j]) 会更简单。

Swap should use void** as parameters.交换应使用 void** 作为参数。 The call would be电话是

    swap(list+i, list+j)

/*   ... */

void swap(void **i, void **j){
void * t;
    t = *i;
    *i = *j;
    *j = t;
}

There are some comments about a void pointer possibly having a different size than a struct pointer or any type of data pointer, and that this could cause an issue.有一些关于 void 指针可能具有与结构指针或任何类型的数据指针不同的大小的评论,这可能会导致问题。 If this was true, then the C library function qsort() would not work because the first parameter for qsort is a void pointer, which will result in the caller's pointer being cast to a void pointer.如果这是真的,那么 C 库 function qsort() 将不起作用,因为 qsort 的第一个参数是 void 指针,这将导致调用者的指针被强制转换为 void 指针。 In the caller's compare function, both parameters are const void pointers which the caller's compare function has to cast to the actual pointer types.在调用者的比较 function 中,两个参数都是 const void 指针,调用者的比较 function 必须转换为实际的指针类型。 With qsort() and the caller's compare function, parameters are being cast both to and from void pointers without issue.使用 qsort() 和调用者的比较 function,参数可以毫无问题地转换为 void 指针和从 void 指针转换。

C guarantees that a void pointer can be used to hold any type of data pointer, so in essence a void pointer is a generic data pointer (in 16 bit segment or selector environments, a generic "near" data pointer). C 保证 void 指针可用于保存任何类型的数据指针,因此本质上 void 指针是通用数据指针(在 16 位段或选择器环境中,通用“近”数据指针)。


This is a working example, using typical Lomuto partition scheme (pivot = a[hi]):这是一个工作示例,使用典型的 Lomuto 分区方案 (pivot = a[hi]):

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int  data;
    char name[32];
}XMPL;

int cmpr(void * pi, void *pj)
{
    if(((XMPL *)pi)->data < ((XMPL *)pj)->data)
        return -1;
    if(((XMPL *)pi)->data > ((XMPL *)pj)->data)
        return  1;
    return 0;
}

void swap(void **i, void **j){
void * t;
    t = *i;
    *i = *j;
    *j = t;
}

void QuickSort(void **a, int lo, int hi, int(*cmpp)(void *, void *))
{
void *p;
int i, j;

    while(lo < hi){
        p = a[hi];
        i = lo;
        for(j = lo; j < hi; ++j){
            if((cmpp(a[j], p) < 0)){
                swap(a+i, a+j);
                ++i;
            }
        }
        swap(a+i, a+hi);
        if(i - lo <= hi - i){           /* avoid stack overflow */
            QuickSort(a, lo, i-1, cmpp);
            lo = i+1;
        } else {
            QuickSort(a, i+1, hi, cmpp);
            hi = i-1;
        }
    }
}

#define COUNT (1024)

int main(int argc, char**argv)
{
XMPL *ax;                               /* array of structures */
XMPL **pax;                             /* array of pointers to structures */
int i;

    ax =  malloc(COUNT * sizeof(XMPL));
    pax = malloc(COUNT * sizeof(void **));

    for(i = 0; i < COUNT; i++){         /* init structs, array of ptrs */
        ax[i].data = rand();
        pax[i] = ax+i;
    }

    QuickSort(pax, 0, COUNT-1, cmpr);

    for(i = 1; i < COUNT; i++){
        if(pax[i-1]->data > pax[i]->data){
            break;
        }
    }
    if(i == COUNT)
        printf("passed\n");
    else
        printf("failed\n");
    
    free(pax);
    free(ax);

    return(0);
}

Hoare parition scheme will probably be a bit faster. Hoare 分区方案可能会快一点。 However, in this case, merge sort should be faster than quick sort.但是,在这种情况下,归并排序应该比快速排序更快。 Merge sort does more moves but fewer compares than quick sort, and in this case, only pointers are being moved, while the compare involves an indirection via a pointer and a call to a compare function via a pointer.与快速排序相比,合并排序移动更多,但比较更少,在这种情况下,只有指针被移动,而比较涉及通过指针的间接寻址和通过指针对比较 function 的调用。


Same basic code, but using qsort.相同的基本代码,但使用 qsort。 Note that the cmpr() function needed one more dereference for each parameter.请注意, cmpr() function 需要对每个参数再取消引用。

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int  data;
    char name[32];
}XMPL;

int cmpr(const void * pi, const void *pj)
{
    if((*(XMPL **)pi)->data < (*(XMPL **)pj)->data)
        return -1;
    if((*(XMPL **)pi)->data > (*(XMPL **)pj)->data)
        return  1;
    return 0;
}

#define COUNT (1024)

int main(int argc, char**argv)
{
XMPL *ax;                               /* array of structures */
XMPL **pax;                             /* array of pointers to structures */
int i;

    ax =  malloc(COUNT * sizeof(XMPL));
    pax = malloc(COUNT * sizeof(void **));

    for(i = 0; i < COUNT; i++){         /* init structs, array of ptrs */
        ax[i].data = rand();
        pax[i] = ax+i;
    }

    qsort(pax, COUNT, sizeof(XMPL *), cmpr);

    for(i = 1; i < COUNT; i++){
        if(pax[i-1]->data > pax[i]->data){
            break;
        }
    }
    if(i == COUNT)
        printf("passed\n");
    else
        printf("failed\n");
    
    free(pax);
    free(ax);

    return(0);
}

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

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