Incertitudes d'un débutant.

Résolu/Fermé
Booniak Messages postés 11 Date d'inscription vendredi 1 août 2014 Statut Membre Dernière intervention 29 août 2014 - 1 août 2014 à 10:51
fil5m Messages postés 9 Date d'inscription dimanche 3 août 2014 Statut Membre Dernière intervention 12 août 2014 - 3 août 2014 à 22:37
J'ai commencé à apprendre la programmation avec c# et je commence à avoir du mal à comprendre certain éléments de logique, j'ai consulté différents sites mais je m'y perds notamment à cause des phrases à rallonge remplie de lexique technique.
J'ai essayé de réaliser un exercice dont le but est de créer une fonction qui fait la moyenne des valeurs contenues dans un tableau, les valeurs sont entrées une à une via une boite de saisie, jusqu'à la saisie de "-1".
L'application se lance mais à la première saisie quelle qu'elle soit j'ai l'erreur: L'exception IndexOutOfRangeException n'a pas été gérée l'index se trouve en dehors des limites du tableau. Ligne 23.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
 /* Je ne comprend pas la signification de void, 
une valeur de retour pour une méthode qui ne retourne pas de valeur, c'est à dire?
Je ne comprend pas non plus ce qui est entre paranthèse après Main.*/
        static void Main(string[] args)
        {
            int compteur = 0, nb_valeurs = 0;
            int[] tableau = new int[nb_valeurs];

/*J'essaye de faire répéter "Ecrivez une valeur..." jusqu'à la validation de -1,
 chaque valeur saisie est enregistré dans une case du tableau.
Je pense que -1 est également enregistré dans le tableau, comment l'éviter?*/
            {
               Console.Write("Ecrivez une valeur, -1 pour terminer: ");
               tableau[compteur] = Convert.ToInt16(Console.Read());
                if (tableau[compteur] != -1)
                {
                    compteur++;
                    nb_valeurs++;
                }
            }
            while (tableau[compteur] != -1);
                    
/*D'après moi le nombre de valeurs n'augmente pas à l'inscription de -1,
 si au moins une valeur est inscrite avant -1 alors,
 la fonction moyenne_tableau est exécutée.*/
                if(nb_valeurs<1)
                {
                    Console.Write("Vous n'avez inscrit aucune valeurs: ");
                }
                else
Console.Write("La moyenne du tableau est de {0}.",moyenne_tableau(tableau,nb_valeurs));
            }
 /*Création d'une fonction pour calculer la moyenne des valeurs du tableau.
L'idée est de partir de la case 0 (qui contient la première valeur si j'ai compris)
et d'ajouter sa valeur à la somme actuelle.
Quand la case du tableau inférieure à la valeur de nb_valeur est atteinte
 la somme est divisé par le nb_valeur...voila.*/
        static int moyenne_tableau(int[] tableau,int nb_valeurs)
        {
            int somme = 0;
            int compteur;
/*En mettant compteur<nb_valeurs j'évite de prendre la dernière case du tableau(donc-1)
 dans la somme, enfin je crois...*/
                for(compteur=0;compteur<nb_valeurs;compteur++)
                    somme+=tableau[compteur];
            return somme/nb_valeurs;

        }
    }
}

4 réponses

fil5m Messages postés 9 Date d'inscription dimanche 3 août 2014 Statut Membre Dernière intervention 12 août 2014 1
3 août 2014 à 20:50
Jusque là, c'est pas si mal. Le problème se situe à la ligne 19.

La fonction Console.Read() :
Lit le caractère suivant à partir du flux d'entrée standard.

Pour que cela fonctionne correctement, utilise plutôt :
Console.ReadLine()
Lit la ligne de caractères suivante à partir du flux d'entrée standard.

Concernant, le Convert.Int16(int), je te conseillerais d'utiliser Convert.Int32(int). La différence entre les deux est la quantité de mémoire allouée.

Int16 => 16 bits | Accepte des entiers allant de -32768 à 32767
Int32 => 32 bits. | Accepte des entiers allant de -2147483648 à 2147483647

Int16 correspond au type "short"
Int32 correspond au type "int"
Int64 correspond au type "long"

Comme ta liste est de type "int", je te recommande Int32.

Pour information, je te suggère de ne pas utiliser l'instruction "break". Bien qu'elle fonctionne et qu'elle soit obligatoire dans certains cas, on essaye de l'utiliser le moins souvent possible.

Utilise plutôt un "bool" :

bool termine = false;
for(int compteur = 0; compteur < nb_valeurs +1 && !termine;) {

    Console.WriteLine("Ecrivez une valeur, -1 pour terminer: ");
    liste.Add(Convert.ToInt16(Console.Read()));

    if (liste[compteur]!=-1)
    {
         compteur++;
         nb_valeurs++;
     }
     else termine = true;
}



Note : !termine est équivalent à => termine == false.

Donc, la boucle va s'arrêter dès que "termine" sera à true.

Petit précision, sache que tu n'as pas besoin de compter le nombre de valeurs. Les listes ainsi que les tableaux contiennent déjà des accesseurs (propriétés) permettant d'effectuer les comptes.

List<int> liste = new List<int>();
liste.Add(1);
liste.Add(2);

Console.WriteLine(liste.Count); //Affiche 2 à l'écran

int[] tabInt = new Int[4];
for(int i = 0; i < tabInt.Length; i++)  //tabInt.Length vaut 4
    tabInt[i] = i;

Console.WriteLine(tabInt.Length); //Affiche 4 à l'écran



Un dernier conseil, tu devrais réviser le type de boucle que tu as choisi.

Utilisation du for :

On l'utilise lorsqu'on sait combien de fois la boucle doit être exécuter. Par exemple, lorsqu'on doit parcourir une liste ou un tableau, on sait qu'on doit parcourir un nombre précis d'éléments.


Utilisation du while :

On l'utilise lorsqu'on a aucune idée de quand la boucle va s'arrêter. On met une condition dans le while et on boucle jusqu'à ce qu'elle soit remplie.


Utilisation du do...while :

Même chose que le while à une chose près : Comme la condition est vérifiée à la fin de la boucle, on est sûr que la boucle est exécutée au moins une fois.

C'est probablement le choix qui te conviendrait le mieux. Tu ne sais pas à quel moment ta boucle va s'arrêter (pas selon un compte précis), mais tu connais la condition (tant que la valeur saisie n'est pas -1). Donc on élimine le for.

Ensuite, comme tu veux que l'utilisateur puisse entrer une valeur au moins une fois, tu devineras que le do...while est la meilleure option ;).

Donc :

do
{
    Console.WriteLine("Écrivez....");
} while( /* ta condition /*);


Note : Il y a un ; après la parenthèse fermante du while.
1
fil5m Messages postés 9 Date d'inscription dimanche 3 août 2014 Statut Membre Dernière intervention 12 août 2014 1
3 août 2014 à 08:38
Signification de void :

Je vais commencer par quelque chose de plus simple : le type de retour int.
Supposons la fonction suivante :

static int doublerNombre(int monNombre) {
    return monNombre * 2;
}


Ici lorsque j'appelle cette fonction comme suit :
int nombreFinal = doublerNombre(5); //NombreFinal vaut 10


La valeur calculée dans l'instruction "return" (indiquer comme un "int") de la fonction doublerNombre se retrouve dans "nombreFinal".

Parfois, il arrive qu'on ait pas besoin d'une valeur de retour. Supposons que je veuille simplement afficher un message à l'écran, je pourrais utiliser la fonction suivante :

static void HelloWorld1() {
    Console.WriteLine("Hello World"); //Affiche Hello World à l'écran
    //Comme le type de retour est void, on ne retourne rien
}

static int HelloWorld2() {
    Console.WriteLine("Hello World"); //Affiche Hello World à l'écran
    return 0; //Type de retour est "int", on doit donc retourner un entier
}


Comme tu vois, HelloWorld1() est plus adaptée puisque le type de retour ne sert à rien dans un cas comme celui-ci. Pourquoi on s'embêterait à retourner une valeur dans une telle fonction ?

Entre les parenthèses du main :

Il s'agit en fait des arguments (paramètres) facultatifs qui peuvent être passer au programme lors de son exécution. Par exemple, si ton programme s'appelle "test.exe", si tu le démarres normalement, "args" sera vide. Par contre, il est possible de le démarrer avec des arguments : test.exe -arg1 -arg2.

args[0] => "arg1"
args[1] => "arg2"

Ici, le symbole => veut dire "vaut".
Si tu ne comprends pas, ce n'est pas grave pour le moment. Ça viendra ;)

IndexOutOfRangeException :

À la ligne 16 de ton code, tu déclares et initialise un tableau. Un tableau est une collection de données de taille fixe. Dans ton cas, tu lui donnes une taille de 0. Cela implique que tu ne peux pas ajouter quoi que ce soit dedans.

Une liste serait plus adaptée dans ton cas. Au niveau de l'utilisation, ça ressemble beaucoup à un tableau. La grande différence c'est que tu peux supprimer et ajouter des éléments à la collection.

using System.Collections.Generic; //À rajouter en haut pour utiliser la liste

static void main(string args) {
    List<int> listeValeurs = new List<int>();

    listeValeurs.Add(1);  //listeValeurs[0] => 1
    listeValeurs.Add(2);  //listeValeurs[1] => 2

    Console.WriteLine(listeValeurs[0]); //Affiche 1 à l'écran
}


À la ligne 4, on déclare une liste. Entre < >, on indique le type que va contenir la collection. Ici, comme on souhaite avoir une liste contenant des entiers, j'ai mis "int". Les parenthèses à la fin sont requises pour faire l'initialisation. Tu verras ça lorsque tu seras rendu à jouer avec les classes. C'est ce qu'on appelle un constructeur. Pour l'instant, ajoute-les. Si tu ne les mets pas, listeValeurs sera null.
0
Booniak Messages postés 11 Date d'inscription vendredi 1 août 2014 Statut Membre Dernière intervention 29 août 2014 5
3 août 2014 à 20:11
Merci pour le coup de main, j'ai tenté de refaire le début en utilisant une liste, deux problème apparaissent:
-après la première saisie le message "Ecrivez..." ce répète 3 fois à chaque saisie.
- La sasie de -1 ne met pas fin à la boucle.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        
        static void Main(string[] args)
        {
            int compteur, nb_valeurs =0;
            List<int> liste = new List<int>();

            for (compteur = 0; compteur < nb_valeurs + 1; )
            {
                Console.WriteLine("Ecrivez une valeur, -1 pour terminer: ");
                    liste.Add(Convert.ToInt16(Console.Read()));

                    if (liste[compteur]!=-1)
                    {
                        compteur++;
                        nb_valeurs++;
                    }
                    else break;
                }
0
Booniak Messages postés 11 Date d'inscription vendredi 1 août 2014 Statut Membre Dernière intervention 29 août 2014 5
3 août 2014 à 22:24
Wow je suis étonné que tout marche normalement juste grâce à ReadLine, le "-1 " n'était pas prit en compte parce que la console ne lisait que "-" ? Pourquoi ça entraîné une répétition? Y a t-il une vie après la mort? Tant de questions sans réponses! Pour les booléens et les boucles c'est des trucs que je connais en théorie mais qui me viennent pas à l'esprit. Merci pour les tuyaux Senior Zorro!
0
fil5m Messages postés 9 Date d'inscription dimanche 3 août 2014 Statut Membre Dernière intervention 12 août 2014 1
3 août 2014 à 22:37
La répétition était causé par le nombre de caractère. Lorsque tu entres 1 et que tu appuies sur Entrée, voici ce qui se passe :

Le premier Console.Read() récupère "1"

Le deuxième coup, le programme n'arrête pas lors du Console.Read() parce qu'il y a encore du texte à lire dans la console. Tu n'as pourtant qu'entré "1".... Qu'est-ce qu'il peut bien rester ?

En fait, il reste ce que la touche Entrée a provoqué c'est à dire un caractère de retour chariot et le caractère de nouvelle ligne soit :

\r\n

Donc lorsque tu entres "1" dans la console et que tu appuies sur Entrée, la console renvoie :

1\r\n

Note : \r compte pour un seul caractère et \n aussi. L'antislash agit comme caractère d'échappement.
0