Appel d’isalpha causant une erreur de segmentation

J’ai le programme suivant qui provoque une erreur de segmentation.

#include  #include  #include  int main(int argc, char *argv[]) { printf("TEST"); for (int k=0; k<(strlen(argv[1])); k++) { if (!isalpha(argv[1])) { printf("Enter only alphabets!"); return 1; } } return 0; } 

J’ai compris que c’est cette ligne qui pose problème

 if (!isalpha(argv[1])) { 

et remplacer argv[1] par argv[1][k] résout le problème.

Cependant, je trouve plutôt curieux que le programme aboutisse à une erreur de segmentation sans même imprimer TEST . Je m’attends également à ce que la fonction isalpha de manière incorrecte si l’octet inférieur du caractère char* pointeur sur argv[1] , mais cela ne semble pas être le cas. J’ai du code pour vérifier le nombre d’arguments mais ce n’est pas indiqué ici pour des raisons de brièveté.

Qu’est-ce qu’il se passe ici?

En général, il est plutôt inutile de discuter de la raison pour laquelle un comportement non défini conduit à ce résultat ou à un autre.

Mais peut-être que tenter de comprendre pourquoi quelque chose se produit même si cela n’est pas conforme aux spécifications ne fait pas de mal.

Il existe des implémentations d’ isalpha qui utilisent un tableau simple pour rechercher toutes les valeurs de caractères unsigned char possibles. Dans ce cas, la valeur transmise en tant que paramètre est utilisée comme index dans le tableau. Alors qu’un caractère réel est limité à 8 bits, un entier ne l’est pas. La fonction prend un int en paramètre. Cela permet également d’entrer EOF , ce qui ne correspond pas au caractère unsigned char .

Si vous transmettez une adresse comme 0x7239482342 à votre fonction, cela va bien au-delà de la fin dudit tableau et lorsque la CPU essaie de lire l’entrée avec cet index, elle tombe du bord du monde. 😉

L’appel d’ isalpha avec une telle adresse est l’endroit où le compilateur doit émettre un avertissement concernant la conversion d’un pointeur en entier. Ce que vous ignorez probablement …

La bibliothèque peut contenir du code qui vérifie les parameters valides, mais elle peut également simplement compter sur l’utilisateur pour ne pas transmettre des éléments à ne pas transmettre.

  1. printf n’a pas été vidé
  2. la conversion implicite de pointeur en entier qui aurait dû générer au moins des diagnostics au moment de la compilation pour la violation de contrainte a produit un nombre qui était hors de la plage pour isalpha . isalpha étant implémenté en tant que table de recherche, cela signifie que votre code a accédé à la table en dehors des limites, donc un comportement indéfini.
  3. La raison pour laquelle vous n’avez pas reçu de diagnostic peut faire partie d’une partie en raison de la manière dont isalpha est implémenté en tant que macro. Sur mon ordinateur avec Glibc 2.27-3ubuntu1, isalpha est défini comme

     # define isalpha(c) __isctype((c), _ISalpha) # define __isctype(c, type) \ ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type) 

    la macro contient une dissortingbution malheureuse à int , qui fera taire votre erreur!


Une des raisons pour lesquelles je publie cette réponse après tant d’autres est que vous n’avez pas corrigé le code , il souffre toujours d’un comportement indéfini étant donné que les caractères étendus et le caractère sont signés (ce qui est généralement le cas sur x86-32 et x86- 64).

L’argument correct à donner à isalpha est (unsigned char)argv[1][k] ! C11 7.4 :

Dans tous les cas, l’argument est un int dont la valeur doit pouvoir être représentée sous la forme d’un caractère unsigned char ou doit être égale à la valeur de la macro EOF . Si l’argument a une autre valeur, le comportement est indéfini.

Je trouve plutôt curieux que le programme aboutisse à une erreur de segmentation sans même imprimer TEST

printf n’imprime pas instantanément, mais écrit dans un tampon temporel. Terminez votre chaîne avec \n si vous souhaitez la vider de la sortie réelle.

et remplacer argv [1] par argv [1] [k] résout le problème.

isalpha est conçu pour fonctionner avec des caractères uniques.

Tout d’abord, un compilateur conforme doit vous donner un message de diagnostic ici. Il n’est pas autorisé de convertir implicitement un pointeur vers le paramètre int isalpha par isalpha . (C’est une violation des règles d’assignation simple, 6.5.16.1.)

Quant à savoir pourquoi “TEST” n’est pas imprimé, c’est peut-être tout simplement parce que stdout n’est pas vidé. Vous pouvez essayer d’append fflush(stdout); après printf et voir si cela résout le problème. Vous pouvez également append un saut de ligne \n à la fin de la chaîne.

Sinon, le compilateur est libre de réordonner l’exécution du code tant qu’il n’y a pas d’effets secondaires. C’est-à-dire qu’il est permis d’exécuter la boucle entière avant le printf("TEST"); , tant qu’il imprime TEST avant d’imprimer potentiellement "Enter only alphabets!" . De telles optimisations ne sont probablement pas susceptibles de se produire ici, mais dans d’autres situations, elles peuvent se produire.