| ||||||||
Petit astucien | Bonsoir, j'essaye de m’entraîner à coder pour un contrôle pour demain et je suis bloqué. J'aurai besoin d'un petit coup de main Le programme que je veux faire est assez simple en soi. L'utilisateur doit écrire une chaîne de caractères avec des parenthèses et le programme doit dire si oui ou non il y a le même nombre de parenthèses fermantes et ouvrantes. Jusqu'ici c'est assez simple, je sais comment faire. Mais j'aime bien compliquer les choses et je veux en écrire le moins possible dans le main. Au lieu de faire une fonction booléenne et écrire le reste dans le main, j'avais envie d'écrire 3 fonctions. Une pour la saisie, une pour le booléen et une pour recommencer. Mon souhait est de pouvoir recommencer la partie saisie et la vérification booléeenne tant que l'utilisateur n'aura pas saisi une chaîne de caractères avec le même nombre de parenthèses ouvrantes et fermantes. Mais je galère car la saisie ne se refait pas et il fait tout tout seul sans mon autorisation. Il doit falloir utiliser les fameux pointeurs mais je ne sais jamais trop comment les utiliser (pour moi c'est encore un peu abstrait). Une petite aide serait la bien venue pour ma fonction recommencer.
#include<iostream> #include<string.h> using namespace std;
void saisir(char ch[51]) { cout<<"Saisir 50 caracteres max avec parentheses : "<<endl; cin.get(ch,51); }
bool verifpar(char ch[51]) { int longueur=strlen(ch); int ouverte=0; int fermee=0; for(int i=0;i<longueur;i++) { if(ch[i]=='(') ouverte++; if(ch[i]==')') fermee++; } if(ouverte==fermee) { cout<<"Le nombre de parentheses correspond."<<endl; return true; } else { cout<<"Il n'y a pas le meme nombre de parentheses."<<endl; return false; } }
void recommencer(char ch[51]) { while(verifpar(ch)) { saisir(ch); verifpar(ch); } }
int main(void) { char chaine[51]; recommencer(chaine); return 0; }
Modifié par Sebas44 le 20/10/2013 20:31 | |||||||
Publicité | ||||||||
| ||||||||
Petit astucien | Bonjour,
Ma réponse vient peut être un peu tard pour le contrôle.
Au niveau des pointeurs: Un pointeur est simplement l'adresse en mémoire de la variable, de la fonction ou du pointeur sur lequel pointe le pointeur. Je branche le décodeur
Pour un processeur, tout est nombre. Dans le cas d'un pointeur sur variable, le pointeur pointe vers une variable: un char, un int, ..., l'adresse est celle de la variable. Un tableau est un pointeur. Dans ton main, tu crée la variable "chaine", un tableau de type char pouvant contenir 51 éléments. Puis, tu appelles la fonction "recommencer" en lui passant comme paramètre un pointeur vers le premier élément de ce tableau. Tant mieux, çà évite de passer les 51 éléments du tableau à chaque fois que tu appelles la fonction "recommencer", çà permet à la fonction "recommencer" d'écrire dans le tableau d'une manière simple: elle connait l'adresse de la variable, le compilateur sait que c'est un tableau, de quel type il est et sa taille, ta fonction "recommencer" peut donc accéder à tous les éléments du tableau, en écriture.
Dans le cas d'un pointeur sur fonction, le pointeur pointe vers une fonction, l'adresse est donc celle d'une fonction (par ex, dans ton code, tu peux avoir un pointeur sur ta fonction saisir). C'est utile pour lier dynamiquement une dll (bibliothèque), par exemple: si tu voulais accéder à une fonction d'une dll (dont tu as une documentation pour connaître le nom la fonction et quels paramètres elle prend), tu charges la dll en mémoire (grâce à une fonction de Windows), tu lis l'adresse en mémoire de la fonction (encore grâce à une fonction de Windows), tu la place dans un pointeur sur fonction correctement typé, et tu peux appeler la fonction. Si la dll est introuvable, ou qu'elle ne contient pas la fonction dont tu as besoin, tu peux afficher une erreur et interrompre. A contrario, tu peux lier statiquement ton prog à la dll, dans ce cas, le linker fit le nécessaire, à ceci près que tu ne peux pas afficher de message d'erreur (çà plante au lancement). Les logiciels pour Windows qui ont des plugins utilisent cette liaison dynamique de dll.
Dans le cas d'un pointeur sur pointeur, le pointeur pointe vers un pointeur. On peut s'en servir pour "compartimenter" l'accès à la mémoire entre des threads différents par ex, ou pour des tableaux, ... L'un des avantages d'un pointeur sur pointeur est que sa taille est connue (fixée par le processeur).
Pour faire simple, le & est un opérateur qui retourne une adresse, et le * retourne l'élément situé à une adresse.
Par ex: #include <iostream> using namespace std; #define TAILLE_CHAINE 10 int main(void) { int i = 0; for ( i; i < TAILLE_CHAINE; i++) // Au début du for, i est déjà initialisé à 0, inutile de le réinitialiser à 0
for ( i = 0; i < TAILLE_CHAINE; i++) _itoa(i, tmp, 10); // On converti i (int) vers une chaîne de caractères qu'on stocke en tmp, la conversion est faite en décimal (base 10) _itoa((int) &chaine[i], tmp, 16); // Ici, _itoa doit convertir un char (1 octet, c'est de l'ASCII) en un int (1 ou 2 octets suivant le compilo), on fait un cast explicite _itoa(i, tmp, 10); _itoa(chaine[i], tmp, 10); _itoa((int) ptr, tmp, 16); _itoa(*ptr, tmp, 10); _itoa(sizeof(char), tmp, 10); ptr += sizeof(char); // On ajoute au pointeur ptr la taille d'un char pour pointer vers le caractère suivant du tableau system("pause"); return 0;
Je bosse avec Visual Studio. Si _itoa ne passe pas, essaye itoa.
Il y a cet article sur développez.com au sujet des pointeurs.
| |||||||
Petit astucien | J'oubliais ta question principale...
A mon avis, après ton cin.get(ch, 51), il te manque un truc, nettoyer le Stream d'entrée: cin.clear();
Puis, éventuellement ignorer au maximum INT_MAX caractères du buffer jusqu'à une nouvelle ligne: cin.ignore(INT_MAX,'\n');
Ici, il y a un soucis: Tantque(verifpar(ch) retourne true) { saisir(ch); verifpar(ch); }
OK, appeler 2 fois la même fonction, sans tenir compte de son retour 1 fois, pourquoi pas
Voici une méthode: créer une variable locale dans la portée de la fonction recommencer, de type bool, et l'initialiser à false. Puis, utiliser un prédicat pour ton while qui s'en serve, ainsi il ne te reste plus qu'un seul appel à ta fonction verifpar(). Place le retour de l'appel à verifpar dans le bool que tu as créée, ainsi la boucle s'interompera lorsque verifpar retournera true.
Ce qui donne: void recommencer(char ch[51]) { bool memenbreparentheses = false; while(!memenbreparentheses ) // le "!" est l'inversion logique (= while(memenbreparentheses == false)) { saisir(ch); memenbreparentheses = verifpar(ch); } }
Dans le cas présent, cette petite modification ne change rien au fonctionnement de ton code, (entre autres, verifpar ne met pas à jour de variable globale, donc pas d'effet de bord). En revanche, la fonction appelée 2 fois effectue un traitement, dans des programmes plus complexes, rien ne te garanti que tu n'aura pas un retour d'une valeur dans le predicat du while qui t'empêcherait d'enregistrer la valeur de retour lors du 2e appel. Par ex, dans ton code, si dans le bloc du while tu avais besoin de sauver la valeur de retour dans une variable globale, tu aurais pu avoir un bug: x boucles pourraient s'exécuter, en sauvant à chaque fois le retour du 2e appel. Si le 1er appel retournait false, le while s'interromperait sans exécuter le 2e appel. La valeur du retour du 2e appel lors de la boucle serait encore sauvée, mais elle n'aurait pas été mise à jour.
Si tu veux tester la création de fonctions, faire un tas de fonctions dans un programme créé dans ce but n'est pas gênant. En revanche, hors exercice, il faut créer les fonctions nécessaires, pas plus, pas moins. Voici pourquoi: - il faut bien que les paramètres soient passés à la fonction que tu crées. Or, ces paramètres sont souvent passés à la fonction grâce à la pile: de manière simplifiée, les paramètres sont empilés dans un certain ordre par le code qui prépare l'appel à la fonction, la fonction est appelée, la fonction dépile ses paramètres dans le bon ordre, la fonction fait son traitement, la fonction place (souvent) la valeur de retour dans l'accumulateur (registre eax en 32 bits ou rax en 64 bits sur processeurs Intel et compatibles). Ensuite, çà dépend de la convention d'appel: soit le registre de pile est ajusté et l'exécution continue juste après l'appel de fonction, soit l'exécution continue juste après l'appel de fonction puis le registre de pile est ajusté. Or, çà prends du temps, la pile étant un RAM. - le code est un peu plus fastidieux à comprendre, s'il est en plus mal organisé et/ou mal documenté, on peut vite y perdre son latin.
J'aime la programmation. J'ai l'impression que tu apprécies aussi, tant mieux ! Modifié par rdany62 le 05/11/2013 16:49 | |||||||
|
Les bons plans du moment PC Astuces | Tous les Bons Plans | ||||||||||||||||||
|