Quels sont les parameters de cet appel de fonction C qsort?

qsort(bt->rw[t], bt->num[t], sizeof(TRELLIS_ATOM *), (int (*)(const void *,const void *))compare_wid); 

bt->rw[t] est un pointeur sur un pointeur struct, bt->[num] est un int , je ne comprends pas ce que ce quasortingème paramètre est, sauf que compare_wid est une fonction définie quelque part comme suit:

 static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b ) { ... return x; } 

(int (*)(const void *,const void *)) signifie “traite ce qui suit comme un pointeur sur une fonction prenant deux parameters de type const void* et renvoyant un int “. compare_wid est en effet une fonction qui peut être traitée de cette façon.

qsort appellera cette fonction pour effectuer des comparaisons entre les éléments lors du sorting: si le nombre entier renvoyé est égal à zéro, les éléments sont supposés égaux, sinon le signe du nombre entier est utilisé pour les classer.

J’arriverai dans un instant à la signification de la ligne, mais avant cela, voyons quelques notions de base sur la raison pour laquelle qsort() besoin de son paramètre final du type dont il a besoin. qsort() est une fonction qui peut sortinger tout type de données.

Vous lui fournissez:

  • un pointeur de base, qui pointe au début d’un bloc de données contigu,
  • le nombre d’éléments dans le bloc,
  • la taille d’un membre de données, et
  • une fonction qui compare deux valeurs de données.

Comme un algorithme de sorting en général ne dépend pas du type de données en cours de sorting, qsort() peut être écrit sans connaître les types de données qu’il sortinge. Mais pour pouvoir le faire, qsort() prend les parameters void * , ce qui signifie “pointeur générique” en C.

Disons que vous avez un tableau de valeurs int non sortingées:

 #define N 1024 int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */ 

Ensuite, vous pouvez les sortinger en appelant qsort() :

 qsort(data, N, sizeof data[0], compare_int); 

data sont de type int * lorsqu’elles sont passées à qsort() , et le premier paramètre de qsort() est de type void * . Comme tout pointeur d’object peut être converti en void * en C, c’est OK. Les deux arguments suivants sont bien aussi. Le dernier argument, compare_int , doit être une fonction qui prend deux parameters const void * et retourne un int . La fonction sera appelée par qsort() avec des paires de pointeurs de &data[0] à &data[N-1] autant de fois que nécessaire.

Pour déclarer une fonction f() qui “prend deux parameters const void * et renvoie int “:

 int f(const void *, const void *); 

Si l’on veut déclarer un pointeur de fonction que nous pouvons définir sur f , le pointeur est déclaré comme suit:

 int (*pf)(const void *, const void *); pf = f; 

Les parenthèses sont obligatoires, car sinon pf serait une fonction renvoyant un int * . Maintenant, pf est un pointeur sur une fonction renvoyant int .

Pour en revenir à notre algorithme de sorting int , et à partir de ce qui précède, nous pourrions définir compare_int() comme:

 int compare_int(const void *a, const void *b) { const int *the_a = a; const int *the_b = b; if (*the_a > *the_b) return 1; else if (*the_a < *the_b) return -1; else return 0; } 

Lors de l'écriture de compare_int() , nous soaps que les pointeurs transmis sont int * déguisés en void * , nous les reconvertissons donc en int * , ce qui est OK, puis nous comparons les nombres.

Nous nous tournons maintenant vers le code en question:

 static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b ) 

signifie que compare_wid est une fonction qui prend deux parameters TRELLIS_ATOM * et retourne un int . Comme nous venons de le voir, le dernier argument de qsort() devrait être une fonction de type:

 int (*)(const void *, const void *) 

C'est-à-dire une fonction prenant deux parameters const void * et renvoyant int . Comme les types ne correspondent pas, le programmeur compare_wid() selon le type requirejs par qsort() .

Cependant, cela a des problèmes. Une fonction de type:

 int (*)(TRELLIS_ATOM *, TRELLIS_ATOM *) 

n'est pas équivalent à une fonction de type:

 int (*)(const void *, const void *) 

il n'est donc pas garanti que le casting fonctionne. Il est beaucoup plus facile, correct et standard de déclarer compare_wid() tant que:

 static int compare_wid(const void *a, const void *b); 

Et la définition de compare_wid() devrait ressembler à ceci:

 static int compare_wid(const void *a, const void *b) { const TRELLIS_ATOM *the_a = a; const TRELLIS_ATOM *the_b = b; ... /* Now do what you have to do to compare the_a and the_b */ return x; } 

Si vous faites cela, vous n'aurez pas besoin de la conversion dans l'appel à qsort() , et votre programme sera non seulement plus facile à lire, mais également à corriger.

Si vous ne pouvez pas modifier compare_wid() , écrivez une autre fonction:

 static int compare_stub(const void *a, const void *b) { return compare_wid(a, b); } 

et appelez qsort() avec compare_stub() (sans la conversion) au lieu de compare_wid() .

Edit : Basé sur beaucoup de mauvaises réponses, voici un programme de test:

 $ cat qs.c #include  #include  struct one_int { int num; }; #ifdef WRONG static int compare(const struct one_int *a, const struct one_int *b) { #else static int compare(const void *a_, const void *b_) { const struct one_int *a = a_; const struct one_int *b = b_; #endif if (a->num > b->num) return 1; else if (a->num < b->num) return -1; else return 0; } int main(void) { struct one_int data[] = { { 42 }, { 1 }, { 100 } }; size_t n = sizeof data / sizeof data[0]; qsort(data, n, sizeof data[0], compare); return 0; } 

La compilation avec compare() définie comme prenant deux valeurs const struct one_int * :

 $ gcc -DWRONG -ansi -pedantic -W -Wall qs.c qs.c: In function `main': qs.c:32: warning: passing argument 4 of `qsort' from incompatible pointer type 

Comstackr avec la définition correcte:

 $ gcc -ansi -pedantic -W -Wall qs.c $ 

Edit 2 : Il semble y avoir une certaine confusion quant à la légalité de l’utilisation de compare_wid tel quel pour l’argument final de qsort() . La FAQ de comp.lang.c, question 13.9 a une bonne explication (c'est moi qui souligne):

Pour comprendre pourquoi les conversions de pointeur curieux dans une fonction de comparaison qsort sont nécessaires ( et pourquoi une conversion du pointeur de la fonction lors de l'appel de qsort ne peut pas aider ), il est utile de réfléchir au fonctionnement de qsort . qsort ne sait rien du type ou de la représentation des données en cours de sorting: il se contente de mélanger de petits morceaux de mémoire. (Tout ce qu'il sait sur les morceaux est leur taille, que vous spécifiez dans le troisième argument de qsort .) Pour déterminer si deux morceaux doivent être échangés, qsort appelle votre fonction de comparaison. (Pour les échanger, il utilise l'équivalent de memcpy .)

Puisque qsort traite de manière générique des morceaux de mémoire de type inconnu, il utilise des pointeurs génériques (void *) pour s'y référer. Lorsque qsort appelle votre fonction de comparaison, il transmet comme arguments deux pointeurs génériques aux fragments à comparer. Comme elle passe les pointeurs génériques, votre fonction de comparaison doit accepter les pointeurs génériques et reconvertir les pointeurs dans leur type approprié avant de les manipuler (c'est-à-dire avant d'effectuer la comparaison). Un pointeur void n'est pas du même type qu'un pointeur de structure et, sur certaines machines, il peut avoir une taille ou une représentation différente (c'est pourquoi ces transtypages sont requirejs pour être corrects).

Comme mentionné dans la FAQ, voyez aussi ceci .

qsort

 void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) ); 

comparateur : fonction qui compare deux éléments. La fonction suivra ce prototype:

comparateur int (const void * elem1, const void * elem2);

La fonction doit accepter deux parameters pointant sur des éléments, typés comme void *. Ces parameters doivent être reconvertis en certains types de données et comparés.

La valeur de retour de cette fonction doit indiquer si elem1 est considéré comme inférieur, égal ou supérieur à elem2 en renvoyant respectivement une valeur négative, zéro ou une valeur positive.

Le quasortingème paramètre contient une conversion explicite en un pointeur de fonction:

 int (*)(const void *,const void *) 
  • la partie int correspond au type de retour
  • la partie (*) est la notation d’un pointeur de fonction
  • la partie (const void *,const void *) sont les types de parameters

C’est une fonction de prédicat appelée par l’implémentation qsort pour comparer deux TRELLIS_ATOM* .

C’est une fonction de sorting personnalisée pour les éléments de type TRELLIS_ATOM (peu importe ce que cela pourrait être).

compare_wid devrait renvoyer quelque chose <0 si *a < *b ,> 0 si *a > *b et 0 si *a et *b sont logiquement considérés égaux.

Il s’agit d’une fonction de sorting rapide qui nécessite un pointeur de fonction sur votre routine de gestionnaire de sorting personnalisé. D’où la transtypage du paramètre pour le pointeur de fonction dans le quasortingème paramètre.

Lorsque la fonction de sorting rapide est appelée, elle exécute le pointeur de fonction que vous avez fourni pour gérer l’algorithme de sorting, c’est-à-dire TRELLIS_ATOM *a et TRELLIS_ATOM *b . ou -1 dans la comparaison logique, par exemple:

 si (* a> * b) retourne 1;
 si (* a == * b) renvoie 0;
 si (* a <* b) renvoie -1;

J'espère que cela vous aidera, Cordialement, Tom.

 /* Integer comparison function for use with the stdlib's Qsort. */ inline int int_compare(const void *p1, const void *p2) { return ( *(int*)p1 - *(int*)p2 ); }