Plan du site  
pixel
pixel

Articles - Étudiants SUPINFO

Plateau pour application console en C

Par Geoffrey TESTELIN Publié le 29/07/2015 à 23:03:43 Noter cet article:
(0 votes)
Avis favorable du comité de lecture

Introduction

Nous verrons ensemble comment réaliser facilement et efficacement un plateau/tableau pour une application.

Cet article est destiné aux étudiants débutants en programmation.

Nous utiliserons le langage C dans une application console (sans interface graphique).

Pour beaucoup d'entre vous, faire un plateau n'est plus du tout un problème.

En revanche, pour un débutant en programmation, cette tâche peut s'avérer être difficile.

Il faut bien comprendre comment concevoir un plateau efficacement afin d'optimiser le code le mieux possible.

Petit clin d'oeil : DRY (Don't Repeat Yourself !) #Supinfo.

Les fichiers

main.c

Voici un rappel du main classique d'un programme en C (sous Code::Blocks..).

#include <stdio.h>
#include <stdlib.h>
#include "plateau.h"

int main()
{
    return EXIT_SUCCESS;
}

plateau.h

Plateau.h contient les prototypes de nos fonctions.

#ifndef PLATEAU_H_INCLUDED
#define PLATEAU_H_INCLUDED

#endif // PLATEAU_H_INCLUDED

plateau.c

Plateau.c contient les fonctions et les implémentations du code.

#include <stdio.h>
#include <stdlib.h>
#include "plateau.h"

P.S : N'oubiez pas d'ajouter les fichiers Plateau.h/Plateau.c dans l'analyse du compilateur pour le mode debug/release.

[Important]

Pour rappel le but de cet article est de bien comprendre comment faire son plateau avec le moins de code possible.

Je vais donc expliquer ce qu'il faut faire et ne pas faire pour éviter de tomber dans le piège de la répétition.

Nous allons procéder étape par étape.

Création du plateau

plateau.h

Je précise un élément important. En règle général, nous ne connaissons pas la taille du plateau.

Pour respecter les "best practices", il est fortement conseillé de faire la déclaration de celui-ci dans le header comme ci-dessous.

La taille du plateau est dynamique, par conséquent, l'utilisation d'un double pointeur s'impose pour pallier au problème.

int taillePlateau;               // Contient la taille du plateau
char **plateau;                  // Double pointeur pour allocation dynamique de la taille du plateau

void nouveauPlateau(int taille); // Création d'un plateau avec taille variable

void destructionPlateau();       // Détruit les allocations de mémoire

plateau.c

La fonction nouveauPlateau() va allouer de la mémoire dynamiquement pour répondre à nos besoins.

C'est à dire que nous allons créer un plateau en deux dimensions en fonction du paramètre d'entrée de la fonction.

L'utilisation du sizeof() sur notre pointeur de tableau était nécéssaire dans ce cas.

[Note]

Il existe plusieurs façons de créer des tableaux dynamiques, je ne sais pas si cette manière de faire est la meilleure mais en tout cas elle fonctionne parfaitement.

void nouveauPlateau(int taille)
{
    taillePlateau = taille;                                         // Modifie la taille du plateau dans le header
    printf("La taille du plateau est de %d.\n", taille);            // Affiche la taille du plateau dans la console

    int index;                                                      // Index utilisé pour le parcours du plateau
    plateau = malloc(taillePlateau * sizeof(*plateau));             // Alloue chaque ligne du plateau
    for(index = 0; index < taillePlateau; index++)                  // Parcourt les lignes du plateau
    {
        plateau[index] = malloc(taillePlateau * sizeof(**plateau)); // Alloue chaque colonne dans la ligne en fonction de l'index et de la taille du plateau
    }
}

La fonction destructionPlateau() va supprimer les allocations de mémoire de chaque case du plateau.

La boucle for() parcourt chaque ligne du plateau et libère l'allocation de chaque colonne de cette ligne.

Il est crucial de faire un free() après un malloc() pour éviter des fuites de mémoire et d'éventuels bug.

void destructionPlateau()
{
    int index;                                     // Index utilisé pour le parcours du plateau
    for(index = 0; index < taillePlateau; index++) // Parcourt les lignes du plateau
    {
        free(plateau[index]);                      // Libère l'allocation de mémoire de chaque colonne dans la ligne en fonction de l'index
    }
    free(plateau);                                 // Libère l'allocation de mémoire pour les lignes du plateau
}

Affichage du plateau

plateau.h

Rien de bien compliqué ici...

void affichagePlateau();         // Affiche le plateau

plateau.c

Dans cette partie, on parcours chaque ligne et colonne du plateau.

Les cases n'ont pas encore de valeur, par conséquent on initialise chaque case avec la valeur 'O' à titre d'exemple.

Pour finir, on affiche le contenu de chaque case.

void affichagePlateau()
{
    int ligne, colonne;                                      // Permet le parcours du plateau facilement
    for(ligne = 0; ligne < taillePlateau; ligne++)           // Parcourt chaque ligne du plateau
    {
        for(colonne = 0; colonne < taillePlateau; colonne++) // Parcourt chaque colonne du plateau en fonction de la ligne
        {
            plateau[ligne][colonne] = 'O';                   // Initialisation de chaque case
            printf("%c", plateau[ligne][colonne]);           // Affiche le contenu de chaque case
        }
    }
}

Voila le résultat, c'est ignoble.

Il va de soit qu'une mise en forme du plateau s'impose.

Je précise un élément important pour les débutants :

  1. Le contenu des cases du tableau doit contenir exclusivement les valeurs voulues, la "décoration" ne doit pas être contenue dans ce tableau !

  2. L'esthétisme est forcément très important sinon le plateau n'est pas un plateau, et n'est bien évidement pas exploitable comme ci-dessus.

  3. Pour un débutant qui ne connait pas la puissance du for(), on peut très vite se compliquer l'existence. :-(

  4. D'autres langages ont des fonctionnalités beaucoup plus intéréssantes pour réaliser des plateaux, je pense au foreach() par exemple. Voir aussi l'utilisation d'un interface graphique.

Commençons par le plus simple : la séparation des cases.

Le résultat nous procurera nos colonnes de séparations.

On ajoute simplement avant le contenu de la case une étoile et un espace puis un espace.

printf("%c", plateau[ligne][colonne]);           // Avant
printf("* %c ", plateau[ligne][colonne]);        // Après

Voila le résultat en image.

Nous avons nos premières colonnes mais toutes les données sont à la suite.

Faire un retour à la ligne après avoir parcouru la colonne s'impose.

[Note]

Un retour à la ligne se fait avec un "\n".

for(colonne = 0; colonne < taillePlateau; colonne++)
{
    plateau[ligne][colonne] = 'O'
    printf("* %c ", plateau[ligne][colonne]);
}
printf("*\n");                                       // Bordure droite du plateau et retour à la ligne

Nos colonnes et nos lignes sont maintenant bien distinctes.

Il manque toujours les bordures entre chaques lignes.

Encore une fois on évite la répétition et on fait en sorte d'avoir un plateau "dynamique".

De mon point de vue, passer par une méthode est tout à fait envisageable. Démonstration.

On ajoute l'appel de la méthode affichageLigne() à deux reprises.

    
for(ligne = 0; ligne < taillePlateau; ligne++)           // Parcourt chaque ligne du plateau
{
    affichageLigne();                                    // Affiche une ligne de séparation à chaque boucle
    for(colonne = 0; colonne < taillePlateau; colonne++) // Parcourt chaque colonne du plateau en fonction de la ligne
    {
        plateau[ligne][colonne] = 'O';                   // Initialisation de chaque case
        printf("* %c ", plateau[ligne][colonne]);        // Affiche le contenu de chaque case
    }
    printf("*\n");                                       // Bordure droite du plateau et retour à la ligne
}
affichageLigne();                                        // Affiche la bordure du bas

Voici la fameuse méthode en question.

Il s'agit simplement de répété une paterne en fonction de la taille du plateau.

void affichageLigne()
{
    int nombre;                                       // Permet la répétition de la paterne
    char ligne[] = "* * ";                            // Paterne à reproduire
    for(nombre = 0; nombre < taillePlateau; nombre++) // Répétition de la paterne
    {
        printf("%s", ligne);                          // Affiche la paterne
    }
    printf("*\n");                                    // Termine la colonne de droite et retour à la ligne
}

Voilà le résultat et notre plateau est terminé.

Fichiers complets

main.c

#include <stdio.h>
#include <stdlib.h>
#include "plateau.h"

int main()
{
    nouveauPlateau(12);   // Création du plateau
    affichagePlateau();   // Affichage du plateau
    destructionPlateau(); // Destruction des variables allouées manuellement
    return EXIT_SUCCESS;  // Fermeture du programme sans erreur
}

plateau.h

#ifndef PLATEAU_H_INCLUDED
#define PLATEAU_H_INCLUDED

int taillePlateau;               // Contient la taille du plateau
char **plateau;                  // Double pointeur pour allocation dynamique de la taille du plateau

void nouveauPlateau(int taille); // Création d'un plateau avec taille variable

void affichagePlateau();         // Affiche le plateau

void affichageLigne();           // Affiche la ligne de décoration

void destructionPlateau();       // Détruit les allocations de mémoire

#endif // PLATEAU_H_INCLUDED

plateau.c

#include <stdio.h>
#include <stdlib.h>
#include "plateau.h"

void nouveauPlateau(int taille)
{
    taillePlateau = taille;                                         // Modifie la taille du plateau dans le header
    printf("La taille du plateau est de %d.\n\n", taille);          // Affiche la taille du plateau dans la console

    int index;                                                      // Index utilisé pour le parcours du plateau
    plateau = malloc(taillePlateau * sizeof(*plateau));             // Alloue chaque ligne du plateau

    if (memoireAllouee == NULL)                                     // En cas d'échec
    {
        exit(EXIT_FAILURE);                                         // On ferme le programme
    }

    for(index = 0; index < taillePlateau; index++)                  // Parcours les lignes du plateau
    {
        plateau[index] = malloc(taillePlateau * sizeof(**plateau)); // Alloue chaque colonne dans la ligne en fonction de l'index et de la taille du plateau
    }
}

void affichagePlateau()
{
    int ligne, colonne;                                      // Permet le parcours du plateau facilement

    for(ligne = 0; ligne < taillePlateau; ligne++)           // Parcours chaque ligne du plateau
    {
        affichageLigne();                                    // Affiche une ligne de séparation à chaque boucle

        for(colonne = 0; colonne < taillePlateau; colonne++) // Parcours chaque colonne du plateau en fonction de la ligne
        {
            plateau[ligne][colonne] = 'O';                   // Initialisation de chaque case
            printf("* %c ", plateau[ligne][colonne]);        // Affiche le contenu de chaque case
        }
        printf("*\n");                                       // Bordure droite du plateau et retour à la ligne
    }
    affichageLigne();                                        // Affiche la bordure du bas
}

void affichageLigne()
{
    int nombre;                                       // Permet la répétition de la patterne
    char ligne[] = "* * ";                            // Paterne à reproduire

    for(nombre = 0; nombre < taillePlateau; nombre++) // Répétition de la patterne
    {
        printf("%s", ligne);                          // Affiche la patterne
    }
    printf("*\n");                                    // Termine la colonne de droite et retour à la ligne
}

void destructionPlateau()
{
    int index;                                     // Index utilisé pour le parcours du plateau

    for(index = 0; index < taillePlateau; index++) // Parcours les lignes du plateau
    {
        free(plateau[index]);                      // Libère l'allocation de mémoire de chaque colonne dans la ligne en fonction de l'index
    }
    free(plateau);                                 // Libère l'allocation de mémoire pour les lignes du plateau
}

Evolutions

Voici quelques évolutions possibles du plateau.

Prompt sur la taille du plateau

En cas d'utilisation d'un plateau ou d'un tableau, l'utilisateur aura sans doute besoin de définir sa propre taille de plateau.

Par conséquent, ajouter un scanf() pour définir la taille du plateau est indispensable.

Ne pas oublier d'ajouter la gestion des erreurs avec des messages explicites, c'est toujours un gros plus.

Gestion des colonnes responsives

Dans cet article, je pars du principe que la case d'un plateau ne contient qu'un seul caractère.

Si une case en contient deux, les colonnes seront complètement décallées.

Gérer responsivement la taille des colonnes est une option totalement indispensable dans ce genre de cas.

Conclusion

J'espère que cet article sera utile pour nos jeunes padawan de l'informatique.

Que le C soit avec vous.

N'hésitez pas à me contacter pour toute suggestion, commentaire, conseil ou tout autre remarque.

Merci de votre lecture et bonne continuation.

That's all folks !

A propos de SUPINFO | Contacts & adresses | Enseigner à SUPINFO | Presse | Conditions d'utilisation & Copyright | Respect de la vie privée | Investir
Logo de la société Cisco, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société IBM, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sun-Oracle, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Apple, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Sybase, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Novell, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Intel, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Accenture, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société SAP, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Prometric, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo de la société Toeic, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management Logo du IT Academy Program par Microsoft, partenaire pédagogique de SUPINFO, la Grande École de l'informatique, du numérique et du management

SUPINFO International University
Ecole d'Informatique - IT School
École Supérieure d'Informatique de Paris, leader en France
La Grande Ecole de l'informatique, du numérique et du management
Fondée en 1965, reconnue par l'État. Titre Bac+5 certifié au niveau I.
SUPINFO International University is globally operated by EDUCINVEST Belgium - Avenue Louise, 534 - 1050 Brussels