【问题标题】:Can the qsort comparison function always return a non-zero value?qsort 比较函数能否始终返回非零值?
【发布时间】:2021-04-11 08:06:56
【问题描述】:

qsortbsearchint 数组上的升序排序回调函数可能如下所示:

int ascending(const void *o1, const void *o2) {
    int a = *(const int *)o1;
    int b = *(const int *)o2;
    return a < b ? -1 : 1;
}

然而,这个函数似乎违反了 C 标准中对 compar 函数的约束:

7.22.5.2 qsort 函数

概要

#include <stdlib.h>
void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

说明
qsort 函数对nmemb 对象数组进行排序,base 指向其初始元素。每个对象的大小由size指定。

数组的内容根据compar指向的比较函数升序排序,该函数使用两个指向被比较对象的参数调用。如果认为第一个参数分别小于、等于或大于第二个参数,则该函数应返回一个小于、等于或大于零的整数。

如果两个元素比较相等,则它们在结果排序数组中的顺序是未指定的。

此比较函数是否正常或会导致未定义的行为?

【问题讨论】:

  • 如果没有两个元素相等,那么是的。
  • bsearch 与 qsort 不同——你可以假设 bsearch 会检查等价性。
  • 当然,比较函数的意图是对象必须与自身相等,因此如果o1o2 指向相同,则正确设计的比较函数将返回相等(零)目的。此外,如果o1o2 指向具有相同字节的两个对象,我们希望它返回相等。一个更好的问题可能是,考虑到这样一个损坏的比较函数,是否有任何合理的排序算法在使用它时会失败?
  • 哦,顺便说一句,我认为这应该是一个合法的用例? int dont_sort (const void *o1, const void *o2) { return -1; }
  • a == b 时会发生什么?您的函数将返回1,向qsort 表明a 大于 b。这不会(可能)使排序操作无休止吗?

标签: c sorting language-lawyer qsort


【解决方案1】:

C 2018 7.22.5 4 说:

当相同的对象(由大小字节组成,与它们在数组中的当前位置无关)多次传递给比较函数时,结果应彼此一致。也就是说,对于qsort,它们应定义数组的总排序,对于bsearch,相同的对象应始终以相同的方式与键进行比较。

total order 要求 a = a。 (从维基百科页面的定义来看:Connexity 说,对于任何 abab em> 或 ba。用 a 代替 b 得到 a ≤ aaa. 所以 aa. 那么反对称条件满足:我们有 aaaa,所以 a = 一个。)

【讨论】:

  • 我认为这解决了它,我不知道这一段。所以它正式是UB。
  • 另外,这部分显然是在 C99 中添加的,而在 C90 中没有。
【解决方案2】:

至少在bsearch 中使用这样的函数可能会导致未定义的行为。

这是一个演示程序。

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

int ascending(const void *o1, const void *o2) {
    int a = *(const int *)o1;
    int b = *(const int *)o2;
    return a < b ? -1 : 1;
}

int ascending1(const void *o1, const void *o2) {
    int a = *(const int *)o1;
    int b = *(const int *)o2;

    return ( b < a ) - ( a < b );
}

int main(void) 
{
    int a[] = { 2, 2, 1, 1, 0, 0 };
    const size_t N = sizeof( a ) / sizeof( *a );
    
    qsort( a, N, sizeof( int ), ascending );
    
    for ( size_t i = 0; i < N; i++ )
    {
        printf( "%d ", a[i] );
    }
    putchar( '\n' );
    
    int key = 1;

    int *p = bsearch( &key, a, N, sizeof( int ), ascending );
    
    if ( p ) printf("*p = %d, p - a = %zu\n", *p, ( size_t )( p - a ) );
    else puts( "Oops!" );

    p = bsearch( &key, a, N, sizeof( int ), ascending1 );
    
    if ( p ) printf("*p = %d, p - a = %zu\n", *p, ( size_t )( p - a ) );
    else puts( "Oops!" );
    
    return 0;
}

程序输出是

0 0 1 1 2 2 
Oops!
*p = 1, p - a = 3

qsort 可以根据其内部实现工作。

但在任何情况下,您都有未定义的行为,因为比较函数不满足要求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-10
    • 2018-03-26
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多