web-dev-qa-db-fra.com

Comment structurer des projets complexes en C?

Je n'ai guère plus que des compétences C de niveau débutant et j'aimerais savoir s'il existe des "normes" de facto pour structurer une application quelque peu complexe en C. Même celles basées sur l'interface graphique.

J'ai toujours utilisé le paradigme OO dans Java et PHP et maintenant que je veux apprendre C je suis J'ai peur de ne pas structurer mes applications de la mauvaise façon. Je ne sais pas quelles directives suivre pour avoir la modularité, le découplage et la sécheresse avec un langage procédural.

Avez-vous des lectures à suggérer? Je n'ai trouvé aucun cadre d'application pour C, même si je n'utilise pas de cadres, j'ai toujours trouvé de bonnes idées en parcourant leur code.

78
Josh

La clé est la modularité. Ceci est plus facile à concevoir, implémenter, compiler et maintenir.

  • Identifiez les modules dans votre application, comme les classes dans une application OO.
  • Interface et implémentation séparées pour chaque module, ne mettez en interface que ce dont les autres modules ont besoin. N'oubliez pas qu'il n'y a pas d'espace de noms en C, vous devez donc rendre tout dans vos interfaces unique (par exemple, avec un préfixe).
  • Masquer les variables globales dans l'implémentation et utiliser les fonctions d'accesseur pour la lecture/écriture.
  • Ne pensez pas en termes d'héritage, mais en termes de composition. En règle générale, n'essayez pas d'imiter C++ en C, ce serait très difficile à lire et à maintenir.

Si vous avez le temps d'apprendre, jetez un œil à la structure d'une application Ada, avec ses package (interface de module) et package body (implémentation du module).

C'est pour le codage.

Pour maintenir (rappelez-vous que vous codez une fois, mais vous maintenez plusieurs fois), je suggère de documenter votre code; Doxygen est un bon choix pour moi. Je suggère également de construire une suite de tests de régression solide, qui vous permettra de refactoriser.

49
mouviciel

C'est une idée fausse courante que les techniques OO ne peuvent pas être appliquées en C. La plupart le peuvent - c'est juste qu'elles sont légèrement plus lourdes que dans les langages avec une syntaxe dédiée au travail.

L'un des fondements d'une conception de système robuste est l'encapsulation d'une implémentation derrière une interface. FILE* Et les fonctions qui fonctionnent avec lui (fopen(), fread() etc.) est un bon exemple de la façon dont l'encapsulation peut être appliquée en C pour établir des interfaces. (Bien sûr, comme C n'a pas de spécificateurs d'accès, vous ne pouvez pas imposer que personne ne jette un œil à l'intérieur d'un struct FILE, Mais seul un masochiste le ferait.)

Si nécessaire, un comportement polymorphe peut être obtenu en C à l'aide de tableaux de pointeurs de fonction. Oui, la syntaxe est moche mais l'effet est le même que les fonctions virtuelles:

struct IAnimal {
    int (*eat)(int food);
    int (*sleep)(int secs);
};

/* "Subclass"/"implement" IAnimal, relying on C's guaranteed equivalence
 * of memory layouts */
struct Cat {
    struct IAnimal _base;
    int (*meow)(void);
};

int cat_eat(int food) { ... }
int cat_sleep(int secs) { ... }
int cat_meow(void) { ... }

/* "Constructor" */
struct Cat* CreateACat(void) {
    struct Cat* x = (Cat*) malloc(sizeof (struct Cat));
    x->_base.eat = cat_eat;
    x->_base.sleep = cat_sleep;
    x->meow = cat_meow;
}

struct IAnimal* pa = CreateACat();
pa->eat(42);                       /* Calls cat_eat() */

((struct Cat*) pa)->meow();        /* "Downcast" */
30
j_random_hacker

Toutes les bonnes réponses.

J'ajouterais seulement "minimiser la structure des données". Cela pourrait même être plus facile en C, car si C++ est "C avec des classes", OOP essaie de vous encourager à prendre chaque nom/verbe dans votre tête et à le transformer en classe/méthode Cela peut être très coûteux.

Par exemple, supposons que vous ayez un tableau de relevés de température à des moments précis et que vous souhaitiez les afficher sous forme de graphique linéaire dans Windows. Windows a un message Paint, et lorsque vous le recevez, vous pouvez parcourir le tableau en faisant les fonctions LineTo, en mettant à l'échelle les données au fur et à mesure pour les convertir en coordonnées pixel.

Ce que j'ai vu trop souvent, c'est que, comme le graphique est composé de points et de lignes, les gens vont construire une structure de données composée d'objets ponctuels et d'objets linéaires, chacun capable de DrawMyself, puis la rendent persistante, selon la théorie que cela est en quelque sorte "plus efficace", ou qu'ils pourraient, peut-être, devoir être en mesure de passer la souris sur des parties du graphique et d'afficher les données numériquement, alors ils construisent des méthodes dans les objets pour y faire face, et cela, bien sûr, implique de créer et de supprimer encore plus d'objets.

Vous vous retrouvez donc avec une énorme quantité de code qui est tellement lisible et passe simplement 90% de son temps à gérer des objets.

Tout cela se fait au nom de "bonnes pratiques de programmation" et "d'efficacité".

Au moins en C, la manière simple et efficace sera plus évidente et la tentation de construire des pyramides moins forte.

12
Mike Dunlavey

Les normes de codage GN ont évolué au cours des deux dernières décennies. Ce serait une bonne idée de les lire, même si vous ne les suivez pas à la lettre. La réflexion sur les points soulevés en eux vous donne une base plus solide sur la façon de structurer votre propre code.

11
user25148

L'encapsulation est toujours la clé d'un développement réussi, quel que soit le langage de développement.

Une astuce que j'ai utilisée pour aider à encapsuler les méthodes "privées" en C est de ne pas inclure leurs prototypes dans le fichier ".h".

3
Nate

Je vous suggère de vérifier le code de tout projet C open source populaire, comme ... hmm ... Noyau Linux, ou Git; et voyez comment ils l'organisent.

3
Ivan Krechetov

La règle numérique pour les applications complexes: elle doit être facile à lire.

Pour simplifier une application complexe, j'emploie Diviser et conquérir .

3
MrValdez

Si vous savez comment structurer votre code en Java ou C++, alors vous pouvez suivre les mêmes principes avec du code C. La seule différence est que vous n'avez pas le compilateur à vos côtés et vous besoin de tout faire très soigneusement manuellement.

Puisqu'il n'y a pas de packages et de classes, vous devez commencer par concevoir soigneusement vos modules. L'approche la plus courante consiste à créer un dossier source distinct pour chaque module. Vous devez vous fier aux conventions de dénomination pour différencier le code entre les différents modules. Par exemple, préfixez toutes les fonctions avec le nom du module.

Vous ne pouvez pas avoir de classes avec C, mais vous pouvez facilement implémenter des "types de données abstraits". Vous créez un fichier .C et .H pour chaque type de données abstrait. Si vous préférez, vous pouvez avoir deux fichiers d'en-tête, un public et un privé. L'idée est que toutes les structures, constantes et fonctions qui doivent être exportées vont dans le fichier d'en-tête public.

Vos outils sont également très importants. Un outil utile pour C est lint , qui peut vous aider à trouver les mauvaises odeurs dans votre code. Un autre outil que vous pouvez utiliser est Doxygen, qui peut vous aider à générer documentation .

3
kgiannakakis

Je suggère de lire un manuel C/C++ comme première étape. Par exemple, C Primer Plus est une bonne référence. La lecture des exemples vous donnerait une idée de la façon de mapper votre Java OO vers un langage plus procédural comme C.

2
Tosa