IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Diviser un gros programme en include.h


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Par défaut Diviser un gros programme en include.h
    Bonsoir,

    J'ai fini et débuggé un programme pour une camera embarquée écrit en C procédural.

    Le programme se divise en trois parties :

    Gestion et acquisition du temps

    Gestion, écriture et lecture d'un fichier index

    Gestion des commandes systèmes pour l'enregistrement de la vidéo.

    La première idée serait de faire : temps.h,index.h et system.h

    Cependant, je n'ai aucune idée pour comment déclarer ces includes dans le fichier principal ( camera.c )

    Des conseils s.v.p

  2. #2
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    329
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 329
    Par défaut
    Bah si tu as déjà scindé ton code source (.c) en mettant chaque fonction dans un code source (.c) différent, c'est déjà pas mal.

    Après, si tu veux faire les choses proprement, tu mettrais les définitions des fonctions et des constantes symboliques (ainsi que les structures, énumérations et autres données utilisées dans tout le programme) dans un fichier d'en-tête (header file .h).
    En ayant placé des "gardes" (#ifndef #define ... #endif) dans le fichier .h, dans chaque fichiers .c contenant les fonctions, tu ajouterais le fichier .h dans chaque fichier .c.

    Ensuite pour la compilation, si tu passes par un "makefile", c'est ultra simple, si tu passes par un IDE, il faudra rajouter tous tes fichiers .c dans le projet, et puis ça devrait se compiler si tu as bien fait cela.

    exemple de "garde":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    #ifndef EXEMPLE_H
    #define EXEMPLE_H
     
    ...ici viennent s'ajouter tout ce qui doit être contenu dans le fichier d'en-tête...
    ...constantes symboliques
    ...définitions de structures
    ...énumérations
    ...définitions des fonctions (prototypes)
    ...
     
    #endif
    fichier makefile:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    exemple: exemple.c fonction1.c fonction2.c fonction3.c exemple.h

  3. #3
    Membre éclairé
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Par défaut Heu ! Un exemple s.v.p
    Bon voici une fonction dans mon entête time.h ( exemple )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char* current_time()
    {
    int error;
     
            time_t curtime;
            time(&curtime);
    	error=errno;
    	if (!(error)) return(ctime(&curtime));
    	else {fprintf(stderr,"Erreur current_time() : %s\n",strerror(error));exit(EXIT_FAILURE);}
    }
    Comment dois-je déclarer ce fichier dans main.c ?

    D'autre part, je ne voudrai pas utiliser la commande make, mais uniquement le compilateur gcc .

    Actuellement je fait : gcc -Wall -pedantic ./camera.c -o camera

    Faudra-t-il changer ces paramètres à la compilation ?

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par frederic13870 Voir le message
    La première idée serait de faire : temps.h,index.h et system.h

    Cependant, je n'ai aucune idée pour comment déclarer ces includes dans le fichier principal ( camera.c )
    C'est pas mal comme approche. C'est d'ailleurs un des principes de programmation MVC (séparer le Modèle, la façon dont sont stockées les données, de la Vue, la façon de les saisir et afficher, et du Contrôleur, la façon de les traiter).
    Il te faut définir chaque ".h" comme ne contenant que les éléments nécessaire au ".c" correspondant. Et mettre dans le ".c" que les fonctions associées. Et ne pas oublier les include guard (exemple inclure a.h et b.h sans savoir que a.h inclut lui aussi b.h). Et surtout pas de code C dans un ".h". Le code C c'est dans un source ".c".
    Et le programme final "camera.c" incluera tout et contiendra le main qui fait appel à tout ça.

    Exemple complet et fonctionnel
    temps.h
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifndef __TEMPS_H_
    #define __TEMPS_H_
    // Ca c'est l'include guard. La première inclusion définit __TEMPS_H_ et comme il est défini, toute nouvelle inclusion voit le "ifndef" faux et ne passe alors pas ici
     
    #define TIME_HOUR				24
    #define TIME_MIN				60
    // Oui, c'est juste un exemple con
     
    // Prototype des fonctions du ".c" correspondant
    float temps_trucA(int, float);
    int temps_trucB(char *);
    #endif // __TEMPS_H_
    // Fin de l'include guard. Toute nouvelle inclusion arrive directement ici et ici il n'y a rien

    temps.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <stdio.h>	// Si nécessaire c'est autorisé
    #include <string.h>	// C'est parce que j'utilise strlen
    #include "temps.h"	// Et tu peux même écrire #include <temps.h> si tu compiles ensuite avec l'option "-I." pour indiquer que les include peuvent être trouvés dans le dossier courant
     
    float temps_trucA(int x, float y) {
    	return x/y;
    }
     
    int temps_trucB(char *nom) {
    	return strlen(nom);
    }

    index.h
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #ifndef __INDEX_H_
    #define __INDEX_H_
     
    // Prototype des fonctions du ".c" correspondant
    float index_trucA(int, float);
    int index_trucB(char *);
    #endif // __INDEX_H_

    index.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <string.h>
    #include "index.h"
     
    float index_trucA(int x, float y) {
    	return x/y;
    }
     
    int index_trucB(char *nom) {
    	return strlen(nom);
    }

    system.h
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #ifndef __SYSTEM_H_
    #define __SYSTEM_H_
     
    // Prototype des fonctions du ".c" correspondant
    float system_trucA(int, float);
    int system_trucB(char *);
    #endif // __SYSTEM_H_

    system.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <string.h>
    #include <system.h>						// Ici utilisation de "<>" => option "-I." obligatoire
     
    float system_trucA(int x, float y) {
    	return x/y;
    }
     
    int system_trucB(char *nom) {
    	return strlen(nom);
    }

    Ensuite, pour camera.c
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <stdio.h>			// Pas de souci, le include_guard qu'il contient le protège
     
    // Les includes locaux
    #include "temps.h"
    #include "index.h"
    #include "system.h"
     
    /*
    Pareil, compilation avec "-I." permet d'écrire
    #include <temps.h>
    #include <index.h>
    #include <system.h>
    C'est sympa mais dans ce cas faut bien évidemment ne pas créer de ".h" local ayant un même nom qu'un ".h" stantard sinon conflit !!!
    */
     
    int main() {
    	printf("%f - %d\n", temps_trucA(5, 6.0), temps_trucB("demain"));
    	printf("%f - %d\n", index_trucA(3, 4.0), index_trucB("hier"));
    	printf("%f - %d\n", system_trucA(7, 12.0), system_trucB("plus tard"));
    }

    Pour la compilation (l'option "-I." est à utiliser si on a écrit "<>" pour les include locaux)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    gcc -I. -c temps.c
    gcc -I. -c index.c
    gcc -I. -c system.c
    gcc -I. -c camera.c
    gcc temps.o index.o system.o camera.o -o camera
    Et si tu veux automatiser tout ça dans un joli Makefile qui te permettra de recompiler tout ou partie de ton projet quand ça te chante
    Code Makefile : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #
    # Fichier Makefile de compilation camera
    #
     
    CC=gcc
    CFLAGS=-I. -Wall -Werror
    LDFLAGS=-lm
     
    MAINFILE=camera.c
    EXECFILE=$(MAINFILE:.c=)
     
    OTHERFILES=temps.c index.c system.c
    HEADERFILES=$(OTHERFILES:.c=.h)
    OBJECTFILES=$(OTHERFILES:.c=.o) $(MAINFILE:.c=.o)
     
    camera: $(OBJECTFILES)
    	$(CC) $^ $(LDFLAGS) -o $@
     
    camera.o: $(MAINFILE) $(HEADERFILES)
    	$(CC) $(CFLAGS) -c $<
     
    temps.o: temps.c temps.h
    	$(CC) $(CFLAGS) -c $<
     
    index.o: index.c index.h
    	$(CC) $(CFLAGS) -c $<
     
    system.o: system.c system.h
    	$(CC) $(CFLAGS) -c $<
     
    clean:
    	rm -f $(OBJECTFILES) $(EXECFILE)
     
    all:
    	@make clean
    	@make $(EXECFILE)

    Tu veux créer l'exécutable "camera" tu tapes "make camera" ou simplement "make" (la première cible est celle prise par défaut). Tu veux compiler seulement "system.o", tu tapes "make system.o". Tu veux compiler "camera.o" pareil. Tu veux effacer les trucs inutiles tu tapes "make clean". Et tu veux tout recompiler de zéro tu tapes "make all".
    Chaque action regardera les dependances associées et ne recompilera que ce qu'il faut si une dépendance est plus récente que la cible.

    Citation Envoyé par frederic13870 Voir le message
    Bon voici une fonction dans mon entête time.h ( exemple )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char* current_time()
    {
    int error;
     
            time_t curtime;
            time(&curtime);
    	error=errno;
    	if (!(error)) return(ctime(&curtime));
    	else {fprintf(stderr,"Erreur current_time() : %s\n",strerror(error));exit(EXIT_FAILURE);}
    }
    Non, pas de code source dans un header. Ca fonctionne mais cela ne se fait pas. Un header c'est juste un "mode d'emploi".
    De même on ne quitte jamais une fonction par exit(). Si un truc se passe mal, on remonte le problème à l'appelant qui pourra alors gérer (et si lui ne sait pas gérer alors lui aussi remontera le souci à son appelant et etc jusqu'au main qui, seul, a le droit de quitter).

    Et surtout pas de test if (!errno) qui sous-entend que errno vaut 0 quand la commande système a réussi car justement, une commande qui réussi ne met pas errno à 0 (pourquoi perdre du temps CPU à mettre à 0 une variable inutile ?). errno est une variable qui sert à indiquer une cause d'échec, pas une cause de réussite. Quand la commande réussit, elle renvoie une valeur (un état) qui peut éventuellement servir de test.
    Extrait du man de time
    RETURN VALUE
    On success, the value of time in seconds since the Epoch is returned.
    On error, ((time_t) -1) is returned, and errno is set appropriately.
    C'est bien indiqué "en cas d'erreur errno est positionné de façon appropriée". C'est un peu ce à quoi je pensais quand j'ai dit qu'on ne testait jamais errno. Et accessoirement, ça ne mange pas de pain de remplacer un if (!var) par un bien plus lisible if (var == 0).

    Et éventuellement on essaye de ne pas mélanger "traitement" et "affichage" (toujours philosophie MVC mais bon, ça c'est moins grave).

    Citation Envoyé par frederic13870 Voir le message
    D'autre part, je ne voudrai pas utiliser la commande make, mais uniquement le compilateur gcc .
    Il ne faut pas confondre. "make" c'est un outil permettant de définir "comment construire C à partir de A et éventuellement B". Une fois les règles écrites, à chaque appel la commande regardera si une dépendance est plus récente que sa cible et si oui refera la cible. Et ceci en cascade si la cible C est aussi ensuite une dépendance d'une éventuelle cible D.
    gcc c'est juste une commande de compilation permettant de créer B à partir de A. Donc pouvant être utilisée par make pourvu que les règles de dépendances soient bien écrites (écrites dans un fichier nommé "Makefile").
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  5. #5
    Membre éclairé
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Par défaut Merci ! je suis en train de le faire !
    À part le make, j'ai à peu près tout saisi !

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 833
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 833
    Billets dans le blog
    1
    Par défaut
    Bah le make ce n'est pas bien compliqué.
    Tout d'abord je définis des variables, ce qui me permet de centraliser les choses. Si par exemple demain je préfère utiliser cc à la place de gcc, je ne modifie qu'une chose dans le Makefile.

    Ensuite il faut juste lire les règles. Chaque règle commence par décrire ce qu'on veut faire suivi d'un double point suivi de la liste des éléments qui en dépendent.
    Et sur les lignes du dessous, avec une tabulation obligatoire, les commandes que l'on taperait si on devait faire nous-même l'action. et si on fait précéder la commande d'un @, la commande n'est alors pas affichée à l'écran (pratique pour le "make all" qui lance alors "make clean" puis "make camera" de façon invisible, on ne voit alors pas le premier appel on ne voit que la création des cibles).
    Exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    temps.o: temps.c temps.h
    	$(CC) $(CFLAGS) -c $<
    Pour obtenir "temps.o" j'ai besoin de "temps.c" et "temps.h". Ainsi si l'un des deux est modifié plus tard, make saura qu'il faut refaire "temps.o". Ensuite pour créer "temps.o" il faut taper "gcc -I. -Wall -Werror temps.c". Chaque variable perso comme "CC" doit être entre parenthèses et la variable "$<" (variable spéciale make) indique la première dépendance (ici temps.c).

    Autre exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    camera: $(OBJECTFILES)
    	$(CC) $^ $(LDFLAGS) -o $@
    Même principe sauf que là l'exécutable dépend de tous les ".o" (qui ont été mis dans la variable "OBJECTFILES") et les options "-I.", "-Wall" et "-Werror" sont inutiles (il s'agit de l'édition de liens) donc j'utilise une autre variable dans laquelle j'appelle la librairie mathématique (pour l'exemple).
    Et là je change de variable interne car "$^" représente toutes les dépendances (oui, pour faire l'exécutable on a besoin de tous les ".o") et $@ représente la cible (donc "camera").
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Membre éclairé
    Homme Profil pro
    Webmaster
    Inscrit en
    Mai 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France

    Informations professionnelles :
    Activité : Webmaster
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2011
    Messages : 258
    Par défaut Ok ! Mais pour les variables globales ?
    J'ai un problème.

    Dans camera.c, à l'origine, j'utilise des variables globales.

    Je suppose qu'il faut ajouter celles-ci au time.c , index.c ....

    Est-ce cela ?

  8. #8
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 766
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 766
    Par défaut
    Citation Envoyé par frederic13870 Voir le message
    J'ai un problème .... des variables globales.
    tu as 2 stratégies

    1) Soit garder tes variables globales. Il faut les déclarer en extern dans l'entête (.h) et les définir dans le source (.c)

    2) Soit supprimer tes variables globales. En gros et pour simplifier, tu créer tes variables dans le main et/ ou dans "tes objets" et ensuite tu utilises les injections de dépendances (<- lien wiki en anglais) ou autres pour passer ces variables.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 28/05/2019, 23h08
  2. Question sur les #include sur un gros projet
    Par Pogzy dans le forum C++
    Réponses: 5
    Dernier message: 28/12/2018, 18h35
  3. Réponses: 10
    Dernier message: 10/02/2017, 14h58
  4. Diviser le traitement d'une requête créant un trop gros resultSet (OutOfMemoryException)
    Par Heavy Metal Hero dans le forum Persistance des données
    Réponses: 4
    Dernier message: 04/12/2015, 17h24
  5. Gros problème d'include
    Par yorukaze dans le forum Langage
    Réponses: 7
    Dernier message: 10/09/2009, 17h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo