Can the qsort comparison function always return a non-zero value?

空扰寡人 提交于 2021-01-07 01:39:38

问题


An ascending sort callback function for qsort and bsearch on an array of int could look like this:

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

Yet this function seems to violate the constraint on the compar function as specified in the C Standard:

7.22.5.2 The qsort function

Synopsis

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

Description
The qsort function sorts an array of nmemb objects, the initial element of which is pointed to by base. The size of each object is specified by size.

The contents of the array are sorted into ascending order according to a comparison function pointed to by compar, which is called with two arguments that point to the objects being compared. The function shall return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.

If two elements compare as equal, their order in the resulting sorted array is unspecified.

Is this comparison function OK or can it cause undefined behavior?


回答1:


C 2018 7.22.5 4 says:

When the same objects (consisting of size bytes, irrespective of their current positions in the array) are passed more than once to the comparison function, the results shall be consistent with one another. That is, for qsort they shall define a total ordering on the array, and for bsearch the same object shall always compare the same way with the key.

A total order requires that a = a. (To see this from the definition in the Wikipedia page: Connexity says, for any a and b, ab or ba. Substituting a for b gives aa or aa. So aa. Then the condition of antisymmetry is satisfied: We have aa and aa, so a = a.)




回答2:


Using such a function at least with bsearch can result in undefined behavior.

Here is a demonstrative program.

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

The program output is

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

qsort can work depending on its internal implementation.

But in any case you have undefined behavior because the comparison function does not satisfy the requirements.



来源:https://stackoverflow.com/questions/65581178/can-the-qsort-comparison-function-always-return-a-non-zero-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!