2009-07-02 5 views
4

est-ce juste moi ou ce code dans Programming Pearls est faux (quicksort veut 2 const const, non?) Si oui, ma solution est-elle correcte? Excuses, juste apprendre ...Erreur dans la fonction qsort dans Perles de programmation?

int wordncmp(char *p, char* q) 
{ int n = k; 
    for (; *p == *q; p++, q++) 
     if (*p == 0 && --n == 0) 
      return 0; 
    return *p - *q; 
} 

int sortcmp(char **p, char **q) 
{ return wordncmp(*p, *q); 
} 
... 

qsort(word, nword, sizeof(word[0]), sortcmp); 

Est-ce une solution?

int sortcmp(const void *p, const void *q) 
{ return wordncmp(* (char * const *) p, * (char * const *) q); 
} 

Répondre

7

Le premier exemple de code fonctionnera probablement avec pratiquement n'importe quel compilateur et processeur; cependant, il s'agit d'un comportement techniquement indéfini, si vous suivez la norme C à la lettre.

Comme vous l'avez dit, le dernier argument à qsort() est un pointeur sur une fonction prenant deux arguments de type const void*. sortcmp prend différents arguments. Votre compilateur devrait vous donner un avertissement sur les signatures de type incompatibles ou quelque chose. Dans tous les cas, une distribution est exécutée d'une fonction d'un type à une fonction d'un autre type.

La norme C spécifie que vous pouvez distribuer des pointeurs de fonction à d'autres pointeurs de fonction avec des types différents, mais vous ne pouvez pas déréférencer et appeler le pointeur de fonction casted. Toutefois, si vous redéfinissez le pointeur de fonction sur son type d'origine, l'appel de ce dernier a un comportement défini: il appelle la fonction d'origine.

Puisque vous d'une coulée int (*)(char**, char**) à un int (*)(const void*, const void*), puis éventuellement qsort() invoque votre fonction de comparaison sans jeter de nouveau à int (*)(char**, char**), c'est un comportement non défini.

Cependant, puisque virtuellement sur toutes les architectures, un char ** et un const void* sont représentés de la même manière, l'appel de la fonction fonctionnera pratiquement toujours.

Si vous souhaitez obtenir un comportement défini, vous devez vous assurer que la fonction de votre comparateur a la bonne signature de type, puis vous pouvez convertir les arguments au type approprié. Votre solution est exactement correcte et ne viole pas la norme C ici. Bien fait sur const -correction - beaucoup de gens ne comprennent pas exactement ce que signifie char * const *.

Vous devez également faire wordncmp() prendre les paramètres de const char*, puisque vous ne modifiez pas les paramètres. Remarque: Vous ne pouvez pas non plus transmettre un pointeur de fonction à un pointeur de données (par exemple, un void*) ou vice-versa. La norme permet aux pointeurs de fonction et aux pointeurs de données d'avoir des tailles différentes. Même si cela fonctionne sur votre ordinateur, il n'est pas garanti de toujours fonctionner.

+0

La norme ne garantit-elle pas que 'char *' et 'void *' ont la même représentation? Je suis d'accord qu'il est toujours UB de faire l'appel avec le mauvais type de pointeur de fonction, mais je ne crois pas que la représentation des arguments est le problème. Le problème est plutôt qu'une implémentation pathologique pourrait utiliser une convention d'appel différente pour passer 'void *' par rapport à 'char *', par ex. avoir un ensemble séparé de registres d'arguments pour chaque type. –

+0

@R.: Oui, la norme garantit que 'char *' et 'void *' ont les mêmes exigences de représentation et d'alignement (C99 §6.2.5/27), mais 'char **' et 'void *' ne sont pas nécessairement compatibles. –

2

Vous avez raison, la signature pour sortcmp ne correspond pas à ce que qsort attend. Votre correction est juste. wordcmp devrait également être faite const -correct car vous perdez techniquement certains des const -ness en cours de route.

int wordncmp(const char *p, const char* q) 
Questions connexes