7
votes

L'apprentissage C, apprécierait l'entrée sur la raison pour laquelle cette solution fonctionne

Ceci est littéralement la première chose que j'ai jamais écrite en C, alors n'hésitez pas à souligner tous les défauts. :) Mon problème, cependant, c'est ceci: Si j'écris le programme comme je me sens propre, je reçois un programme cassé: xxx

tel qu'il se trouve, ce qui précède ne génère aucune erreur de compiler, Mais me donne un segfault quand couru. Je peux résoudre ce problème en modifiant le programme pour réussir CMD en référence lorsque vous appelez Scanf et STRCMP. Le Segfault s'en va et est remplacé par des avertissements pour chaque utilisation de STRMP au moment de la compilation. Malgré les avertissements, le code concerné fonctionne.

AVERTISSEMENT: Passez l'ARG 1 de 'STRCMP' de type pointeur incompatible

En tant que bonus ajouté, modifier les appels SCANF et STRCMP permet au programme de progresser suffisamment assez pour exécuter le retour (0), à quel point la chose qui se bloque avec un piège à avorter. Si je shappe le retour (0) pour la sortie (0), tout fonctionne comme prévu.

Cela me laisse avec deux questions: pourquoi le programme d'origine s'est-il faux? Comment puis-je le réparer mieux que moi?

Le bit sur la nécessité d'utiliser la sortie au lieu de retour m'a particulièrement déroulé.


0 commentaires

9 Réponses :


7
votes

Vous n'allez pas allouer la mémoire pour cmd, il est donc null code>.

Essayez de le déclarer avec un peu d'espace: P>

char cmd[1000];


0 commentaires

11
votes

Cela se produit à cause de l'instruction Scanf.

Regardez comment cmd pointe vers NULL. Lorsque SCANF est exécuté, il écrit à l'adresse de cmd, qui est null, générant ainsi un segfault. P>

La solution consiste à créer un tampon pour cmd, tel que: P>

credit(&acct, &amt);


4 commentaires

Aimer la dernière phrase de cette réponse;)


Il convient de noter que les questions suivantes "Alors, comment puis-je y gérer cela sans une limite arbitraire sur la durée de l'entrée?" et que cette question a été répondu à plusieurs reprises à plusieurs reprises. La réponse courte est utilisée fgets ou getline , et une note sur qui ces choses fonctionnent peuvent être trouvées Stackoverflow.com/questions/2532425/... < / a> parmi beaucoup.


Ah, ça a du sens. Et DMCkee a préempté ma prochaine question. :) Pour ce qu'il vaut la peine, les fonctions de crédit, de crédit, de débit et de service sont en réalité utilisés pour manipuler une structure de compte (une partie du code coupé) à l'aide des valeurs transmises. Les valeurs transmises ne sont pas modifiées. Je suppose que ce serait une bonne idée de déclarer les paramètres const?


Dans ce cas, alors oui. Const probablement serait un bon filet de sécurité à utiliser.



4
votes

ceci: xxx


devrait être: xxx




Veuillez noter: Vous devez vous assurer que la chaîne de l'utilisateur entrera dans cmd a une longueur inférieure à 100 ou n


2 commentaires

Le second est C ++ dans ce cas, il devrait simplement utiliser STD :: String


Je pense que les commentaires C ++ étaient un peu déroutant mais +1 depuis que vous les avez corrigés. :)



2
votes

Dans votre exemple, scanf () code> est adopté un pointeur NULL.

char   cmd[80];
...
scanf ("%s",cmd);


0 commentaires


5
votes

Comme d'autres ont souligné, vous n'avez rien attribué à Scanf à lire. Mais vous devez également tester la valeur de retour de Scanf: xxx

La fonction Scanf renvoie le nombre de conversions succinces, de sorte que si quelqu'un est de type XXXX lorsque vous vous attendez à détecter un entier. et traiter avec ça. Mais franchement, le code d'interface utilisateur qui utilise scanf () ne sera jamais vraiment la preuve contre ce genre de chose. Scanf () était en fait destiné à lire des fichiers formatés, pas d'entrée aléatoire de l'homme.


1 commentaires

C'est ce que les valeurs de retour sont pour!



2
votes

CMD est initialisé à un pointeur NULL qui ne pointe jamais dans une mémoire. scanf code> ne vérifie pas que la cmd est valide avant d'essayer d'écrire sur ce que cmd pointe vers.

Une solution préliminaire crée plutôt un espace pour cmd à pointer sur: P>

char cmd[30]; /* DANGEROUS! */


0 commentaires

1
votes

Votre problème de base est que vous n'avez pas attribué la mémoire pour votre chaîne. En C, vous êtes responsable de la gestion de la mémoire. Si vous déclarez des variables sur la pile, c'est facile. Avec des pointeurs, c'est un peu plus difficile. Étant donné que vous avez la ligne char * str = null code>, lorsque vous essayez de scanf code> dans celui-ci, vous écrivez des octets sur null code>, qui est illégal. Ce que le spécificateur % S code> est écrit dans ce que str code> pointe à; Il ne peut pas changer str code>, car les paramètres sont passés par la valeur. C'est pourquoi vous devez passer & acct code> au lieu de ACCT code>.

Alors, comment puis-tu le réparer? Vous devez fournir une mémoire où la chaîne de lecture peut vivre. Quelque chose comme char Str [5] = "" code>. Cela fait STR code> un réseau de caractères de cinq éléments, suffisamment grand pour tenir "EXIT" et son octet zéro terminant. (Les matières se décomposent dans les pointeurs à la moindre provocation, nous allons donc bien sur ce front.) Cependant, c'est dangereux. Si l'utilisateur entre dans la chaîne malveillante code>, vous allez écrire "malic" code> dans str code> et les octets pour " 0 " code> dans tout ce qui vient après cela à la mémoire. Ceci est un débordement tampon et est un bug classique. Le moyen le plus simple de le corriger ici est d'obliger l'utilisateur à entrer une commande d'au plus N lettres, où n est la commande la plus longue que vous avez; Dans ce cas, n = 4. Ensuite, vous pouvez indiquer scanf code> à lire au plus quatre caractères: scanf ("% 4s% u% i", CMD, & Acct, & amt) code>. Le % 4s code> dit "Lisez-le au plus em> quatre caractères", vous ne pouvez donc pas bousiller une autre mémoire. Toutefois, notez que si l'utilisateur entre malformé 3 4 code>, vous ne pourrez pas trouver les 3 et les 4, car vous recherchez ormed code>.

La raison pour laquelle vous pourriez faire Scanf ("% S% U% I% I", & cmd, & Acct, & montant) code> Est-ce que c n'est pas en sécurité. Lorsque vous l'avez donnée & cmd code>, vous avez donné un char * ** code>; Cependant, il était heureux de le traiter comme un char * code>. Ainsi, il a écrit des octets sur em> cmd code>, donc si vous avez passé dans la chaîne EXIT code>, cmd code> pourrait (si elle étaient quatre octets larges et avaient l'endansité appropriée) être égale à 0x65786974 code> (0x65 = E code>, 0x78 = x code>, 0x69 = i code>, 0x74 = t code>). Et puis l'octet zéro, ou tout autre octet que vous avez passé, vous commencerez à écrire sur une mémoire aléatoire. Si vous le modifiez à STRCMP code> aussi, il sera aussi em> traiter la valeur em> de str code> comme une chaîne, et tout sera cohérent. Quant à pourquoi retour 0; code> échoue mais quitte (0) code> fonctionne, je ne suis pas sûr, mais je suppose que vous avez peut-être écrit sur l'adresse de retour de principal code>. Cela est aussi stocké sur la pile et s'il arrive à venir après cmd code> dans la mise en page de la pile, vous pourriez alors la mettre à zéro ou la griffonnage dessus. Maintenant, EXIT CODE> DOIT effectuer son nettoyage manuellement, sauter aux bons endroits, etc. Cependant, si (comme je pense que c'est le cas, bien que je ne suis pas sûr) principal code> Se comporte comme toute autre fonction, son em> retour code> saute dans l'espace sur la pile stockée comme adresse de retour (qui est probablement une routine de nettoyage de certaines sortes). Cependant, comme vous avez griffonné sur cela, vous obtenez une avortement. P>

Maintenant, il y a deux autres petites améliorations que vous pourriez faire. Premièrement, puisque vous traitez fait code> en tant que booléen, vous devez faire une boucle tandis (! DONE) {...} code>. Deuxièmement, la configuration actuelle vous oblige à écrire Quitter 1 1 code> pour quitter le programme, même si le bit 1 1 code> ne doit pas être nécessaire. Troisièmement, vous devriez vérifier si vous avez lu avec succès les trois arguments, de sorte que vous ne recevez pas d'erreurs / incohérences; Par exemple, si vous ne corrigez pas cela, alors l'entrée P>

while (1) {
  if (feof(stdin)) break;

  if (scanf("%4s", cmd, &acct) != 1) {
    fprintf(stderr, "Could not read the command!\n");
    scanf(" %*s "); /* Get rid of the rest of the line */
    continue;
  }

  if (strcmp(cmd, "exit") == 0) break;

  if (scanf(" %u %i", &acct, &amount) != 2) {
    fprintf(stderr, "Could not read the arguments!\n");
    scanf(" %*s "); /* Get rid of the rest of the line */
    continue;
  }

  if ((strcmp(cmd, "dep") == 0) || (strcmp(cmd, "deb") == 0))
    debit(acct, amount);
  else if ((strcmp(cmd, "wd") == 0) || (strcmp(cmd, "cred") == 0))
    credit(acct, amount);
  else if (strcmp(cmd, "fee") == 0)
    service_fee(acct, amount);
  else
    fprintf(stderr, "Invalid input!\n");
}


1 commentaires

Par curiosité, pourquoi le bowvote? Y a-t-il quelque chose qui ne va pas avec la réponse ou quelque chose que je devrais améliorer?



0
votes

Afin de bien comprendre ce qui se passe ici, vous devez comprendre quelques bases sur les indicateurs de C. Je vous suggère de jeter un oeil ici si vous êtes vraiment aussi nouveau pour C:

http://www.cprogramming.com/taturial.html#ctorial

La cause la plus courante des SEGFAULTS est détaillée ici:

http://www.cprogramming.com/debugging/segfault.html


0 commentaires