Question

Il y a les déclarations suivantes:

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

Alors, quelque part dans le programme il y a l'appel suivant:

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(Ignorer les trois premiers arguments et numeric).

Je demande ce qui est ceci:

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

Je comprends que qsort attend un « pointeur vers une fonction qui obtient deux pointeurs void et retourne un int » comme il est 4ème argument, mais comment ce qui est écrit ci-dessus satisfait que? Il me semble comme une sorte de casting parce qu'elle est faite de deux parenthèses, mais ce serait un casting très bizarre. Parce qu'il faut une fonction et rend cette fonction un « pointeur vers une fonction qui obtient deux pointeurs void et retourne un int ». Ce qui n'a pas de sens.
(Je suivais ici la règle selon laquelle un type type entre parenthèses avant une variable favorise la variable à ce type).

Je crois que je viens me trompe, peut-être que quelqu'un peut me dire comment lire ceci, quel est l'ordre?

Était-ce utile?

La solution

Qu'est-ce qui se passe ici est en effet un casting. Permet d'ignorer le ternaire pour une seconde et prétendre que numcmp est toujours utilisé. Aux fins de cette question, les fonctions peuvent agir comme des pointeurs de fonction en C. Donc, si vous regardez le type de numérique, il est en fait

(int (*)(int*,int*))

Pour que cela soit correctement utilisé dans qsort il doit avoir des paramètres vides. Parce que les types ici ont tous la même taille par rapport aux paramètres et aux types de retour, il est possible de substituer à l'autre. Tout ce qui est nécessaire est un casting pour rendre le compilateur heureux.

(int (*)(void*,void*))(numcmp )

Autres conseils

Vous avez manqué l'astuce ici - la partie

(numeric ? numcmp : strcmp)

utilise l'opérateur ternaire choisir qui fonction est appelée à l'intérieur de qsort. Si les données est numérique, il utilise numcmp. Dans le cas contraire, il utilise strcmp. Une mise en œuvre plus facile à lire ressemblerait à ceci:

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

Vous pouvez le faire sans la distribution de pointeur de fonction. Voici comment . Dans mon expérience, dans la plupart des endroits, si vous utilisez un casting, vous le faites mal.

Notez que la définition standard de qsort() comprend const:

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Notez que le comparateur de chaîne est donnée deux valeurs « char ** », pas « les valeurs de char * ».

J'écris mes comparateurs de sorte que des moulages ne sont pas nécessaires dans le code d'appel:

#include <stdlib.h>    /* qsort() */
#include <string.h>    /* strcmp() */

int num_cmp(const void *v1, const void *v2)
{
    int i1 = *(const int *)v1;
    int i2 = *(const int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

int str_cmp(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return(strcmp(s1, s2));
}

Obliger les gens à écrire des moulages dans le code à l'aide de vos fonctions est laid. Ne pas.

Les deux fonctions que j'ai écrit correspondent au prototype de la fonction requise par le qsort() standard. Le nom d'une fonction lorsqu'ils ne sont pas suivie entre parenthèses est équivalent à un pointeur vers la fonction.

Vous trouverez dans l'ancien code, ou code écrit par ceux qui ont été élevés sur compilateurs plus anciens, que des pointeurs vers des fonctions sont utilisées en utilisant la notation:

result = (*pointer_to_function)(arg1, arg2, ...);

Dans un style moderne, ce qui est écrit:

result = pointer_to_function(arg1, arg2, ...);

Personnellement, je trouve le plus clair explicite déréférencement, mais pas tout le monde est d'accord.

Celui qui a écrit cet extrait de code a été d'essayer d'être trop intelligent . Dans son esprit, il pense sans doute qu'il est d'être un bon programmeur en faisant un habile « one-liner ». En réalité, il est un code qui rend moins lisible et est désagréable de travailler avec sur le long terme et devrait être réécrite sous une forme plus évidente semblable au code de Harper Shelby.

Rappelez-vous l'adage de Brian Kernighan:

  

Le débogage est deux fois plus dur que l'écriture   le code en premier lieu.   Par conséquent, si vous écrivez le code comme   intelligemment que possible, vous êtes, par   définition, pas assez intelligent pour debug   il.


Je fais beaucoup de performances de codage critique avec des échéances en temps réel dur ... et je n'ai pas encore vu un endroit où une seule ligne dense est appropriée.

J'ai même foiré autour avec la compilation et la vérification de la asm pour voir si celui-liner a une meilleure mise en œuvre compilé asm mais ai jamais trouvé la peine d'être un liner il.

Comme d'autres l'ont souligné, pour

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

alors ce qui suit est une distribution de type

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

et l'expression est

(numeric ? numcmp : strcmp)

déclarations de C peuvent être assez difficiles à lire, mais il est possible d'apprendre. La méthode consiste à commencer à la partie intérieure et puis allez à droite un pas, puis à gauche un pas, continuer à droite, à gauche, à droite, à gauche, etc vers l'extérieur jusqu'à la fin. Vous ne traversez pas à l'extérieur une parenthèse avant tout a été évalué à l'intérieur. Par exemple, pour le type jeté ci-dessus, (*) indique que cela est un pointeur. Pointer était la seule chose à l'intérieur de la parenthèse afin d'évaluer nous sur le côté droit à l'extérieur. (void*,void*) indique que est un pointeur vers une fonction à deux arguments de pointeur. Enfin int indique le type de retour de la fonction. La parenthèse extérieure fait un casting de type. Mise à jour: deux articles plus détaillés: La Clockwise / Spirale Règle et lecture noreferrer Déclarations C:. Un guide pour les Mystified

Cependant, les bonnes nouvelles sont que même si ce qui précède est extrêmement utile de savoir, il existe un moyen très simple de tricher : cdecl programme peut convertir C Description en anglais et vice versa:

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

Exercice: Quel type de variable est i

int *(*(*i)[])(int *)

Réponse rot13 au cas où vous ne l'avez pas cdecl installé sur votre machine (mais vous devrait vraiment):

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

Je lirais probablement comme ceci:

typedef int (*PFNCMP)(void *, void *);

PFNCMP comparison_function;

if (numeric)
{
    comparison_function =  numcmp;
}
else
{
    comparison_function = strcmp;
}

qsort((void**) lineptr, 0, nlines-1, comparison_function);

L'exemple de la question est un cas explicite.

Votre logique est correcte, je pense. Il est en effet coulée à « pointeur vers une fonction qui obtient deux pointeurs vides et retourne un entier » qui est le type requis par la signature de la méthode.

Les deux numcmp et strcmp sont des pointeurs vers des fonctions qui prennent deux char* en tant que paramètres et renvoie un int. La routine qsort attend un pointeur vers une fonction qui prend deux void* en tant que paramètres et retourne un int. D'où le casting. C'est sûr, puisque void* agit comme un pointeur générique. Maintenant, à la lecture de la déclaration: Prenons votre Déclaration de strcmp:

 int strcmp(char *, char *);

Le compilateur se lit comme strcmp est en fait:

 int (strcmp)(char *, char *)

une fonction (décomposition d'un pointeur vers une fonction dans la plupart des cas) qui prend deux arguments de char *. Le type du pointeur strcmp est donc:

 int (*)(char *, char *)

Par conséquent, lorsque vous avez besoin de lancer une autre fonction compatible pour vous strcmp utiliseriez ci-dessus comme type de caster.

De même, étant donné que l'argument comparateur qsort prend deux void *s et donc le casting bizarre!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top