J'ai le programme suivant qui provoque une erreur de segmentation.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
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 le remplacement de 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
vérifie de manière incorrecte si l'octet inférieur du pointeur char*
à 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 par souci de concision.
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 de isalpha
qui utilisent un tableau simple pour rechercher toutes les valeurs possibles de unsigned char
. 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 n'est pas . La fonction prend une variable int
en paramètre. Ceci permet de saisir EOF
également, ce qui ne rentre pas dans unsigned char
.
Si vous transmettez une adresse comme 0x7239482342 à votre fonction, cela va bien au-delà de la fin dudit tableau et lorsque le CPU essaie de lire l'entrée avec cet index, il tombe du bord du monde. ;)
L'appel de 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 pourrait contient un code qui recherche les paramètres valides, mais elle peut aussi simplement compter sur l’utilisateur pour ne pas transmettre de choses à ne pas transmettre.
printf
n'a pas été vidéisalpha
. isalpha
étant implémenté en tant que table de recherche, votre code a accédé à la table en dehors des limites, donc un comportement indéfini.Pourquoi vous n'avez pas obtenu des diagnostics peut faire partie d'une partie à cause du fait que commentisalpha
est implémenté sous forme de 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 distribution malheureuse sur int
, ce 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 a toujours un comportement indéfini étant donné les caractères étendus et char
étant signé (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 être représentable sous la forme d'ununsigned char
ou doit être égale à la valeur de la macroEOF
. Si l'argument a une autre valeur, le comportement n'est pas dé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 voulez la vider à la sortie réelle.
le remplacement de 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
attendu 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'ajouter fflush(stdout);
après printf et voir si cela résout le problème. Vous pouvez également ajouter 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 autorisé à exécuter la totalité de la boucle avant la printf("TEST");
, tant qu'elle 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.