Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse

NodeBB

  1. Home
  2. Programmation
  3. Développement de logiciels
  4. C
  5. [Cours complémentaires] Les bonnes pratiques en C

[Cours complémentaires] Les bonnes pratiques en C

Scheduled Pinned Locked Moved C
8 Posts 4 Posters 3.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • AlexMogA
    AlexMogA
    AlexMog
    Modérateur spécialisé
    wrote on last edited by
    #1

    <p>Bonjour à tous,</p>
    <p>Dans ce cours un peu spécial, nous allons voir ensemble les bonnes pratiques à prendre en C, nous allons aussi voir dans quels cas certaines choses sont plus optimisées que d'autres.</p>
    <p> </p>
    <p>Nous allons donc voir d'une manière générale la propreté du code, et l'optimisation de celui-ci en passant par des exemples d'erreures que tout le monde fait.</p>
    <p> </p>
    <p><strong>I- Propreté du code.</strong></p>
    <p>Pour un code lisible en C, il est impératif de bien construire son code. Les fichiers .h (headers) sont là pour les prototypes des fonctions, ainsi, le .c ne contiendra QUE le code des dites fonctions (et non pas des prototypes ou autres structures qui trainent dans des .c... BUARK!).</p>
    <p>DECOUPEZ VOTRE CODE! Plus votre code sera découpé, et mieu il sera, plus vous ferez en sorte d'avoir un certain nombre de fonctions (en évitant les fonctions "poubelles" quand même...), plus votre code sera lisible et propre! Prensez donc bien à découper votre code!</p>
    <p>Evtez les fonctions de 300 lignes! (ce qui rejoint le paragraphe précédent), effectivement, vous vous en sortez mieu en découpant le tout 🙂 .</p>
    <p>Essayez de respecter la taille d'un terminal (hé oui, les programmeurs Linux programment souvent sur le terminal, il faut penser à eux!) qui est de 80 caractères. Ainsi, évitez de dépasser les 80 caractères par lignes de code 😉 .</p>
    <p>Les includes se font EN HAUT du code, je ne veux plus jamais voir des includes en plein milieu d'un code source... BRRRRR!</p>
    <p>Vous pouvez, en plus, respecter un certain format d'include, certains trient par ordre alphabétique, d'autres par longueur, le but étant de mettre tout de même les includes System AVANT les includes perso.</p>
    <p>ex:</p>
    <pre class="ipsCode prettyprint">
    // Ca c'est bon
    #include <stdio.h>
    #include "mon_include.h"

    // CA C'EST BOF!
    #include "mon_include.h"
    #include <stdio.h>
    </pre>
    <p>Evitez les includes inutiles! Cela ralentiera votre compilation si vous gardez des includes qui n'ont rien à faire là!</p>
    <p>Pensez à sauter des lignes, éviter le multi-exécution sur une ligne... c'est pas très propre.</p>
    <p> </p>
    <p>Bref, rendez votre code LISIBLE! Un code source, c'est comme un livre, si tout est en bordel, on a pas envie de le lire 🙂 .</p>
    <p> </p>
    <p><strong>II- Optimisations et bonnes pratiques!</strong></p>
    <p>Passons à la partie la plus intéressente! L'optimisation et les bonnes pratiques. D'une manière générale les deux sont proches.</p>
    <p> </p>
    <p>Au niveau des optimisations, nous allons d'abord commencer par un cas très spécial:</p>
    <p>Doit-on passer par copie ou pas? (ceci ne s'applique pas aux typpages de base, mais plutôt aux tableaux et aux structures).</p>
    <p>La réponse est simple: TOUT DEPENDS DE VOTRE CODE!</p>
    <p>Si vous souhaitez utiliser la variable que vous passez dans une fonction de manière locale à ladite fonction, passez la par copie.</p>
    <p>Dans le cas contraire, si vous voulez modifier une structure ou les cases d'un tableau par exemple, NE LA PASSEZ PAS PAR COPIE! Laissez votre stack tranquile non de dieu!</p>
    <p>Pour bien vous faire différencier ce qui est une copie et ce qui ne l'est pas, vouci des exemples concrets.</p>
    <pre class="ipsCode prettyprint">
    // Ceci est une copie (buark)
    void mafonction(int tab[10]);

    // Ceci n'est pas une copie
    void mafonction(int *tab);
    void mafonction(int tab[]);

    // Ceci est une copie
    void mafonction(struct mastruct s);

    //Ceci n'est pas une copie
    void mafonction(struct mastruct *s);
    </pre>
    <p>Nous avons aussi le cas de l'allocation, quand faut-il allouer de la mémoire? Déjà, il faut savoir que l'allocation dynamique est la chose la plus LOURDE dans le monde de la programmation.</p>
    <p>Il est donc conseillé d'allouer tout l'espace naisséssaire au chargement de son programme, pour éviter d'allouer en plein milieu.</p>
    <p>Si les allocations sont rares, on peut tout de même les utiliser de manière dynamique. Mais il ne faut surtout pas en abuser! (c'est pour cela qu'un tableau à une dimension (au niveau de l'alloc) sera moins lourd qu'un tableau à double dimensions).</p>
    <p> </p>
    <p>Une autre pratique qui est plutôt bonne à connaitre pour l'optimisation: les calculs à base d'INT.</p>
    <p>Pourquoi utiliser le typpage int plutôt que float? Il faut savoir que les int sont stockés sur un seul registre, alors qu'un float (chiffre à virgule) est stocké sur 4 registres (merci l'ASM de nous le démontrer), il est donc conseillé d'utiliser des int plutôt que des float pour optimiser les calculs. N'oubliez pas que le calcul sur 4 registres sera TOUJOURS plus long que le calcul sur 1 registre (4x plus long), et cela, peut importe la valeur stockée dans les registres.</p>
    <p> </p>
    <p>Enfin, la meilleure pratique pour bien optimiser son programme, c'est savoir de quoi est composé le dit programme!</p>
    <p>Je m'explique, utiliser des fonctions préfaites par la LibC, c'est bien, mais quand on ne sait pas comment elles fonctionnent, c'est MAL!</p>
    <p>En effet, vous êtes plusieurs à ne pas savoir que dans certains cas, printf n'est pas du tout optimisé (en effet, printf passe par 3 boucles ET une bufferisation des données AVANT affichage, alors qu'un simple puts ou write aurait fait l'affaire!), je vous conseille donc de vous renseigner sur les fonctions que vous utilisez, ou encore mieu! RECODEZ LES! Vous apprendrez beaucoup plus ainsi que de n'importe qu'elle autre manière!</p>
    <p>Je vous invites à lire mes cours dans la section C, qui utilisent le principe de l'utilisation de Fonctions System au lieu de passer par des fonctions de la LibC, pour vous apprendre à bien comprendre la différence entre les deux, et avoir de bonnes habitudes (qui sait, vous comprendrez peut être comment ça marche 🙂 )</p>
    <p> </p>
    <p>Si vous désirez d'autres informations, n'hésitez pas à laisser un commentaire ci-dessous!</p>
    <p> </p>
    <p>A très bientôt pour un prochain cours!</p>
    <p>AlexMog.</p>

    Multiplayer GameDev @ Unexpected
    

    Mon CV

    1 Reply Last reply
    3
    • SoulalexS
      SoulalexS
      Soulalex
      wrote on last edited by
      #2

      <p>Au moins c'est net et clair 🙂 Merci pour ce cours très bien construit et très instructif.</p>

      Soulalex, Administrateur de Melinyel+ E-Mail : [email protected]+ GitHub : https://github.com/Soualex

      1 Reply Last reply
      0
      • AlemortA
        AlemortA
        Alemort
        wrote on last edited by
        #3

        <div>Merci 🙂 .</div>
        <div>Cependant pourrais tu entrer plus dans les détails sur plusieurs points?</div>
        <div> </div>
        <div>-Les registres sont contenus dans la mémoire la plus rapide de l'ordinateur, non? Étant donné que le type "char" est stocké sur moins d'octets, non par rapport à un type "int" (sous Windows: int==> 4 octets // char ==> 1 octet), un "char" est aussi contenu dans 1 registre?</div>
        <div> </div>
        <div>-Maintenant en rapport avec les tableaux, quel sont les risques de le passer par copie lorsque l'on compte modifier ses valeurs? tu parles souvent de stack, tu fais référence à Piles? Pour aller plus loin toutes les structures de données sont modifiés sur le principe des piles?</div>
        <div> </div>
        <div>-Enfin par rapport aux fonctions, pour les recoder, il faut se diriger vers quel type de fonctions? Avant faut bien sur aller voir comment elles sont codés, non? Elles sont situées dans les fichiers d'Ide "code-blocks"?</div>

        1 Reply Last reply
        0
        • AlexMogA
          AlexMogA
          AlexMog
          Modérateur spécialisé
          wrote on last edited by
          #4

          <blockquote class="ipsQuote" data-cite="Alemort" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8597" data-ipsquote-contentid="808" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1402596343" data-ipsquote-username="Alemort"><div>
          <p>Merci 🙂 .</p><p>
          Cependant pourrais tu entrer plus dans les détails sur plusieurs points?</p><p>
           </p><p>
          -Les registres sont contenus dans la mémoire la plus rapide de l'ordinateur, non? Étant donné que le type "char" est stocké sur moins d'octets, non par rapport à un type "int" (sous Windows: int==> 4 octets // char ==> 1 octet), un "char" est aussi contenu dans 1 registre?</p>
          </div></blockquote>
          <p>Les registres sont contenus dans la mémoire du processeur (sisi, vous savez, la mémoire cache 😉 ), on parle de registres en ASM uniquement. Ils ne sont pas accessibles en C, c'est la base des calculs du processeur.</p><p>
           </p>
          <blockquote class="ipsQuote" data-cite="Alemort" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8597" data-ipsquote-contentid="808" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1402596343" data-ipsquote-username="Alemort"><div>
          <p>-Maintenant en rapport avec les tableaux, quel sont les risques de le passer par copie lorsque l'on compte modifier ses valeurs? tu parles souvent de stack, tu fais référence à Piles? Pour aller plus loin toutes les structures de données sont modifiés sur le principe des piles?</p>
          </div></blockquote>
          <p>Je l'avais déjà expliqué plus tôt (voir mon cours sur les stacks), lorsque tu lance une fonction, celle-ci sauvegarde l'ancienne stack (et la récupère à la fin de la fonction), du coup, tu as un push sur la stack de l'ancienne stack, si tu y ajoute ce que tu as passé par copie, tu repush ce qui a déjà été push en stack. POur les structures, tout est dans mon cours.</p>
          <blockquote class="ipsQuote" data-cite="Alemort" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8597" data-ipsquote-contentid="808" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1402596343" data-ipsquote-username="Alemort"><div>
          <p>-Enfin par rapport aux fonctions, pour les recoder, il faut se diriger vers quel type de fonctions? Avant faut bien sur aller voir comment elles sont codés, non? Elles sont situées dans les fichiers d'Ide "code-blocks"?</p>
          </div></blockquote>
          <p>Alors non, pas du tout, et c'est là que linux est concret pour apprendre à programmer!</p>
          <p>Le principe de l'openSource de linux te permet de savoir beaucoup de choses sur les fonctions que tu utilises, mais là n'est pas la question.</p>
          <p>Pour recoder une fonction de la libC, il te faut uniquement les fonctions qu'à utilisée la libC pour être crée: les fonctions system. Une fois que tu les as, surtout, ne regarde pas les sources existantes! Ca ne sert à rien, autant essayer de réfléchir par toi même pour comprendre comment ça marche! Une fois que tu as tenté de recoder toi même, vas voir les sources, et pleure un bon coup, en voyant que printf, c'est assez lourd quand même 🙂 .</p>
          <p>Code blocks ne t'aidera pas, car les libs ne sont pas open-source sur windows (en tout cas, les libC)</p>

          Multiplayer GameDev @ Unexpected
          

          Mon CV

          1 Reply Last reply
          2
          • D
            D
            davydavek
            wrote on last edited by
            #5

            <blockquote class="ipsQuote" data-ipsquote=""><div>"<span style="color:rgb(40,40,40);font-family:Helvetica, helvetica, arial, sans-serif;">les libs ne sont pas open-source sur windows (en tout cas, les libC)"</span></div></blockquote>
            <p><span>La libc et la STL sont open source sous Windows. Si tu as Visual Studio d’installé, les sources sont dans le dossier "\Microsoft VisualStudio XX.x\VC\crt\src" et les headers : "</span><span style="font-size:14px;color:rgb(40,40,40);font-family:Helvetica, helvetica, arial, sans-serif;background-color:rgb(255,255,255);">Microsoft VisualStudio XX.x\include".</span></p>
            <p> </p>
            <blockquote class="ipsQuote" data-ipsquote=""><div>"<span style="color:rgb(40,40,40);font-family:Helvetica, helvetica, arial, sans-serif;">lorsque tu lance une fonction, celle-ci sauvegarde l'ancienne stack (et la récupère à la fin de la fonction)"</span><span style="font-size:14px;background-color:rgb(255,255,255);"> </span><span style="font-size:14px;background-color:rgb(255,255,255);"> </span></div></blockquote>
            <p> </p>
            <p>On dirait que tu confond avec les stackframes, il n'y a qu'une seule stack par thread, et sur cette stack il y a les stackframes de chaque fonction en cours d’exécution. </p>

            C# dev
            github.com/DavyWk

            1 Reply Last reply
            0
            • AlexMogA
              AlexMogA
              AlexMog
              Modérateur spécialisé
              wrote on last edited by
              #6

              <blockquote class="ipsQuote" data-cite="davydavek" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8602" data-ipsquote-contentid="808" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1402619671" data-ipsquote-username="davydavek"><div>
              <div>
              <p><span>La libc et la STL sont open source sous Windows. Si tu as Visual Studio d’installé, les sources sont dans le dossier "\Microsoft VisualStudio XX.x\VC\crt\src" et les headers : "</span><span style="font-size:14px;color:rgb(40,40,40);font-family:Helvetica, helvetica, arial, sans-serif;background-color:rgb(255,255,255);">Microsoft VisualStudio XX.x\include".</span></p>
              <p></p><p>On dirait que tu confond avec les stackframes, il n'y a qu'une seule stack par thread, et sur cette stack il y a les stackframes de chaque fonction en cours d’exécution. </p>
              </div>
              </div></blockquote>
              <p>Autant pour moi pour les sources windows, et merci pour l'info 🙂 </p>
              <p> </p>
              <p>Pour le stackframes, sache que la stack est sauvegardée AVANT d'initialiser le stackframe d'une fonciton. (on le voit dans le code ASM des fonctions en C), voici un code ASM d'exemple:</p>
              <pre class="ipsCode prettyprint">
              ;; exemple avec la fonction "hello world" x86 asm
              global main
              extern printf

              section .text
              main:
              push rbp ;; Ici, le generic push
              mov rbp, rsp ;; Suite du generic push (on le retrouvera dans toutes les fonctions en C (sans exceptions, il est ajouté automatiquement par le compilateur)

              mov rdi, FormatStr ;; 1er param de printf
              call printf ;; call de printf

              mov rsp, rbp ;; on remet l'ancienne stack en place
              pop rbp ;; on vire la sauvegarde de l'ancienne stack

              mov rax, 60 ;; on prépare l'appel au systcall "exit"
              xor rdi, rdi ;; on passe le paramètre 0 dans rdi (xor x, x revient à mettre 0 dans x (car un xor de 2 même valeurs donne 0)
              syscall ;; appel du systcall n°60
              ret

              ;; section read only
              section .rodata
              FormatStr db 'Hello World !',0Ah,0
              </pre>
              <p>(c'est ce que j'appelle dans mon code le "generic push", on voit très bien que l'ancienne stack est sauvegardée dans la mémoire exécutive)</p>

              Multiplayer GameDev @ Unexpected
              

              Mon CV

              1 Reply Last reply
              0
              • D
                D
                davydavek
                wrote on last edited by
                #7

                <blockquote class="ipsQuote" data-ipsquote=""><div>"<span style="color:rgb(40,40,40);font-family:Helvetica, helvetica, arial, sans-serif;">Pour le stackframes, sache que la stack est sauvegardée AVANT d'initialiser le stackframe d'une fonciton. (on le voit dans le code ASM des fonctions en C)"</span>
                <p> </p>
                </div></blockquote>
                <p><span> C'est l'addresse de l'ancienne stackframe qui est sauvegardée, ce qui crée une linked list. (chaque base pointer (rbp/ebp) pointe vers celui de la stackframe précédente)</span></p>
                <pre class="ipsCode prettyprint">
                ;; exemple avec la fonction "hello world" x86 asm
                global main
                extern printf

                section .text
                main:
                push rbp ;; Sauvegarde de l'ancien base pointer
                mov rbp, rsp ;; Change le base pointer vers la stackframe de main

                mov rdi, FormatStr ;; 1er param de printf
                call printf ;; call de printf
                ;; printf est en cdecl donc faudrait enlever l'argument de la stack ici
                ;; add rsp, 4/8 (32 ou 64 bit) vu que tu as passer un pointeur

                mov rsp, rbp ;; Remet le stack pointer de la fonction précédente
                pop rbp ;; Remet le base pointer de la fonction précédente

                mov rax, 60
                xor rdi, rdi
                syscall
                ret

                ;; section read only
                section .rodata
                FormatStr db 'Hello World !',0Ah,0
                </pre>

                C# dev
                github.com/DavyWk

                1 Reply Last reply
                0
                • AlexMogA
                  AlexMogA
                  AlexMog
                  Modérateur spécialisé
                  wrote on last edited by
                  #8

                  <blockquote class="ipsQuote" data-cite="davydavek" data-ipsquote="" data-ipsquote-contentclass="forums_Topic" data-ipsquote-contentcommentid="8606" data-ipsquote-contentid="808" data-ipsquote-contenttype="forums" data-ipsquote-timestamp="1402667040" data-ipsquote-username="davydavek"><div>
                  <div>
                  <p> </p>
                  <p><span> C'est l'addresse de l'ancienne stackframe qui est sauvegardée, ce qui crée une linked list. (chaque base pointer (rbp/ebp) pointe vers celui de la stackframe précédente)</span></p>
                  <pre class="ipsCode prettyprint">
                  ;; exemple avec la fonction "hello world" x86 asm
                  global main
                  extern printf

                  section .text
                  main:
                  push rbp ;; Sauvegarde de l'ancien base pointer
                  mov rbp, rsp ;; Change le base pointer vers la stackframe de main

                  mov rdi, FormatStr ;; 1er param de printf
                  call printf ;; call de printf
                  ;; printf est en cdecl donc faudrait enlever l'argument de la stack ici
                  ;; add rsp, 4/8 (32 ou 64 bit) vu que tu as passer un pointeur

                  mov rsp, rbp ;; Remet le stack pointer de la fonction précédente
                  pop rbp ;; Remet le base pointer de la fonction précédente

                  mov rax, 60
                  xor rdi, rdi
                  syscall
                  ret

                  ;; section read only
                  section .rodata
                  FormatStr db 'Hello World !',0Ah,0
                  </pre>
                  </div>
                  </div></blockquote>
                  <p>Autant pour moi, je me suis mal exprimé.</p>
                  <p>Par contre, ca ne change pas le problème de duplication des données.</p>

                  Multiplayer GameDev @ Unexpected
                  

                  Mon CV

                  1 Reply Last reply
                  1

                  Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                  Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                  With your input, this post could be even better 💗

                  Register Login
                  Reply
                  • Reply as topic
                  Log in to reply
                  • Oldest to Newest
                  • Newest to Oldest
                  • Most Votes


                  • Login

                  • Login or register to search.
                  Powered by NodeBB Contributors
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • World
                  • Users
                  • Groups