Organisation en plusieurs fichiers sources
Ah non
Bien sûr, un looooooooog fichier, ce n’est pas maintenable.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// Structure pour associer un Katakana à sa prononciation (Romaji)
typedef struct {
char *katakana;
char *romaji;
} Kana;
// Base de données des Katakanas de base
Kana baseKatakana[] = {
{"ア", "a"}, {"イ", "i"}, {"ウ", "u"}, {"エ", "e"}, {"オ", "o"},
{"カ", "ka"}, {"キ", "ki"}, {"ク", "ku"}, {"ケ", "ke"}, {"コ", "ko"},
{"サ", "sa"}, {"シ", "shi"}, {"ス", "su"}, {"セ", "se"}, {"ソ", "so"},
{"タ", "ta"}, {"チ", "chi"}, {"ツ", "tsu"}, {"テ", "te"}, {"ト", "to"},
{"ナ", "na"}, {"ニ", "ni"}, {"ヌ", "nu"}, {"ネ", "ne"}, {"ノ", "no"},
{"ハ", "ha"}, {"ヒ", "hi"}, {"フ", "fu"}, {"ヘ", "he"}, {"ホ", "ho"},
{"マ", "ma"}, {"ミ", "mi"}, {"ム", "mu"}, {"メ", "me"}, {"モ", "mo"},
{"ヤ", "ya"}, {"ユ", "yu"}, {"ヨ", "yo"},
{"ラ", "ra"}, {"リ", "ri"}, {"ル", "ru"}, {"レ", "re"}, {"ロ", "ro"},
{"ワ", "wa"}, {"ヲ", "wo"}, {"ン", "n"}
};
int totalKana = sizeof(baseKatakana) / sizeof(baseKatakana[0]);
int main() {
int score = 0;
int nombreQuestions = 10; // Nombre de questions par partie
// Initialisation de l'aléatoire
srand(time(NULL));
printf("=== QCM : APPRENDRE LES KATAKANAS ===\n");
printf("Trouvez la bonne transcription (Romaji) pour chaque symbole.\n\n");
for (int q = 1; q <= nombreQuestions; q++) {
// 1. Choisir un Katakana aléatoire pour la question
int indexCorrect = rand() % totalKana;
Kana cible = baseKatakana[indexCorrect];
// 2. Choisir à quelle position (1 à 4) sera la bonne réponse
int bonneReponsePos = (rand() % 4) + 1;
int choix[4];
// Remplir les 4 choix (1 correct + 3 faux uniques)
choix[bonneReponsePos - 1] = indexCorrect;
for (int i = 0; i < 4; i++) {
if (i == bonneReponsePos - 1) continue;
int fauxIndex;
int dejaPresent;
do {
dejaPresent = 0;
fauxIndex = rand() % totalKana;
// Vérifier qu'on ne met pas deux fois le même choix
if (fauxIndex == indexCorrect) {
dejaPresent = 1;
}
for (int j = 0; j < i; j++) {
if (choix[j] == fauxIndex) {
dejaPresent = 1;
}
}
} while (dejaPresent);
choix[i] = fauxIndex;
}
// 3. Afficher la question
printf("--- Question %d / %d ---\n", q, nombreQuestions);
printf("Quel est ce Katakana : [ %s ] ?\n", cible.katakana);
for (int i = 0; i < 4; i++) {
printf("%d) %s\n", i + 1, baseKatakana[choix[i]].romaji);
}
// 4. Lire la réponse de l'utilisateur
int reponse;
printf("Votre choix (1-4) : ");
if (scanf("%d", &reponse) != 1) {
printf("Entrée invalide. Passage à la question suivante.\n");
while (getchar() != '\n'); // Vider le buffer
continue;
}
// 5. Vérifier la réponse
if (reponse == bonneReponsePos) {
printf("Bravo ! C'etait bien '%s'.\n\n", cible.romaji);
score++;
} else {
printf("Dommage... La bonne réponse était %d) %s\n\n", bonneReponsePos, cible.romaji);
}
}
// 6. Score final
printf("=== FIN DU QUIZ ===\n");
printf("Votre score final : %d / %d\n", score, nombreQuestions);
if (score == nombreQuestions) {
printf("Parfait ! Vous maîtrisez vos Katakanas !\n");
} else if (score >= nombreQuestions / 2) {
printf("Pas mal ! Encore un petit effort.\n");
} else {
printf("Il faut encore réviser un peu ! \n");
}
return 0;
}
Fichiers source et fichiers header
On découpe le projet en plusieurs fichiers.
Fichiers source
.c contient les définitions des fonctions.
Un fichier fonction_mathematiques.c contient par exemple :
#include "fonctions_mathematiques.h"
int addition(const int a, const int b) {
return a + b;
}
int multiplication(const int a, const int b) {
return a * b;
}
int division(const int a, const int b) {
return a / b;
}
int sousroutine(int x) {
...
}
...
Fichiers header
.h contient des déclarations de fonctions, ainsi que les spécifications.
Voici un exemple de fichier header (vous pouvez écrire les spécifications avec l’extension Doxygen Documentation) :
/**
* @brief perform an addition in O(1)
*
* @param a
* @param b
* @return the sum of a and b
*/
int sum(const int a, const int b);
/**
* @brief perform a multiplication in O(1)
*
* @param a
* @param b
* @return the product of a and b
*/
int product(const int a, const int b);
/**
* @brief perform an Euclidean division
*
* @param a
* @param b non zero
* @return the Euclidean division of a and b. If b is non zero, the behavior is not defined.
*/
int division(const int a, const int b);
...
Attention aux inclusions circulaires
Si main.c inclut fonctions_mathematiques.h et bloup.h qui lui aussi inclut fonctions_mathematiques.h, on inclut fonctions_mathematiques.h deux fois ! Ca ne va pas compiler !
Solution traditionnelle : un #define artificiel
#ifndef FONCTIONS_MATHEMATIQUES_H
#define FONCTIONS_MATHEMATIQUES_H
/**
* @brief perform an addition in O(1)
*
* @param a
* @param b
* @return the sum of a and b
*/
int sum(const int a, const int b);
/**
* @brief perform a multiplication in O(1)
*
* @param a
* @param b
* @return the product of a and b
*/
int product(const int a, const int b);
/**
* @brief perform an Euclidean division
*
* @param a
* @param b non zero
* @return the Euclidean division of a and b. If b is non zero, the behavior is not defined.
*/
int division(const int a, const int b);
#endif
Solution moderne : #pragma once
#pragma once
/**
* @brief perform an addition in O(1)
*
* @param a
* @param b
* @return the sum of a and b
*/
int sum(const int a, const int b);
/**
* @brief perform a multiplication in O(1)
*
* @param a
* @param b
* @return the product of a and b
*/
int product(const int a, const int b);
/**
* @brief perform an Euclidean division
*
* @param a
* @param b non zero
* @return the Euclidean division of a and b. If b is non zero, the behavior is not defined.
*/
int division(const int a, const int b);
Exercice
Découper votre projet :
- Fichier
dynarray.cqui contient les définitions pour utiliser undynarray - Fichier
dynarray.hqui ne contient que les déclarations et spécifications des fonctions - Fichier
main.cqui utilisedynarrayet qui inclutdynarray.h - Compiler avec
gcc dynarray.c main.c -o main -Wall
- Cloner le dépôt qui contient le jeu de tir en C dans le terminal https://git.aliens-lyon.fr/enslyon-l3-prog/shmup_in_c
- Le lancer.
- Découper le projet en plusieurs fichiers pour améliorer la lisibilité et l’extension futur.
- Ajouter des explosions quand un tir touche un ennemi
- Ajouter le déplacement des étoiles.
Dossiers
Dans des très grands projets, afin de pouvoir distribuer plus facilement les .h importants, on met tous les fichiers source .c et .h intermédiaires dans un dossier src et les fichiers header importants dans un dossier include. On organise le code par sous-dossiers.