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.
printf
n’a pas été vidé 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. 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èreunsigned char
ou doit être égale à la valeur de la macroEOF
. 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.