C qsort函数-尝试使用比较器

C qsort函数-尝试使用比较器,c,C,我为一个更大的程序做了一个qsort函数。它是按时间排序的。我有一个课程表,我正在工作,并发现需要比较上午和下午的时间。如果选择了A,那么上午所有的课都要上课,如果选择了P,那么下午所有的课都要上课。我的问题是,有没有一种方法可以使用大于或小于比较器的排序函数?如果是这样的话,有人能告诉我如果没有太多麻烦怎么办吗 int sortFunction(const void *p, const void *q) { return ((sched_record *) p)->start.h

我为一个更大的程序做了一个qsort函数。它是按时间排序的。我有一个课程表,我正在工作,并发现需要比较上午和下午的时间。如果选择了A,那么上午所有的课都要上课,如果选择了P,那么下午所有的课都要上课。我的问题是,有没有一种方法可以使用大于或小于比较器的排序函数?如果是这样的话,有人能告诉我如果没有太多麻烦怎么办吗

int sortFunction(const void *p, const void *q) {
    return ((sched_record *) p)->start.hour -
                   ((sched_record *) q)->start.hour;
}

假设您的函数
大于
,则可以实现
sortFunction
,如下所示:

int sortFunction(const void *p, const void *q) {
    if (greaterThan(p, q)) { // p > q
        return +1;
    } else if (greaterThan(q, p)) { // p < q
        return -1;
    } else { // p == q
        return  0;
    }
}
int排序函数(const void*p,const void*q){
如果(大于(p,q)){//p>q
返回+1;
}else如果(大于(q,p)){//p
假设您的函数
大于
,则可以按如下方式实现
排序函数

int sortFunction(const void *p, const void *q) {
    if (greaterThan(p, q)) { // p > q
        return +1;
    } else if (greaterThan(q, p)) { // p < q
        return -1;
    } else { // p == q
        return  0;
    }
}
int排序函数(const void*p,const void*q){
如果(大于(p,q)){//p>q
返回+1;
}else如果(大于(q,p)){//p
只需添加AM和PM的单独检查,并使任何AM时间小于PM时间:

int sortFunction(const void *p, const void *q) {
  return
    (sched_record *) p)->am_pm < (sched_record *) q)->am_pm ?
      -1 :
    (sched_record *) p)->am_pm > (sched_record *) q)->am_pm ?
       1 :
       ((sched_record *) p)->start.hour -
               ((sched_record *) q)->start.hour;
}

只需添加AM和PM的单独检查,并使任何AM时间小于PM时间:

int sortFunction(const void *p, const void *q) {
  return
    (sched_record *) p)->am_pm < (sched_record *) q)->am_pm ?
      -1 :
    (sched_record *) p)->am_pm > (sched_record *) q)->am_pm ?
       1 :
       ((sched_record *) p)->start.hour -
               ((sched_record *) q)->start.hour;
}
编写比较器 在C语言中,
qsort()
中的比较器函数返回一个小于、等于或大于零的数字,这取决于第一个参数表示的数据结构是否应在第二个参数之前、等于或之后排序

整理上午和下午的时间是痛苦的;即使是从转换也不完全是微不足道的。将时间值存储为24小时的符号(甚至是从纪元开始的秒数)要好得多。表示层应处理以am/pm表示法表示时间的问题;模型和控制器层通常应避免与am/pm混淆。别忘了:

12:01 am happens 1 hour   before  1:01 am
11:59 am happens 1 minute before 12:00 pm
12:00 pm happens 1 hour   before  1:00 pm
假设您不受限于从小时开始的事件,并且您已决定在内部使用24小时时间,那么您可以编写如下代码:

int SchedRecordTimeComparator(void const *v1, void const *v2)
{
    sched_record const *r1 = v1;  /* I don't mind a cast; there are those who do */
    sched_record const *r2 = v2;
    if (r1->start.hour < r2->start.hour)
        return -1;
    else if (r1->start.hour > r2->start.hour)
        return +1;
    else if (r1->start.minute < r2->start.minute)
        return -1;
    else if (r1->start.minute > r2->start.minute)
        return +1;
    else
        return  0;
}
就这些。它可以是一个固定大小的数组:

sched_record array[NUM_RECORDS];
然后,假设您仍然有一个变量
size\t num\u records
,该变量指示有多少记录在使用中,则使用相同的
qsort()
调用。使用
qsort()
非常简单。使用
bsearch()
稍微复杂一些,因为通常需要伪造记录才能找到:

sched_record *SchedRecordSearch(int hour, int minute, sched_record *array, size_t num_records)
{
    sched_record key = { .start.hour = hour, .start.minute = minute };
    return bsearch(&key, array, num_records, sizeof(*array), SchedRecordTimeComparator);
}
使用C99和指定的初始值设定项使其更容易。您必须确保您的密钥记录在比较器将使用的每个字段中都有一个适当的值。当然,在使用
bsearch()
对数组进行排序之前,您已经使用
qsort()
对数组进行了排序,或者确保数据的排序顺序与使用相同比较器对数组进行排序时的顺序相同

还值得编写一个函数来检查数组的排序顺序——它是直接的,作为“读者的练习”。例如,您可以在断言中使用它


不要编写自己的
qsort()
我注意到,我们所有回答这个问题的人都假设您正在使用标准的C库排序函数,但您的问题表明您已经编写了自己的函数。一般来说,您必须非常出色,才能比系统提供的
qsort()
做得更好;除非我能证明系统功能太慢,否则我不会费心写我自己的

  • 使用系统提供的
    qsort()
    ,直到您不需要询问如何为自己编写一个
如果仍然必须编写代码,则需要决定接口。您可以模拟标准接口(但使用不同的名称),也可以编写绑定到一个特定类型的自定义接口(如果需要对不同类型进行排序,则必须重新参数化)。后者是C++与模板的大致关系。

编写自己的泛型比较器的一个问题是,在事先不知道元素有多大的情况下交换元素。如果你能使用C99和VLA,那也不算太糟糕,不过如果一个元素的大小影响了你的堆栈,那么你就完全被套住了

在函数内部,您必须小心使用
char*
而不是
void*
,因为您不能合法地在
void*
上执行指针算术,尽管GCC允许您作为非标准扩展执行此操作

您需要确保清楚地了解数据的布局,以及排序代码将对其执行的操作。您将使用各种答案中描述的比较器,当您需要进行比较时,您将执行以下操作:

 int cmp = (*Comparator)(char_base + (i * element_size), char_base + (j * element_size));
然后,您可以执行以下操作:

 if (cmp < 0)
     act on "element i smaller than element j"
 else if (cmp > 0)
     act on "element i greater than element j"
 else
     act on "elements i and j are equal"
  • 生成正确的键-
    lo
    hi

    typedef struct sched_range
    {
        sched_record *lo;
        sched_record *hi;
    } sched_range;
    
    sched_record lo, hi;
    if (choice == 'A')
    {
        lo.start.hour = 0;        /* Midnight (am) */
        hi.start.hour = 12 * 60;  /* Midday */
    }
    else if (choice == 'D')
    {
        lo.start.hour = 12 * 60;  /* Midday */
        hi.start.hour = 24 * 60;  /* Midnight (pm) */
    }
    else
    {
        lo.start.hour = 0;        /* Midnight (am) */
        hi.start.hour = 24 * 60;  /* Midnight (pm) */
    }
    
  • 编写
    SchedRangeSearch()
    函数:

    sched_range SchedRangeSearch(sched_record const *array, size_t num_records,
                    sched_record *lo, sched_record *hi,
                    int (*comparator)(void const *v1, void const *v2))
    {
         sched_range r = { 0, 0 };
         sched_record const *ptr = array;
         sched_record const *end = array + num_records;
    
         /* Skip records before start time */
         while (ptr < end && (*comparator)(lo, ptr) < 0)
             ptr++;
         if (ptr >= end)
             return r;  /* No matching records */
    
         r.lo = ptr;  /* First record in range */
    
         /* Find first record after finish time - if any */
         while (ptr < end && (*comparator)(ptr, hi) < 0)
             ptr++;
    
         r.hi = ptr;
         return r;
    }
    
  • 显示相关记录:

    if (r.lo != 0)
    {
        assert(r.hi != 0);
        sched_record *ptr;
    
        for (ptr = r.lo; ptr < r.hi; ptr++)
            show_sched_record(ptr);
    }
    else
        show_empty_schedule();
    
    if(r.lo!=0)
    {
    断言(r.hi!=0);
    sched_记录*ptr;
    对于(ptr=r.lo;ptr
  • 未经测试的代码:小心崩溃、越界访问等

    其目的是搜索函数提供两个指针,指向范围的开始(范围中的第一个有效项)和结束,其中结束指针指向最后一个有效项之外。因此,显示有效数据的
    for
    循环从开始到结束严格小于结束。(这是C++中与STL迭代器相同的约定。重用好的想法是值得的。)< /P>编写比较器 在C语言中,
    qsort()
    中的比较器函数根据第一个参数表示的数据结构是否正确返回一个小于、等于或大于零的数字
    sched_range SchedRangeSearch(sched_record const *array, size_t num_records,
                    sched_record *lo, sched_record *hi,
                    int (*comparator)(void const *v1, void const *v2))
    {
         sched_range r = { 0, 0 };
         sched_record const *ptr = array;
         sched_record const *end = array + num_records;
    
         /* Skip records before start time */
         while (ptr < end && (*comparator)(lo, ptr) < 0)
             ptr++;
         if (ptr >= end)
             return r;  /* No matching records */
    
         r.lo = ptr;  /* First record in range */
    
         /* Find first record after finish time - if any */
         while (ptr < end && (*comparator)(ptr, hi) < 0)
             ptr++;
    
         r.hi = ptr;
         return r;
    }
    
    sched_range r = SchedRangeSearch(array, num_records, &lo, &hi, SchedRecordTimeComparator);
    
    if (r.lo != 0)
    {
        assert(r.hi != 0);
        sched_record *ptr;
    
        for (ptr = r.lo; ptr < r.hi; ptr++)
            show_sched_record(ptr);
    }
    else
        show_empty_schedule();