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

  1. #1
    Nouveau Candidat au Club
    Structures à utiliser pour un exercice
    Bonjour, je viens ici car j'ai une question et je ne comprends pas bien le fonctionnement.

    J'ai un exercice à réaliser avec une structure
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct SwiStruct {
      int ref;
      int nb_ports;
      int constr;
      int speed;
      float energy;
      float prix;
      int qte;
    };


    et voici les valeurs que je dois définir pour cette structure :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {34215637, 8, 001, 256, 1.8, 123.59, 12},
    {34215641, 64, 001, 512, 4.2, 223.59, 45},
    {34215117, 8, 010, 256, 2.1, 93.59, 116},
    {34215118, 2, 001, 64, 0.8, 23.59, 2},
    {34200007, 24, 001, 256, 1.8, 123.59, 212},
    {34214637, 8, 012, 256, 1.8, 49.99, 342},
    {34200017, 32, 100, 256, 2.58, 183.49, 89} ,
    {34225637, 4, 101, 256, 1, 33.59, 62} ,
    {14215637, 16, 101, 256, 1.4, 89.49, 592}


    Comment faire pour définir ma structure avec toutes ces valeurs ?

    Merci à vous.

  2. #2
    Expert éminent
    Il faut faire 1 tableau

    Code :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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef struct s_SwiStruct {
      int ref;
      int nb_ports;
      int constr;
      int speed;
      float energy;
      float prix;
      int qte;
    } t_SwiStruct;
     
     
    #define IS_EMPTY_ELEMENT(ONE_ELEMENT) ((ONE_ELEMENT->ref == 0) && (ONE_ELEMENT->nb_ports == 0) && \
                              (ONE_ELEMENT->constr == 0) && (ONE_ELEMENT->speed == 0) && \
                              (ONE_ELEMENT->energy == 0.0) && (ONE_ELEMENT->prix == 0.0) && \
                              (ONE_ELEMENT->qte == 0))
     
     
    void display(t_SwiStruct* one_element) {
        if (one_element != NULL) {
            printf("%d, %d, %d, %d, %f, %f, %d\n", one_element->ref, one_element->nb_ports,
                one_element->constr, one_element->speed, one_element->energy,
                one_element->prix, one_element->qte);
        }
    }
     
     
    /*****************************************************************************/
    /***********************************  Main  **********************************/
    /*****************************************************************************/
     
    int main(int argc, char** argv)
    {
        t_SwiStruct list_elements[] = {
            {34215637, 8, 001, 256, 1.8, 123.59, 12},
            {34215641, 64, 001, 512, 4.2, 223.59, 45},
            {34215117, 8, 010, 256, 2.1, 93.59, 116},
            {34215118, 2, 001, 64, 0.8, 23.59, 2},
            {34200007, 24, 001, 256, 1.8, 123.59, 212},
            {34214637, 8, 012, 256, 1.8, 49.99, 342},
            {34200017, 32, 100, 256, 2.58, 183.49, 89} ,
            {34225637, 4, 101, 256, 1, 33.59, 62} ,
            {14215637, 16, 101, 256, 1.4, 89.49, 592},
            {0, 0, 0, 0, 0.0, 0.0, 0} // sentinelle
        };
     
        t_SwiStruct* elt;
     
        elt = list_elements;
     
        while ( !IS_EMPTY_ELEMENT(elt) ) {
            display(elt);
     
            ++elt;
        }
     
     
        return EXIT_SUCCESS;
    }

  3. #3
    Nouveau Candidat au Club
    Merci beaucoup ! Cela marche parfaitement avec ce que j'ai réalisé.

    Un peu bête de ma part de ne pas y avoir pensé...

    Si je peux abuser un petit peu, je dois afficher le tableau trié par ordre croissant selon le 5è champ (energy).
    Même en lisant la doc, j'ai du mal à comprendre son fonctionnement. Serait-il possible de me l'expliquer ?

    Encore merci de votre temps et vous m'ôtez une énorme épine du pied !

  4. #4
    Expert éminent
    Attention à ma solution parce que c'est 1 tableau constant

    Je suis presque sûr qu'on ne peut pas ajouter ou supprimer d'éléments.
    Et ensuite, on peut peut-être modifier les valeurs des éléments

    Pour ton tri, c'est trivial : tu prends n'importe quel tri (tri rapide, tri à bulles, tri sélectif, tri par insertion, ...) mais
    • Les tests vont porter sur le membre enegy if (elt1->energy <= elt2->energy) par exemple
    • lors d'échange de 2 éléments, il faut recopier tous les membres. Donc il faut avoir 1 fonction void swap_2_elements(t_SwiStruct*, t_SwiStruct*).
    • Peut-être même avoir 1 petite bibliothèque pour faire des choses de base : copier, échanger, tester en fonction d'1 membre, ... voire même des structures spécifiques comme 1 tableau, 1 pile par exemple pour ajouter ou supprimer 1 élément, inverser des éléments, partager ou fusionner 2 structures, ...

  5. #5
    Expert confirmé
    Bonjour,

    Si l'exercice consiste à utiliser la fonction qsort(), il faut :
    a) définir une fonction qui indiquera comment trier sur le champ energy qui pourrait s'appeler int compare_energy_of_SwiStruct(const void*, const void*).
    b) appeler la fonction qsort() en lui passant les bons paramètres, et le dernier des 4 est justement la fonction compare_energy_of_SwiStruct.

  6. #6
    Expert éminent sénior
    Bonjour

    Citation Envoyé par foetus Voir le message
    Je suis presque sûr qu'on ne peut pas ajouter ou supprimer d'éléments.
    Exact.

    Citation Envoyé par foetus Voir le message
    Et ensuite, on peut peut-être modifier les valeurs des éléments
    Oui, on peut. Ton tableau c'est exactement comme écrire int tab[]={1, 2, 3}; tab[0]=5.

    Citation Envoyé par foetus Voir le message
    Peut-être même avoir 1 petite bibliothèque pour faire des choses de base : copier, échanger, tester en fonction d'1 membre, ... voire même des structures spécifiques comme 1 tableau, 1 pile par exemple pour ajouter ou supprimer 1 élément, inverser des éléments, partager ou fusionner 2 structures, ...
    Hum... je pense que tu as perdu le PO...
    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

  7. #7
    Nouveau Candidat au Club
    Merci à tous pour vos réponses, ça marche du feu de dieu pour le moment !

    Si j'ai d'autres problèmes concernant la suite de mon exercice, je peux continuer à m'adresser à vous ?

  8. #8
    Expert éminent sénior
    Citation Envoyé par IdiCoda Voir le message
    je peux continuer à m'adresser à vous ?
    Ben... c'est un peu la raison d'être de ce forum. Et si on est là, c'est qu'on a envie d'aider les débutants à progresser en C, qu'on a tous quelque part une envie d'enseigner (certains d'entre nous sont ou ont été enseignants d'ailleurs)...
    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

  9. #9
    Membre averti
    Citation Envoyé par foetus Voir le message

    • lors d'échange de 2 éléments, il faut recopier tous les membres. Donc il faut avoir 1 fonction void swap_2_elements(t_SwiStruct*, t_SwiStruct*).
    en, C, on peut copier le contenu d'une struct vers une autre de même type avec l'opérateur =

    à moins d'avoir à faire des traitements intermédiaires (ce qui n'est pas le cas si la copie est à l'identique), créer une fonction ne me parait pas nécessaire.

  10. #10
    Rédacteur/Modérateur

    Faire un swap c'est assigner b à un temporaire, puis assigner a à b, puis assigner le temporaire à a.
    Créer une fonction est un minimum de bon sens si on fait ça plus de 1 fois. Ou tout simplement pour avoir plus de lisibilité dans le code.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  11. #11
    Membre averti
    Citation Envoyé par Bousk Voir le message
    Faire un swap c'est assigner b à un temporaire, puis assigner a à b, puis assigner le temporaire à a.
    Créer une fonction est un minimum de bon sens si on fait ça plus de 1 fois. Ou tout simplement pour avoir plus de lisibilité dans le code.
    Certes, mais ce n'est pas mon point. Ce que que souligne, c'est que le C permet d'effectuer une affectation d'une struct avec l'opérateur = et qu'il est inutile de recopier tous les membres, car le C s'en charge, alors que le commentaire de foetus pouvait laisser penser le contraire.

    J'ai donc essayé de clarifier ce point.

    Une fonction peut être créée, bien sûr. Elle contiendra 3 instructions.

    Je ne suis pas sûr que le fait qu'elles figurent dans une fonction appelée par une boucle ou dans la boucle qui les utilisera soit une affaire de bon sens dans notre cas de figure, ni une nécessité, mais plutôt de style ou de préférences personnelles et je ne vais pas me battre là dessus.

  12. #12
    Expert éminent
    Citation Envoyé par -Eks- Voir le message
    une affectation d'une struct avec l'opérateur = et qu'il est inutile de recopier tous les membres, car le C s'en charge
    Effectivement ... mais c'est 1 copie binaire

    Donc avec les pointeurs, tu fais 1 "shallow copy" ... et donc potentiellement plantogène si ce n'est pas le comportement souhaité.
    Mais, ici, c'est 1 structure avec des entiers/ flottants ... donc pas trop de problème.


    Citation Envoyé par -Eks- Voir le message
    une affaire de bon sens dans notre cas de figure, ni une nécessité, mais plutôt de style ou de préférences personnelles
    Effectivement

    Cela va dépendre de la relecture de ton code ... comme le dit également @Bousk
    Tu en auras 1 plus "moderne" avec des swap, copy, ... des types Array_SwiStruct (par exemple) ... et donc qui attire l'attention sur le type de données que tu manipules.
    Et 1 autre, 1 code C qu'il faut percuter que ta structure est 1 P.O.D. ("Plain Old Data") qui est manipulée comme 1 entier ou 1 flottant.

  13. #13
    Membre averti
    Peu importe la façon dont le C implémente la copie. C'est juste une copie du contenu de la struct que réalise l'opérateur = pas plus, pas moins

    Citation Envoyé par foetus Voir le message
    Mais, ici, c'est 1 structure avec des entiers/ flottants ... donc pas trop de problème.
    Je serais plus affirmatif : il n'y a, dans le cas présent aucun problème, vu que ce que l'on cherche à copier est juste le contenu de la struct

    Si je vois un code qui s'amuse inutilement à copier chaque membre de la struct à l'identique, cela ne me donne pas une bonne impression pour plusieurs raisons :

    • c'est une ignorance des capacités du langage
    • c'est du code inutile et qu'il faudra modifier demain si les membres de la struct changent, introduisant un risque de copie partielle du contenu si ce n'est pas fait


    Citation Envoyé par foetus Voir le message
    Cela va dépendre de la relecture de ton code ... comme le dit également @Bousk
    Tu en auras 1 plus "moderne" avec des swap, copy, ... des types Array_SwiStruct (par exemple) ... et donc qui attire l'attention sur le type de données que tu manipules.
    Et 1 autre, 1 code C qu'il faut percuter que ta structure est 1 P.O.D. ("Plain Old Data") qui est manipulée comme 1 entier ou 1 flottant.
    Erm... J'ai du mal à saisir dans quel cas une struct en C ne serait pas POD ?

    Personnellement, je ne suis pas pour la définition de "types" comme Array_SwiStruct. Si je peux éviter les typedef je le fais car ils masquent l'information. Le typedef crée un alias sur les types réels, qui ne sont plus visibles en utilisant cet alias.

    C'est même tout à fait mal vu par certains :

    https://www.kernel.org/doc/html/v4.1....html#typedefs

  14. #14
    Expert éminent sénior
    Citation Envoyé par -Eks- Voir le message
    Si je peux éviter les typedef je le fais car ils masquent l'information.... C'est même tout à fait mal vu par certains :https://www.kernel.org/doc/html/v4.1....html#typedefs
    Euh... j'ai du mal avec l'anglais mais il me semble que le premier exemple de cet article met en opposition vps_t a face à struct virtual_container *a. Et donc ce n'est pas la création du type "vps_t" qui est mis au ban, mais le fait que le type créé masque l'étoile !!! Et là je suis en parfait accord avec cet ostracisation. On ne masque jamais un pointeur. De plus il me semble que le suffixe "_t" est réservé aux types "officiels" de la libc (ex size_t) et que pour les types perso, c'est le préfixe "t_" qui doit être utilisé => typedef ... t_vps.

    En revanche je n'aurai rien à redire face à ce code...
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    typedef struct {
    	unsigned short jj, mm, aa
    } t_date;
     
    ...
     
    t_date naissance;

    ... car je ne trouve pas que ce qu'il masque soit vraiment décisif face à cette écriture...
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct s_date {
    	unsigned short jj, mm, aa
    };
     
    ...
     
    struct s_date naissance;

    Ok, dans la seconde écriture on voit que c'est une structure (on nous le jette à la figure à chaque variable créée en fait) et pas dans la première. Mais est-ce vraiment capital ?
    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

  15. #15
    Expert éminent
    Citation Envoyé par -Eks- Voir le message
    Peu importe la façon dont le C implémente la copie
    ...

    Si je vois un code qui s'amuse inutilement à copier chaque membre de la struct à l'identique, cela ne me donne pas une bonne impression pour plusieurs raisons
    Pour moi, il n'y a pas de bonne façon ni de mauvaise

    En quoi faire 1 fonction de copy/ swap/ ... serait mauvais ? Justement cette fonction (qui peut être 1 macro) peut juste faire 1 affectation de structure.

    Mais si tu rajoutes 1 pointeur ou 1 tableau dans ta structure, avec juste 1 affection tu seras quand même obligé de changer ton code, si tu veux faire des recopies ("deep copy")
    D'autant + qu'1 fonction, c'est centralisé en 1 seul endroit au lieu de rechercher toutes les affectations pour modifier le code.

    Mais on a bien compris ta façon de coder en C en mode "old school" et tout ce qui n'est pas nécessaire (typedef, petite bibliothèque pour mimiquer la POO, ...) on ne le fait pas.


    Citation Envoyé par -Eks- Voir le message
    J'ai du mal à saisir dans quel cas une struct en C ne serait pas POD ?
    Justement je disais qu'1 structure est 1 POD, et donc que l'affectation fonctionne (mais c'est 1 copie binaire que justement je disais il faut connaître la subtilité)


    Citation Envoyé par Sve@r Voir le message
    Euh... j'ai du mal avec l'anglais mais il me semble que le premier exemple de cet article met en opposition vps_t a face à struct virtual_container *a.
    Le lien justement ne veut pas qu'on utilise typedef comme 1 sorte de type qu'on peut changer en cours de développement de façon + ou - transparente.

    Par exemple passer d'1 type typedef int My_Flag à typedef long My_Flag.
    C'est pour moi, 1 approche "old school" : chaque modification ne doit pas être transparente, et chaque modification dans 1 endroit de ton code doit être analysée pour éviter d'en louper 1 et de créer 1 bogue potentiel.

  16. #16
    Membre averti
    @foetus: cela m'a pris du temps de devenir "oldschool" comme tu dis ... je me suis beaucoup fourvoyé avec les typedef à mes débuts et l'encapsulation à outrance ;-)

    Sur les changements "transparents" de types, l'un des cas où ce document reconnaît l'intérêt et la nécessité des typedef est justement lorsque l'on crée un type volontairement opaque. On doit avoir une bonne raison pour cela : l'utilisateur ne doit pas savoir ce qu'il y a sous le capot car les détails de l'implémentation peuvent changer. On lui demande alors de se reposer sur une interface qui permet à son code de fonctionner exactement de la même manière quelques soient les changements "internes" faits (dans la libc, les exemples typiques sont FILE ou time_t).

    @Sve@r: c'est en anglais, mais il faut lire en entier et ne pas s'arrêter au premier exemple...

    In general, a pointer, or a struct that has elements that can reasonably be directly accessed should never be a typedef
    Vas discuter de ton exemple avec Linus ;-)

    Les exceptions sont listées de a) à e) et certaines ne concernent que le contexte du développement du noyau.

    si tu écris :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    struct date d;

    Tu sais que d est de type struct date et tu n'as pas à te sentir obligé de préfixer l'étiquette d'un s_, car il n'y a pas d'équivoque pour le compilateur que "date" est l'étiquette d'une struct, ni pour le lecteur. En rajoutant un préfixe s_ tu introduis une répétition d'information redondante.

    Si tu décides de créer un typedef, tu vas te sentir obligé de mettre un t_ devant ton nom de type (et un s_ devant le nom de l'étiquette de la struct comme tu le fais, histoire de marquer le fait que ce sont deux identifiants qualifiant des choses différentes). Puis, sauf à masquer cette information, pour savoir qu'il s'agit d'une struct en lisant le nom du type, tu rajouteras éventuellement cette information dans le nom du type, comme l'infortuné nom de type qu'on impose au PO SwiStruct, ou le t_SwiStruct qui a été cité dans ce fil.

    je préfère écrire, dans ces conditions :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    struct date d[9];

    au lieu de :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    t_date_struct d[9];

    ou de :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    t_array_date_struct d;


    après, il faut dédramatiser, on parle de goûts et de couleurs :-)

  17. #17
    Expert éminent sénior
    Citation Envoyé par -Eks- Voir le message
    c'est en anglais, mais il faut lire en entier et ne pas s'arrêter au premier exemple...
    Ouais j'avoue je devrais faire un effort de ce côté. D'autant plus qu'il y des traducteurs en ligne assez efficaces

    Citation Envoyé par -Eks- Voir le message
    Vas discuter de ton exemple avec Linus ;-)
    Oui, je l'ai fait et il est tombé d'accord avec moi. Il a dit "t'as raison, ce que tu as fait ca roxxe grave"

    Citation Envoyé par -Eks- Voir le message
    si tu écris :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    struct date d;

    Tu sais que d est de type struct date et tu n'as pas à te sentir obligé de préfixer l'étiquette d'un s_, car il n'y a pas d'équivoque pour le compilateur que "date" est l'étiquette d'une struct, ni pour le lecteur. En rajoutant un préfixe s_ tu introduis une répétition d'information redondante.
    Exact. Là effectivement c'est une bêtise de ma part. Le struct date suffit parfaitement.

    Citation Envoyé par -Eks- Voir le message
    je préfère écrire, dans ces conditions :

    Ah l'horreur !!! Aurais-tu masqué un tableau derrière un typedef ???

    En fait, j'ai l'impression de ce dont on parle c'est la radicalisation d'un outil, et qu'on peut alors retrouver dans tous les outils (écrire tab[i++]=0 ou tab[i]=0; i++ ou encore memset(tab, 0, n*sizeof(*tab))). Est-ce l'outil qui est intrinsèquement mauvais ou bien juste la façon de s'en servir ? Un outil c'est bien quand on s'en sert correctement. J'ai déjà vu des cas où le goto se justifiait parfaitement. Ok il pouvait être supprimé mais rendait alors le code plus complexe avec des répétitions inutiles. Il reste à définir ce qu'on entend par "correctement" et ça c'est la partie la plus difficile du travail (goûts et couleurs comme tu le dis). Alors pour paraphraser je sais plus trop qui, je sais pas définir quand c'est correct mais je sais reconnaitre quand ça dépasse les limites, et cacher un tableau (ou un pointeur) dans un typedef c'est pour moi clairement une des choses à ne pas faire
    Bien entendu, tout cela dit avec le respect d'un developpeur a un autre
    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

  18. #18
    Membre averti
    Bonjour,

    Citation Envoyé par -Eks- Voir le message

    C'est même tout à fait mal vu par certains :

    https://www.kernel.org/doc/html/v4.1....html#typedefs
    C'est une convention de codage parmi tant d'autres. En dehors des personnes impliquées dans le code du noyau, elle n'a aucune valeur autoritative.
    Mais de là à justifier des alias de types qui finissent en notation hongroise, c’est quand même une autre paire de manches.

  19. #19
    Modérateur

    Je hais la notation hongroise

    Un IDE moderne te met de l'auto-complétion, te montre l'aide d'un type quand tu passes ton curseur dessus, te permet de faire un CRTL+Clic sur un type ou une variable pour voir comme la déclaration / définition. J'ai même vu des IDE transformer un . en -> quand tu actives l'auto-complétion car il sait que c'est un pointeur.

    En C++, quand tu fais struct Date {}, ça fait implicitement un typedef. Date date; et le tour est joué. Continuer en C à se promener avec struct date_t date;, c'est faire de la notation hongroise

    Et puis, en toute honnêteté, si dans une fonction vous n'êtes pas capable de savoir de quel type est la variable x, est-ce que ce n'est pas que la fonction est beaucoup trop longue et a dépassé la taille critique au-delà laquelle il devient difficile de comprendre une fonction dans son ensemble ?

  20. #20
    Nouveau Candidat au Club
    Rebonjour !

    Pour la suite de l'exercice, j'ai besoin d'ajouter la possibilité à l'utilisateur de soumettre sa propre commande, avec trois paramètres obligatoires : le nombre de ports, le débit et le prix. Le constructeur et l'énergie sont facultatifs, il peut les soumettre mais cela ne crée pas d'erreur si ce n'est pas le cas.

    Si le nombre de paramètres est soit 3, 4 ou 5, la commande est valide; sinon un message d'erreur est renvoyé.

    À la base, je pensais faire un scanf() puis une condition sur celui-ci mais cela me semble bien trop restrictif et je ne vois pas comment faire pour tester le nombre paramètres avec cette méthode.

    Peut-être avec un case ?

    Auriez-vous des idées ?

###raw>template_hook.ano_emploi###