2008-11-29 6 views
4

J'ai une structure:Comment écrire une fonction de comparaison pour qsort de stdlib?

struct pkt_ 
{ 
    double x; 
    double y; 
    double alfa; 
    double r_kw; 
}; 

typedef struct pkt_ pkt; 

Une table de ces structures:

pkt *tab_pkt; 

tab_pkt = malloc(ilosc_pkt * sizeof(pkt)); 

Ce que je veux faire est de trier tab_pkt par tab_pkt.alfa et tab_pkt.r:

qsort(tab_pkt, ilosc_pkt, sizeof(pkt), porownaj); 

Où porownaj est une fonction de comparaison, mais comment l'écrire? Voici mon « sketch » de celui-ci:

int porownaj(const void *pkt_a, const void *pkt_b) 
{ 
    if (pkt_a.alfa > pkt_b.alfa && pkt_a.r_kw > pkt_b.r_kw) return 1; 
    if (pkt_a.alfa == pkt_b.alfa && pkt_a.r_kw == pkt_b.r_kw) return 0; 
    if (pkt_a.alfa < pkt_b.alfa && pkt_a.r_kw < pkt_b.r_kw) return -1; 
} 
+0

J'ai ajouté la balise qsort, car ce problème concerne la fonction qsort predicate. Je pense que d'autres ppl utilisant qsort auront des problèmes avec ça maintenant et puis aussi. –

+0

De même, la même fonction peut être utilisée avec bsearch(); en effet, c'est généralement une erreur si vous n'utilisez pas la même fonction de comparaison pour qsort() d'un tableau et bsearch() du même tableau - en supposant que vous utilisez les deux fonctions. –

Répondre

12

Quelque chose comme cela devrait fonctionner:

int porownaj(const void *p_a, const void *p_b) 
{ 
    /* Need to store arguments in appropriate type before using */ 
    const pkt *pkt_a = p_a; 
    const pkt *pkt_b = p_b; 

    /* Return 1 or -1 if alfa members are not equal */ 
    if (pkt_a->alfa > pkt_b->alfa) return 1; 
    if (pkt_a->alfa < pkt_b->alfa) return -1; 

    /* If alfa members are equal return 1 or -1 if r_kw members not equal */ 
    if (pkt_a->r_kw > pkt_b->r_kw) return 1; 
    if (pkt_a->r_kw < pkt_b->r_kw) return -1; 

    /* Return 0 if both members are equal in both structures */ 
    return 0; 
} 

Restez à l'écart de trucs stupides comme:

return pkt_a->r_kw - pkt_b->r_kw; 

qui renvoient des valeurs et non normalisées, sont source de confusion pour lire, ne fonctionnera pas correctement pour flotter numéros de point, et ont parfois des cas de coin difficiles qui ne fonctionnent pas correctement, même pour les valeurs entières.

+0

arg. Je savais que void * n'a pas besoin d'un cast en C quand il est assigné à quelque chose T *. mais je ne savais pas si cela s'appliquait aussi à const void *: D je mémoriserai que c'est aussi possible alors :) –

+0

J'allais commenter en faveur du "truc stupide" dont tu as parlé, mais ensuite je suis revenu à moi : b +1 – efotinis

+0

Ouais; J'ai fait plusieurs erreurs dans ma solution.J'ai complètement oublié que nous traitons des doubles ici, pas des entiers. Une soustraction d'entier est beaucoup plus rapide que trois if supplémentaires, c'est pourquoi j'ai utilisé cette méthode. – strager

1

Il y a deux parties au problème - comment écrire le code, et comment comparer les types de paquets. Vous devez vous assurer de toujours retourner une valeur. Votre code devrait toujours être telle que:

porownaj(&pkt_a, &pkt_b) == -porownaj(&pkt_b, &pkt_a) 

Votre comparaison de contours ne gère pas les cas tels que:

pkt_a->alfa > pkt_b->alfa && pkt_a->r_kw <= pkt_b->r_kw 
pkt_a->alfa < pkt_b->alfa && pkt_a->r_kw >= pkt_b->r_kw 
pkt_a->alfa == pkt_b->alfa && pkt_a->r_kw != pkt_b->r_kw 

Il y a un problème plus - est-il approprié pour comparer les valeurs à virgule flottante pour l'égalité exacte ? Cela dépendra de votre application.

Mécaniquement, vous devez convertir les pointeurs const void en pointeurs de structure const. J'utilise la distribution explicite - C++ l'exige, et j'essaye de rendre mon code acceptable à un compilateur C++ même quand c'est vraiment du code C.

int porownaj(const void *vp1, const void *vp2) 
{ 
    const pkt *pkt_a = (const pkt *)vp1; 
    const pkt *pkt_b = (const pkt *)vp2; 

    if (pkt_a->alfa > pkt_b->alfa && pkt_a->r_kw > pkt_b->r_kw) return 1; 
    if (pkt_a->alfa == pkt_b->alfa && pkt_a->r_kw == pkt_b->r_kw) return 0; 
    if (pkt_a->alfa < pkt_b->alfa && pkt_a->r_kw < pkt_b->r_kw) return -1; 
    return 0; 
} 

Ceci ne concerne pas les bits que je ne peux pas résoudre puisque je ne suis pas partie aux informations nécessaires. Notez qu'en général, les objets multidimensionnels (tels que les nombres complexes, ou les coordonnées (x, y) ou (x, y, z)) ne peuvent pas être simplement comparés pour plus ou moins que ou égal à.

0

Oui, je trierai par alfa et r_kw décidera si pkt est premier (la première valeur aura la plus grande (ou la plus petite) alfa et r_kw je pense). C'est comme ça que je comprends le problème, je ne suis pas sûr à 100%.

Questions connexes