C语言 如何重用qsort比较函数以便在不同的比较器之间切换

rkttyhzu  于 2023-01-16  发布在  其他
关注(0)|答案(2)|浏览(119)

我想在qsort中的比较器之间切换,而不是创建5个不同的比较器,但是由于某种原因程序不工作。我不知道为什么会发生这种情况。有人能解释一下吗?谢谢!

struct propertySales{
    unsigned int tanggal[3];
    char pelanggan[30];
    char jenisProperty[30];
    char namaProperty[30];
    int jumlahProperty;
    double hargaProperty;
    double totalPembayaran;
}Arr[100], compare[100], temp;
typedef int (*compfn)(const void*, const void*);
int compare_harga(struct propertySales *, struct propertySales *);
int compare_pembayaran(struct propertySales *, struct propertySales *);
int compare_harga(struct propertySales *elem1, struct propertySales *elem2){
    if( elem1->hargaProperty < elem2->hargaProperty){
        return -1;
    }
    else if(elem1->hargaProperty > elem2->hargaProperty){
        return 1;
    }
    else{
        return 0;
    }   
}

int compare_pembayaran(struct propertySales *elem1, struct propertySales *elem2){
    if( elem1->totalPembayaran < elem2->totalPembayaran){
        return -1;
    }
    else if(elem1->totalPembayaran > elem2->totalPembayaran){
        return 1;
    }
    else{
        return 0;
    }   
}

编译器抛出警告---[警告]从不同大小的整数转换为指针[-Wint-to-pointer-cast]

lbsnaicq

lbsnaicq1#

短代码

让我们从我们的结构开始,我将把它们弄乱,只是为了在里面有更多种类的对象类型。

// property-sales.h

struct Tanggal { // date
    short tahun; // year
    char  bulan; // month
    char  hari;  // day
};
typedef struct Tanggal Tanggal;

struct Penjualan_Properti {
    Tanggal      tanggal_penjualan;   // date of sale      // two different ways
    time_t       tanggal_pembelian;   // date of purchase  // to manage a date
    char         jalan[32];           // street name
    int          nomor;               // number
    const char * kelurahan;           // subdistrict       // These are strings kept 
    const char * kecamatan;           // district          // elsewhere, selected from
    const char * kota_atau_kabupaten; // city or regency   // a list.
    const char * propinsi;            // province          //
    char         kode_pos[6];         // zip code
    double       luasnya_km;          // area (km)
    double       harga;               // price
    double       total_pembayarn;     // total payment to date
    Pelanggan  * pelanggan;           // customer
    char         keadaan;             // status   'U'=tertunda, 'J'=terjual, 'A'=arsip
};
typedef struct Penjualan_Properti Penjualan_Properti;

// Precision for floating point comparisons can be controlled
extern double epsilon;

现在来看比较器功能。

// property-sales.c

#include "property-sales.h"

// Precision for floating point comparisons
double epsilon = 0.00001;

// Now we just list a comparator function for every kind of sort order we want

int membandingkan_nomor_Penjualan_Properti( const void * _a, const void * _b ) {
    // Sort non-decreasing by building number
    const Penjualan_Properti * a = _a;
    const Penjualan_Properti * b = _b;
    return (*a < *b) ? -1 : (*b < *a);
}

int membandingkan_kode_pos_Penjualan_Properti( const void * _a, const void * _b ) {
    // Sort non-decreasing by building number
    const Penjualan_Properti * a = _a;
    const Penjualan_Properti * b = _b;
    return strcmp( a->kode_pos, b->kode_pos );
}

// ...and so on... (for as many ways as you wish to sort two Property_Sales objects)

就是这样!现在要对数据进行排序,您只需要使用适当的比较器函数!

qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_kode_pos_Penjualan_Properti );

英文:

qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_zip_code );

* 较短 * 的代码(带X宏)

使用某些宏可以大大减少重复输入,但这里真正的好处是ODR,它在C和C++中都是一个有用的概念。
这个想法是创建一个单一的信息"表",我们以后将使用它来"构造"我们的对象。

// property-sales.h

#include <time.h>

#define IS_INDEXABLE(arg) (sizeof(arg[0]))
#define IS_ARRAY(arg) (IS_INDEXABLE(arg) && (((void *) &arg) == ((void *) arg)))
#define ARRAY_SIZE(arr) (IS_ARRAY(arr) ? (sizeof(arr) / sizeof(arr[0])) : 0)

struct Tanggal {
    short tahun;   // year
    char  bulan;   // month
    char  hari;    // day
};
typedef struct Tanggal Tanggal;

typedef struct Pelanggan { int foo; } Pelanggan;  // Client struct stub defintion

//     type,         name,         dimensions, comparator function code
#define PENJUALAN_PROPERTI(X) \
    X( Tanggal,      tanggal_penjualan,      , (a->tahun < b->tahun) ? -1 : (a->tahun < b->tahun) ? 1 :   \
                                               (a->bulan < b->bulan) ? -1 : (a->bulan < b->bulan) ? 1 :   \
                                               (a->hari  < b->hari)  ? -1 : (a->hari  < b->hari);       ) \
    X( time_t,       tanggal_pembelian,      , (*a < *b) ? -1 : (*b < *a) ) \
    X( char,         jalan,              [32], strcmp( a, b ) ) \
    X( int,          nomor,                  , (*a < *b) ? -1 : (*b < *a) ) \
    X( const char *, kelurahan,              , strcmp( *a, *b ) ) \
    X( const char *, kecamatan,              , 0 ) /* see note below */ \
    X( const char *, kota_atau_kabupaten,    , strcmp( *a, *b ) ) \
    X( const char *, propinsi,               , strcmp( *a, *b ) ) \
    X( char,         kode_pos,            [6], strcmp(  a,  b ) ) \
    X( double,       luasnya_km,             , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( double,       harga,                  , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( double,       total_pembayarn,        , (fabs(*a-*b) < epsilon) ? 0 : (*a < *b) ? -1 : 1 ) \
    X( Pelanggan *,  pelanggan,              , membandingkan_Pelanggan( *a, *b ) ) \
    X( char,         keadaan,                , status_cmp( *a, *b ) ) \

// Create the struct!
#define F(type, name, extents, code) type name extents;
struct Penjualan_Properti { PENJUALAN_PROPERTI(F) };
typedef struct Penjualan_Properti Penjualan_Properti;
#undef F

// Create comparator function prototypes
#define F(type, name, extents, code) \
    int membandingkan_ ## name ## _Penjualan_Properti( const void * _a, const void * _b );
PENJUALAN_PROPERTI(F)
#undef F

// Precision for floating point comparisons can be controlled
extern double epsilon;

// Order of sorting by status code can be controlled as well:
extern char status_order[4];  // 'U'=tertunda, 'J'=terjual, 'A'=arsip

哇!是的!现在生成比较器函数:

// property-sales.c

#include <stddef.h>
#include <string.h>
#include "property-sales.h"

// Precision for floating point comparisons
double epsilon = 0.00001;

// Helpers to sort status code as we desire. Defaults to:
char status_order[4] = "UJA";

int status_cmp( char _a, char _b )
{
    int a = (int)(strchr( status_order, _a ) - status_order);
    int b = (int)(strchr( status_order, _b ) - status_order);
    return (a < b) ? -1 : (b < a);
}

// Also, that compare_client() function needs to exist
int membandingkan_Pelanggan( const Pelanggan * a, const Pelanggan * b ) {
    return 0;  // Change this to something that properly sorts by client.
               // Since a Pelanggan is a struct, feel free to use any
               // dirty trick you like to compare them by a specific element.
               // For example, a `client_order` global may exist that
               // is itself just a function pointer to a `compare_Client_name`
               // function or a `compare_Client_district` function, etc, which
               // we could use here as:
               //   return client_order( a, b );
}

// Generate the comparator functions!
#define F(type, name, extents, code) \
    int membandingkan_ ## name ## _Penjualan_Properti( const void * _a, const void * _b ) { \
        type const * a = (const void *)((const char *)_a + offsetof(Penjualan_Properti,name)); \
        type const * b = (const void *)((const char *)_b + offsetof(Penjualan_Properti,name)); \
        return code; \
    }
PENJUALAN_PROPERTI(F)
#undef F

这是......令人惊讶的短,对吗?即使有所有的额外代码doign的东西,原来的代码不能,和 * 完整 * 代码为 * 所有 * 比较器函数(因为我们离开了他们都在第一个例子)。

显而易见的代价是,现在我们正处在一个棘手的代码领域。

原始代码非常清晰易读。这东西需要额外的脑力。
它是否值得是其他地方的争论。现在,我们可以很容易地根据任何成员对Property_Sales数组进行排序。

strcpy( status_order, "AUJ" ); // sort by status: Archived, Pending, Sold
qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_keadaan_Penjualan_Properti );

英文:

strcpy( status_order, "APS" ); // sort by status: Archived, Pending, Sold
qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_status );

如果Client结构存在,并且设置为通过特定的成员比较器函数对Client进行排序,那么您也可以通过客户端进行排序:

pelanggan_order = &membandingkan_nama_keluarga_Pelanggan;
qsort( properti, ARRAY_SIZE(properti), sizeof(Penjualan_Properti),
    &membandingkan_pelanggan_Penjualan_Properti

英文:

client_order = &compare_Client_surname;
qsort( properties, ARRAY_SIZE(properties), sizeof(Property_Sales),
    &compare_Property_Sales_client );

就是这样!
所有的代码仍然是重复的,但是我们把所有的重复放入一些宏中,从一个表中为我们 * 生成 * 代码,这个技巧被称为X-macro trick
从上文中注意到:我们通过简单地返回一个0值,使这个地区实际上是不可排序的。2我们本应该像它周围的所有其他元素一样,赋予它排序能力。3重点只是提供一个例子!
我需要指出的是,我还使用了著名的offsetof()宏来使事情变得更好,在排序函数中,ab现在是指向要比较的成员元素的直接指针(而不是像以前那样指向结构本身),请注意这对PENJUALAN_PROPERTI表中的比较代码有何直接影响:注意你所处的间接层。2例如,注意在成员字符串 * array * 和成员字符串 * pointer * 上使用strcmp()的区别。
还要记住qsort()既不是可重入的也不是稳定的,像我们这样使用全局状态意味着我们需要小心线程化和递归使用。
哦,在这里找到ARRAY_SIZE macro(名称略有不同)。
记住,权力越大,责任越大!

ddrv8njm

ddrv8njm2#

OP要求提供较长代码块的示例来说明用法。
(No保证这是适合的。)

int compare_pembayaran( const void *p1, const void *p2 ) {
    const struct propertySales *elem1 = p1, *elem2 = p2;

    if( elem1->totalPembayaran < elem2->totalPembayaran)
        return -1;
    
    return elem1->totalPembayaran > elem2->totalPembayaran;
}

相关问题