Partage
  • Partager sur Facebook
  • Partager sur Twitter

Faire confiance à un type

Les rues ne sont plus sûres ^^

Sujet résolu
    16 septembre 2013 à 11:39:20

    Bonjour,

    Il y a longtemps de cela, sur un code que je proposais sur ce site, on m'a fait une remarque sur un fwrite :

    "Fais gaffe quand-même parce qu'un INT ne fera pas forcément 4 octets sur toutes les bécanes."

    Je me souvient très bien avoir fait une recherche dans la foulée, et effectivement, il apparait que le INT est le plus souvent composé d'un nombre d'octets "naturel" pour l'UC, donc c'est selon, Ok, d'accord. Et encore, je pense que l'OS vient jouer les trouble-fête parce que sur un i5 (64 bits "naturel", donc) et un xp pro 32, mon int fait quand même que 4 octets...

    Bref, sur ce, je continue ma petite vie en restant convaincu qu'un char est sur un octet, qu'un short sur 2, un long 4 et un long long 8. Ce sont donc les types que j'utilise lorsque j'ai besoin d'être certain d’accéder à X octets, dans un fichier par exemple.

    Et là, (tardivement, peut-être) je me rend compte qu'en fait ils ne feraient pas X Octets mais AU MOINS, donc un char AU MOINS 1 octet, un short AU MOINS 2 octets etc, donc seule la taille minimale serait garantie...

    Donc, les vrais seuls types auxquels on peux faire confiance au final, ce seraient les uint8_t, sint8_t, etc, en passant par 16,32 et 64 ?

    Surtout qu'ici, page 34, ils définissent INT_MAX à 32767 et UINT_MAX à 65535... ça sent le 16 bits un peu quand-même... ils sont pas un peu à la ramasse ?

    Du coup, en fait, en vrai, c'est le bordel total et on ne peut se fier à rien ? Chacun fait ce qu'il veut dans coin ? Si dans dix ans un type décide que le char fait désormais 2 octets, tous ceux qui scannent leurs fichiers au *char au lieu d'un *uint8_t vont se retrouver marron ?

    Quelqu'un saurait-il m'apporter quelques précisions là dessus ? Ok, il y a la norme, mais je ne pige pas tout quand-même...

    -
    Edité par drx 16 septembre 2013 à 11:47:45

    • Partager sur Facebook
    • Partager sur Twitter

    Bonhomme !! | Jeu de plateforme : Prototype.

      16 septembre 2013 à 11:57:07

      drx a écrit:

      Et là, (tardivement, peut-être) je me rend compte qu'en fait ils ne feraient pas X Octets mais AU MOINS, donc un char AU MOINS 1 octet, un short AU MOINS 2 octets etc, donc seule la taille minimale serait garantie...

      Donc, les vrais seuls types auxquels on peux faire confiance au final, ce seraient les uint8_t, sint8_t, etc, en passant par 16,32 et 64 ?

      Surtout qu'ici, page 34, ils définissent INT_MAX à 32767 et UINT_MAX à 65535... ça sent le 16 bits un peu quand-même... ils sont pas un peu à la ramasse ?

      Je pense que la raison pour laquelle il est écrit que INT_MAX = 32767 c'est justement parce que, comme tu l'as dis, ils font AU MOINS... par exemple sur un processeur 16 bits, sizeof(int) = 2.

      drx a écrit:

      Du coup, en fait, en vrai, c'est le bordel total et on ne peut se fier à rien ? Chacun fait ce qu'il veut dans coin ? Si dans dix ans un type décide que le char fait désormais 2 octets, tous ceux qui scannent leurs fichiers au *char au lieu d'un *uint8_t vont se retrouver marron ?

      Si tu fais des programmes pour PC, tu n'as pas à t'en soucier... ton programme ne se lancera même pas sur un processeur 16 bits. Si tu programmes des micro-contrôleurs en revanche, je pense en effet qu'il faudra que tu t'adaptes...

      Et passer le char de 1 à 2 octets, je ne pense pas que ça se fasse comme ça du jour au lendemain, juste parce que "un type décide que le char fait désormais 2 octets:-°. Ne t'en fais pas !

      -
      Edité par GuiTeK 16 septembre 2013 à 12:17:53

      • Partager sur Facebook
      • Partager sur Twitter
        16 septembre 2013 à 13:45:58

        Lu'!

        Dans ton programme, et plus particulièrement dans ta partie traitement, à moins que tu saches que c'est destiné a du matériel particulier, il faut simplement que tu t'arranges pour que ça tienne dans les types généraux. Donc tu t'assures que tu ne vas pas au-delà du "au moins" et tout va bien.

        Pour ce qui est de la lecture dans les fichiers/réseau/whatever, tu utilises les types (u|s)(type)_(size)t qui sont fixés et ne peuvent pas changer d'une archi à l'autre. Par ailleurs, pour ce genre de cas, il faut aussi gérer toutes les jolies histoires d'indianisme (sur les bits, sur les octets), etc ... et donc de toute façon, tu as déjà tout plein de problèmes en plus de la taille des types.

        EDIT : Ah, et à moins que tu sois certain que dans tes architectures cibles il y ait du 16 bits, ne te casse pas le cul avec leur support, plus vite elles seront remisées, mieux le monde se portera.

        -
        Edité par Ksass`Peuk 16 septembre 2013 à 13:53:31

        • Partager sur Facebook
        • Partager sur Twitter

        Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

          16 septembre 2013 à 13:55:02

          En effet, si tu veux lire/ecrire des fichiers binaires, il faudra faire attention.

          Si tu veux que ton programme soit absolument portable, il faudra aussi faire attention.

          Mais dans beaucoup de programmes de tous les jours, tu n'auras pas de soucis.

          • Partager sur Facebook
          • Partager sur Twitter

          Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

            16 septembre 2013 à 16:17:16

            drx a écrit:

            Bref, sur ce, je continue ma petite vie en restant convaincu qu'un char est sur un octet, qu'un short sur 2, un long 4 et un long long 8. Ce sont donc les types que j'utilise lorsque j'ai besoin d'être certain d’accéder à X octets, dans un fichier par exemple.

            Du coup, en fait, en vrai, c'est le bordel total et on ne peut se fier à rien ? Chacun fait ce qu'il veut dans coin ? Si dans dix ans un type décide que le char fait désormais 2 octets, tous ceux qui scannent leurs fichiers au *char au lieu d'un *uint8_t vont se retrouver marron ?

            Attention, un long c'est 8 octets sur un système PC contemporain. 4 octets, c'était le cas sur les systèmes 32 bits. Ca reste encore bien utilisé, mais il faut avoir conscience que c'est dépassé.
            Encore plus dépassé, Windows 3 avait des int de 2 octets.

            Chacun fait presque ce qu'il veut dans son coin. L'idée de la norme, c'est que le C puisse être porté sur un système avec des bytes de 9 bits et des mots de 36 bits, tout en respectant la norme.

            Il peut aussi exister des processeurs spécialisés qui n'ont qu'une seule taille de donnée: 32 bits. Dessus, un char fait 32 bits. Et voilà. De toute façon de tels systèmes ne sont pas destinés à recevoir des programmes généralistes, donc on s'en soucie peu, et on code pour des systèmes avec des char de 8 bits avec représentation signée en complément à deux.

            Entre l'endianess, les char signés ou pas, la taille des int, des long et des pointeurs, il y a déjà de quoi donner à réfléchir pour la portabilité.

            -
            Edité par Marc Mongenet 16 septembre 2013 à 16:19:29

            • Partager sur Facebook
            • Partager sur Twitter
              16 septembre 2013 à 16:43:22

              Marc Mongenet > "Attention, un long c'est 8 octets sur un système PC contemporain"


              Hélas, ce n'est pas aussi simple. Ces couillons n'ont pas réussi à se mettre d'accord, ce qui fait qu'il existe plusieurs normes 64 bits :

              http://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models

              Si sous Linux, un long est 8 octets, sous Windows, c'est resté 4 octets. (Il existe d'autres types 64 bits pour Windows). 

              Et il existe d'autres modèles exotiques...

              • Partager sur Facebook
              • Partager sur Twitter

              Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                16 septembre 2013 à 16:58:03

                Fvirtman a écrit:

                Si sous Linux, un long est 8 octets, sous Windows, c'est resté 4 octets. (Il existe d'autres types 64 bits pour Windows). 

                Juste, désolé pour l'oubli.

                • Partager sur Facebook
                • Partager sur Twitter
                  17 septembre 2013 à 3:45:14

                  Bonjour,

                  Effectivement, dans un sens, je ne cherche pas non-plus à voir mes programmes tourner sur des 68000, mais sans aller chercher aussi loin, et comme le remarquent Marc Mongenet et Fvirtman, il n'y a pas besoin d'aller bien loin pour avoir des soucis. Si le long fait 8 Octets sous Linux, alors mes accès de fichiers vont fatalement foirer sur cette plateforme.

                  Je ne suis pas un ardent défenseur du "tout portable", parce que je mes programmes restent destinés à du matos contemporain (et futur autant que possible), mais il y a minimum quand-même, et d'autres OS que Win32/64 en font partie.

                  Je vais donc m'empresser de faire un tour dans ma lib pour corriger tout ça.

                  Cependant, en continuant mes recherches, je n'arrive pas à trouver de liste exhaustive des type "garantis", je suppose que les (s|u)int(taille)_t en font partie, mais qu'en est-il des autres comme size_t par exemple ?

                  Parce que j'ai tendance à pas mal utiliser ce type, outre sa taille, c'est le genre de type qui sert également de commentaire dans la mesure où je l'utilise fréquemment pour stocker les volumes.

                  J'en conclu également que sur un changement de plateforme, il convient de re-compiler non-seulement le programme, mais également les utilitaires pour reformater les fichiers utiles à l'application... Du coup le transfert de fichiers d'une plateforme à l'autre devient improbable...

                  Et enfin, une dernière question :

                  Y a-t-il un moyen de conditionner le type d'une variable à sa déclaration ? J'imagine que oui avec des directives preproc, et donc pas en "dynamique". L'idée serait d'avoir un truc du genre :

                  #if (sizeof(int)==1)
                    uint8_t *ptr;
                  
                  #elif (sizeof(int)==2)
                    uint16_t *ptr;
                  
                  #elif (sizeof(int)==4)
                    uint32_t *ptr;
                  
                  #endif




                  -
                  Edité par drx 17 septembre 2013 à 3:56:19

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Bonhomme !! | Jeu de plateforme : Prototype.

                    17 septembre 2013 à 8:33:34

                    drx a écrit:

                    Cependant, en continuant mes recherches, je n'arrive pas à trouver de liste exhaustive des type "garantis", je suppose que les (s|u)int(taille)_t en font partie, mais qu'en est-il des autres comme size_t par exemple ?

                    size_t n'a pas une taille garantie. Ce type est typiquement fait pour passer tes allocations, or la taille maximale d'allocation n'est pas la même en fonction de ton architecture (et donc de la taille de tes pointeurs ;) ).

                    drx a écrit:

                    J'en conclu également que sur un changement de plateforme, il convient de re-compiler non-seulement le programme, mais également les utilitaires pour reformater les fichiers utiles à l'application... Du coup le transfert de fichiers d'une plateforme à l'autre devient improbable...

                    Ben, oui il faut recompiler, mais tes fichiers eux doivent avoir le même format (taille de type compris) sur toutes les machines. C'est là l'intérêt de passer par les types fixés sus-nommés.

                    drx a écrit:

                    Y a-t-il un moyen de conditionner le type d'une variable à sa déclaration ? J'imagine que oui avec des directives preproc, et donc pas en "dynamique". L'idée serait d'avoir un truc du genre :

                    #if (sizeof(int)==1)
                    //[...]
                    #endif

                    Oui :

                    #ifdef sizeof(int) == 1
                      typedef uint8t  m_int;
                    #ifdef sizeof(int) == 2
                      typedef uint16t m_int;
                    //[...]
                    #endif

                    -
                    Edité par Ksass`Peuk 17 septembre 2013 à 9:59:09

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                      17 septembre 2013 à 9:47:45

                      L'avenir de la portabilité va être sombre à cause des architectures 64 bits LP64 et LLP64, qui vont poser pas mal de soucis, quand tout le monde compilera en 64.... :(
                      • Partager sur Facebook
                      • Partager sur Twitter

                      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                        17 septembre 2013 à 9:49:42

                        drx a écrit:

                        Effectivement, dans un sens, je ne cherche pas non-plus à voir mes programmes tourner sur des 68000, mais sans aller chercher aussi loin

                        mais qu'en est-il des autres comme size_t par exemple ?

                        Oh, un 680x0 n'a rien de bien exotique. C'est d'ailleurs le processeur qui a servi de base à beaucoup de stations de travail (notamment Unix). Et aussi à GCC (http://gcc.gnu.org/wiki/History).

                        Sinon, pour size_t, il est garanti qu'il est entier non signé. Il permet de représenter la taille de tout objet; c'est le type résultant d'un sizeof.
                        • Partager sur Facebook
                        • Partager sur Twitter
                          17 septembre 2013 à 11:34:00

                          Salut,

                          Si tu inclues limits.h dans ton fichier, une macro INT_MAX et UINT_MAX sont définies en fonction de ton système.

                          • Partager sur Facebook
                          • Partager sur Twitter
                          « The first rule of Fight Club is: You do not talk about Fight Club. »
                            17 septembre 2013 à 16:48:33

                            Salut,
                            drx a écrit:

                            Cependant, en continuant mes recherches, je n'arrive pas à trouver de liste exhaustive des type "garantis", je suppose que les (s|u)int(taille)_t en font partie, mais qu'en est-il des autres comme size_t par exemple ?

                            En fait, la norme ne te garantit pas l'existence des types de taille fixe.
                            C11 (N1570), § 7.20.1.1, Exact-width integer types, al. 3, p. 290.

                            These types are optional.

                            Elle ne te garantit que la présence des types u?intleastXX_t, u?intfastXX_t et u?intmax_t.
                            Ksass`Peuk a écrit: >> drx a écrit: >> Y a-t-il un moyen de conditionner le type d'une variable à sa déclaration ? J'imagine que oui avec des directives preproc, et donc pas en "dynamique".

                            Oui :

                            #ifdef sizeof(int) == 1
                            typedef uint8t m_int;
                            

                            ifdef sizeof(int) == 2

                            typedef uint16t m_int; //[...]

                            endif

                            </pre>

                            Je suppose que tu voulais utiliser la directive #if et non #ifdef ? Sinon, de telles expressions de contrôle ne sont pas possible au niveau du préprocesseur car les mots-clés sont remplacés par 0 au sein de celles-ci.
                            C11 (N1570), § 6.10.1, Conditional inclusion, al. 4, p. 163.

                            Prior to evaluation, macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator), just as in normal text. If the token defined is generated as a result of this replacement process or use of the defined unary operator does not match one of the two specified forms prior to macro replacement, the behavior is undefined. After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0, and then each preprocessing token is converted into a token [...].

                            Soit dit en passant, cela peut donner des trucs assez amusants, comme ceci par exemple :

                            #if if == else
                            #error Ben quoi ?
                            #endif
                            
                            int
                            main(void)
                            {
                                    return 0;
                            }
                            

                            drx a écrit:

                            J'en conclu également que sur un changement de plateforme, il convient de re-compiler non-seulement le programme, mais également les utilitaires pour reformater les fichiers utiles à l'application... Du coup le transfert de fichiers d'une plateforme à l'autre devient improbable...

                            Si c'est le cas, il y a un soucis dans ta mise en œuvre des utilitaires gérant les fichiers. Techniquement, tu n'as qu'une seule chose à fixer : le format de ton fichier et la représentation de ses données (taille, boutisme, etc). Pour le reste, il te suffit de récupérer les données à l'aide de tableaux de char.

                            -
                            Edité par Taurre 17 septembre 2013 à 16:51:02

                            • Partager sur Facebook
                            • Partager sur Twitter
                              17 septembre 2013 à 16:51:48

                              Taurre a écrit:

                              Pour le reste, il te suffit de récupérer les données à l'aide de tableaux de char.

                              On peut préférer unsigned char pour être sûr d'avoir affaire à un entier non signé (ou signed char si l'on veut un entier signé, mais bon).

                              • Partager sur Facebook
                              • Partager sur Twitter
                                17 septembre 2013 à 16:58:27

                                Marc Mongenet a écrit:

                                On peut préférer unsigned char pour être sûr d'avoir affaire à un entier non signé (ou signed char si l'on veut un entier signé, mais bon).

                                Effectivement, j'avais oublié de le préciser, merci. ;)

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  18 septembre 2013 à 2:16:16

                                  Salut,

                                  Taurre a écrit:

                                  Si c'est le cas, il y a un soucis dans ta mise en œuvre des utilitaires gérant les fichiers. Techniquement, tu n'as qu'une seule chose à fixer : le format de ton fichier et la représentation de ses données (taille, boutisme, etc). Pour le reste, il te suffit de récupérer les données à l'aide de tableaux de char.

                                  Et oui, justement. Par exemple dans l'utilitaire qui regroupe mes fichier de ressources, ma table d'index est une table de long, justement parce que je croyais, comme précisé dans mon premier post qu'un long serait de taille constante quelque soit le système. Table que je récupère donc sous forme de table de long dans l'appli qui a besoin des ressources, sauf que si je compile mon appli chez Marc qui a un long de 8, au moment d'aller chercher la table dans le fichier ça va lamentablement foirer.

                                  Donc, faire une relecture avec l'opérateur sizeof : fread(*maVariable,sizeof(long),1,fichier) est clairement une erreur, il est plus sécurisé de fixer en dur les volumes en fonction de la taille des types du système qui a créé le fichier : fread(*maVariable,4,1,fichier).

                                  Ensuite dans le cas de stockage/relecture d'une structure, il faudrait le faire membre par membre ?

                                  Exemple, en mettant de côté les problèmes de padding et d'indianness si vous voulez bien, en admettant que Marc déclare chez lui une structure du genre :

                                  struct demo
                                  {
                                     long varA;
                                     int varB;
                                  }

                                  Donc 8+4 Octets chez Marc, (si je ne me trompe) puis qu'il colle dans un fichier au fwrite(*demo,sizeof(demo),1,fichier) pour me l'envoyer. Avec la même structure de (4+4) Octets si le "lecteur" est compilé chez moi, j'aurai beau relire membre par membre en sachant que je dois lire (8+4) Octets, je vais quand-même être dans le caca pour stocker le contenu de la structure en mémoire non ?
                                  La structure déclarée dans le "lecteur" du fichier ne peut décidément pas être la même selon le système puisqu'il va me falloir un long long et un int pour arriver aux 12 octets nécessaires...

                                  Et mettons, que j'aille les chercher au tableau de char, sachant pertinemment que la première valeur à aller chercher est sur 8 octets, je vais la coller dans un unsigned char[8], et ensuite ? Si je veux extraire la valeur, le problème reste le même, il faut un long*ptr chez Marc et un long long*ptr chez moi...

                                  En fait, il arrive un moment où le type en soi importe peu, mais où c'est la taille qui compte (contrairement à ce qui se dit :p ) et que savoir que ma structure est sur 12 Octets dont un entier sur 8 et un sur 4, est la seule chose dont j'ai besoin, que ce soient des ints, des doubles, des choux ou des carottes...

                                  Je comprend bien que le principe de portabilité ne s'applique qu'au code et non à l'exécutable, je ne m'attend pas à ce qu'un truc compilé pour Xbox tourner sur une PS et vice-versa. Mais entre 2 PC, même avec des OS différents alors que l'architecture physique va être la même (ou très proche), c'est un peu abusé si on ne peut pas être certain de la taille d'un type et refiler un fichier d'un système à l'autre.

                                  Toujours est-il que je vous remercie bien pour vos remarques, ça m'a permit d'apprendre des trucs et de me rendre compte que les choses ne sont pas toujours aussi simples qu'elles semblent, parce qu'en quelque sorte, il s'agît d'un "faux vrai problème", puisque ma méthode a toujours fonctionné jusqu'ici (sous Win32/64 au moins). Donc jusqu'au jours où j'aurai tenté de compiler chez Marc (j'apporterai les bières ;) ) je n'ai aucun moyen de savoir que ça ne va plus fonctionner...

                                  Salutations.

                                  -
                                  Edité par drx 18 septembre 2013 à 2:20:40

                                  • Partager sur Facebook
                                  • Partager sur Twitter

                                  Bonhomme !! | Jeu de plateforme : Prototype.

                                    18 septembre 2013 à 8:20:14

                                    @Taurre : j'aurais bien cité mais ça ne fonctionne plus ... Donc oui c'est bien "#if" que je voulais mettre, comme quoi il faut pas faire de copier/coller (et surtout pas hâtifs) :lol: .
                                    • Partager sur Facebook
                                    • Partager sur Twitter

                                    Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                      18 septembre 2013 à 9:59:16

                                      Il est normal qu'il existe beaucoup de diversité sur les PC. Ce sont des machines avec un historique inégalé, qui ont un mode de fonctionnement 16 bits, un 32 bits, un 64 bits...

                                      Concernant la sérialisation (sauvegarde sur fichier, transfert réseau), elle doit être spécifiée octet par octet, sinon elle n'est pas portable.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        18 septembre 2013 à 12:16:11

                                        En effet, souvent, dans les fichiers binaires, les normes (quand ils sont documentés - mais même quand ils ne le sont pas en fait) parlent de int32, de int16, ou autres types dont les tailles sont fixées. Jamais de "int".

                                        Il existe aussi une documentation sur l'endianness dans le fichier. Soit elle est fixée (et c'est au lecteur de s'adapter), soit il y a un char8, voir un seul bit au début, qui définit l'endianness dans le fichier.

                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                                          18 septembre 2013 à 20:47:33

                                          drx a écrit:

                                          Ensuite dans le cas de stockage/relecture d'une structure, il faudrait le faire membre par membre ?

                                          En effet, il est nécessaire de convertir chaque membre en un tableau de char et d'écrire ensuite ce tableau dans le fichier (et inversément pour la lecture). Cela peut se faire facilement à l'aide de quelques fonctions dédiées.

                                          -
                                          Edité par Taurre 18 septembre 2013 à 20:48:23

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            19 septembre 2013 à 19:53:00

                                            Tu te fais des fonctions "LireFloat", "LireDouble", "LireInt32"  ; "EcrireFloat" ....

                                            Ce sont elles qui s'occupent de gérer l'endianness voulu. Et hop, une fois ces fonctions bas niveau implémentées, fini les soucis d'endianness.

                                            Ensuite, tu peux te faire des fonction "EcrireTableauFloat" ... Ou "EcrireCetteStructure".... Et les fonctions symétriques "LireTableauFloat"....

                                            Bref, se faire des couches pour faire très vite abstraction des soucis bas niveau.

                                            Chaque couche a ses soucis, ses particularités, ses précautions. A nous de bien faire pour que ces choses restent dans leur couche, et ne remontent pas plus haut. Sinon, en haut, on se noie.

                                            -
                                            Edité par Fvirtman 19 septembre 2013 à 19:54:33

                                            • Partager sur Facebook
                                            • Partager sur Twitter

                                            Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                                              20 septembre 2013 à 5:01:11

                                              Re,

                                              Désolé d'insister mais, soit je me suis mal fait comprendre, soit vous lisez en diagonal.

                                              Quelque soit la méthode utilisée pour créer le fichier, si la bécane d'en face n'a pas des types de même taille, même en recompilant le lecteur (et surtout en recompilant, j'ai envie de dire), il lui sera impossible d'interpréter correctement les données du ficher sans revoir le code ou reformater le fichier pour cette plateforme.

                                              si par exemple, mon code est celui-ci, un exemple comme un autre hein :p :

                                              Avec un double de 8 chez lui et un de 4 chez moi, la relecture reste impossible sans recréer le fichier chez moi. Sauf si son programme tourne chez moi sans avoir à recompiler puisque les constantes resterons les même (sizeof())...

                                              Maintenant, je me suis essayé à un code (pas testé, j'espère avoir pensé à tout) en suivant vos conseilles, Uchar par Uchar:

                                              int writeValueInFile(void*val, size_t size, FILE* file)
                                              {
                                                  /***Declarations***/
                                                  int ret=0;
                                              
                                                  /***PRL***/
                                                  if (val==NULL || size==0 || file==NULL)
                                                      return FUNC_ERR;
                                              
                                                  /**Execution**/
                                                  switch (size)
                                                  {
                                                      case 1:
                                                          ret=writeOctInFile(val, file);
                                                      break;
                                              
                                                      case 2:
                                                          ret=writeWordInFile(val,file);
                                                      break;
                                              
                                                      case 4:
                                                          ret=writeDWordInFile(val,file);
                                                      break;
                                              
                                                      case 8:
                                                          ret=writeQWordInFile(val,file);
                                                      break;
                                              
                                                      default:
                                                          fprintf(stderr,"Valeur exotique ou d'une autre planète, va t'acheter un pc...\n);
                                                          ret=FUNC_ERR;
                                                      break;
                                                  }
                                              
                                                  return ret;
                                              }
                                              
                                              /**Exemple en 32 bits...*/
                                              int writeDWordInFile(void* val,FILE*file)
                                              {/*32 bits vers le fichier en lilendian*/
                                              
                                                  /***Déclarations***/
                                                  usigned char *ptr=val, recover=0;
                                                  int index,ret=FUNC_OK;
                                                  (void)(*backOp[2])(unsigned char*, int)={swapEndLilToBig,swapEndLilToMed};
                                              
                                                      /***PRL***/
                                                  if(val==NULL || file==NULL)
                                                      return FUNC_ERR;
                                              
                                                  /***Executions***/
                                              
                                                  if(BYTE_ORDER==BIG_ENDIAN)
                                                  {/*convertir en lil Endian*/
                                                      swapEndBigToLil(ptr,4);
                                                      recover=1;
                                                  }
                                                  else if(BYTE_ORDER==MID_ENDIAN)
                                                  {/*convertir en lil Endian*/
                                                      swapEndMedToLil(ptr,4);
                                                      recover=2;
                                                  }
                                              
                                                  if (fwrite(ptr,1,4,file)<4)/*écrire dans le fichier*/
                                                      ret=FUNC_ERR;
                                              
                                                  if (recover)/*rétablir l'état de la valeur*/
                                                      backOp[recover-1](ptr,4);
                                              
                                                  return ret;
                                              }

                                              Donc là, normalement, je suis plutôt bien blindé pour l'écriture (même si pas forcément optimisé et si ça fait cher pour coller 4 octets dans un fichier ^^ ), mais reste quand même que si je créé un fichier ainsi pour le filer à Marc ou Fvirtman, ils seront de toute façons également incapables de lire le fichier puisque le retour de readValueInFile(sizeOf(long),fichier), dans le même principe que les fonctions ci dessus en version "lecture" va lire une valeur sur 8 et non sur 4 octets.

                                              Donc, pas le choix, changement de plateforme, c'est forcément reformatage des fichiers pour cette plateforme...

                                              Taurre a écrit:

                                              Salut,

                                              [...]

                                              drx a écrit:

                                              J'en conclu également que sur un changement de plateforme, il convient de re-compiler non-seulement le programme, mais également les utilitaires pour reformater les fichiers utiles à l'application... Du coup le transfert de fichiers d'une plateforme à l'autre devient improbable...

                                              Si c'est le cas, il y a un soucis dans ta mise en œuvre des utilitaires gérant les fichiers. Techniquement, tu n'as qu'une seule chose à fixer : le format de ton fichier et la représentation de ses données (taille, boutisme, etc). Pour le reste, il te suffit de récupérer les données à l'aide de tableaux de char.

                                              Ai-je été plus clair sur ma démonstration ? Donc le fichier portable est-il une utopie tant que la taille des types n'est pas garantie d'une plateforme à l'autre ?

                                              Bonne continuation

                                              -
                                              Edité par drx 20 septembre 2013 à 6:04:53

                                              • Partager sur Facebook
                                              • Partager sur Twitter

                                              Bonhomme !! | Jeu de plateforme : Prototype.

                                                20 septembre 2013 à 7:34:18

                                                "readValueInFile(sizeOf(long),fichier),"

                                                Justement, il ne faut pas considérer des long mais des int32 ou des int64 (après tu te fais le typedef qui va bien). Que des types à taille fixe.

                                                • Partager sur Facebook
                                                • Partager sur Twitter

                                                Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                                                  20 septembre 2013 à 8:17:54

                                                  Re,

                                                  Ha bin ok alors, désolé d'avoir été insistant, mais comme

                                                  Taurre a écrit:

                                                  Salut,

                                                  drx a écrit:

                                                  Cependant, en continuant mes recherches, je n'arrive pas à trouver de liste exhaustive des type "garantis", je suppose que les (s|u)int(taille)_t en font partie, mais qu'en est-il des autres comme size_t par exemple ?

                                                  En fait, la norme ne te garantit pas l'existence des types de taille fixe.

                                                  Du coup, il m'a un peu fait flipper. Bon merci pour toutes ces infos, je vais continuer sur ma lancée et finir d'implémenter mes fonctions "j'écris et je lis" à l'épreuve des balles ^^ .

                                                  Salutations.

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter

                                                  Bonhomme !! | Jeu de plateforme : Prototype.

                                                    20 septembre 2013 à 9:46:01

                                                    Salut, quelqu'un pourrait me suggerer r comment passer ces define en parametre à une function?

                                                    #define CAN_ERR_MASK 0x1FFFFFFFU

                                                    #define CAN_ERR_FLAG 0x20000000U

                                                    #define CAN_EFF_MASK 0x1FFFFFFFU

                                                    #define CAN_EFF_FLAG 0x80000000U

                                                    #define CAN_SFF_MASK 0x000007FFU

                                                    int ManageMessage(CAN_ERR_MASK )

                                                    {

                                                        /*   utilisation le parametre ici */

                                                    }

                                                    Merci

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      20 septembre 2013 à 9:49:03

                                                      Fvirtman a écrit:

                                                      Justement, il ne faut pas considérer des long mais des int32 ou des int64 (après tu te fais le typedef qui va bien). Que des types à taille fixe.

                                                      C'est une solution, mais il est encore plus simple de concevoir des fonctions qui effectuent des conversions en tableau de char.
                                                      drx a écrit:

                                                      Ai-je été plus clair sur ma démonstration ? Donc le fichier portable est-il une utopie tant que la taille des types n'est pas garantie d'une plateforme à l'autre ?

                                                      Non, le fichier portable n'est pas une utopie. Voici un exemple de module fournissant des fonctions de lecture/écriture pour des entiers de 16 et 32 bits au format petit boutiste.
                                                      io.h

                                                      #ifndef IO_H
                                                      #define IO_H
                                                      
                                                      extern int read_s16le(FILE *);
                                                      extern unsigned read_u16le(FILE *);
                                                      extern long read_s32le(FILE *);
                                                      extern unsigned long read_u32le(FILE *);
                                                      
                                                      extern void write_s16le(int *, FILE *);
                                                      extern void write_u16le(unsigned *, FILE *);
                                                      extern void write_s32le(long *, FILE *);
                                                      extern void write_u32le(unsigned long *, FILE *);
                                                      
                                                      extern unsigned read_write_err;
                                                      
                                                      #endif /* !IO_H */
                                                      

                                                      io.c

                                                      #include <stdio.h>
                                                      
                                                      #include "io.h"
                                                      
                                                      
                                                      unsigned read_write_err;
                                                      
                                                      
                                                      int
                                                      read_s16le(FILE *fp)
                                                      {
                                                              unsigned char b[2];
                                                      
                                                              fread(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                                      return 0;
                                                              }
                                                              return b[0] | b[1] << 8;
                                                      }
                                                      
                                                      
                                                      unsigned
                                                      read_u16le(FILE *fp)
                                                      {
                                                              unsigned char b[2];
                                                      
                                                              fread(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                                      return 0;
                                                              }
                                                              return 0U | b[0] | b[1] << 8;
                                                      }
                                                      
                                                      
                                                      long
                                                      read_s32le(FILE *fp)
                                                      {
                                                              unsigned char b[4];
                                                      
                                                              fread(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                                      return 0;
                                                              }
                                                              return b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
                                                      }
                                                      
                                                      
                                                      unsigned long
                                                      read_u32le(FILE *fp)
                                                      {
                                                              unsigned char b[4];
                                                      
                                                              fread(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                                      return 0;
                                                              }
                                                              return 0UL | b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24;
                                                      }
                                                      
                                                      
                                                      void
                                                      write_s16le(int *p, FILE *fp)
                                                      {
                                                              write_u16le((unsigned *)p, fp);
                                                      }
                                                      
                                                      
                                                      void
                                                      write_u16le(unsigned *p, FILE *fp)
                                                      {
                                                              unsigned char b[2];
                                                      
                                                              b[0] = *p & 0xFF;
                                                              b[1] = (*p >> 8) & 0xFF;
                                                              fwrite(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                              }
                                                      }
                                                      
                                                      
                                                      void 
                                                      write_s32le(long *p, FILE *fp)
                                                      {
                                                              write_u32le((unsigned long *)p, fp);
                                                      }
                                                      
                                                      
                                                      void
                                                      write_u32le(unsigned long *p, FILE *fp)
                                                      {
                                                              unsigned char b[4];
                                                      
                                                              b[0] = *p & 0xFF;
                                                              b[1] = (*p >> 8) & 0xFF;
                                                              b[2] = (*p >> 16) & 0xFF;
                                                              b[3] = (*p >> 24) & 0xFF;
                                                              fwrite(b, 1, sizeof b, fp);
                                                              if (ferror(fp)) {
                                                                      ++read_write_err;
                                                              }
                                                      }
                                                      

                                                      Avec de telles fonctions, le résultat des lecture/écriture sera le même, peut importe la taille des types (sauf pour le type char qui doit avoir une taille de huit bits) et le boutisme utilisé sur les différentes machines (attention toutefois que, pour les entiers signés, cela suppose que les machines cibles utilisent une représentation en complément à deux).

                                                      -
                                                      Edité par Taurre 20 septembre 2013 à 9:49:48

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        20 septembre 2013 à 10:20:55

                                                        Taurre a écrit:

                                                        [...] (sauf pour le type char qui doit avoir une taille de huit bits) [...]

                                                        Et oui.. sauf... c'est bien là le problème de base. Parce qu'à ce moment là, sérialiser un long, ça marche aussi, bon, SAUF que le long doit faire 32...

                                                        Donc si une boite pond un proc au char de 2 octets parce que, mettons l'octet ne sert plus à rien (déjà que...) et que l'UTF16 est devenu natif et remplace l'ascii (par exemple) bin ça marche plus. Donc tu ne fais que supposer que le char fait un octet au lieu d'imposer un 8 bits...

                                                        Marc Mongenet a écrit:

                                                        [...] Il peut aussi exister des processeurs spécialisés qui n'ont qu'une seule taille de donnée: 32 bits. Dessus, un char fait 32 bits. Et voilà.[...]

                                                        Dommage... Bon je cherche la petite bête, avec un peu de mauvaise foi, c'est sûr. Mais du coup, tu peux ptet remplacer tes uchar par des uint8_t ^^ .

                                                        Bonne continuation.

                                                        EDIT. Ceci dit, merci pour le jeu de fonction, même si je préfère me faire les miennes, ça me sera utile à titre d'inspiration ^^ .

                                                        -
                                                        Edité par drx 20 septembre 2013 à 10:43:32

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter

                                                        Bonhomme !! | Jeu de plateforme : Prototype.

                                                          20 septembre 2013 à 10:30:29

                                                          Lors que tu définis le format de ton fichier, tu définis au passage dans quelle plage de données doivent se trouver les valeurs manipulées. Il y a deux cas qui peuvent éventuellement poser problème :

                                                          Si la machine d'accueil utilise des types plus petits en natif, auquel cas, soit tu spécifies que "la machine doit utiliser des types d'au moins [mettez les tailles ici]" et donc tu limites ta portabilité côté exécution. Ou alors, tu t'assures qu'elle utilisera des types plus longs aussi à l'exécution, quitte à ralentir le fonctionnement à coup de typedef en fonction de l'architecture.

                                                          Si la machine d'accueil utilise des types plus longs en natif, on pourrait dire "ça risque de poser des problèmes à l'écriture". Sauf qu'en fait, non, si tu as définis des tailles précises pour ton fichier, c'est que tu es sûr que l'exécution ne peut pas générer des valeurs plus grandes quelle que soit l'architecture, donc tout est ok.

                                                          -
                                                          Edité par Ksass`Peuk 20 septembre 2013 à 10:30:54

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter

                                                          Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C

                                                            20 septembre 2013 à 11:29:04

                                                            drx a écrit:

                                                            Marc Mongenet a écrit:

                                                            [...] Il peut aussi exister des processeurs spécialisés qui n'ont qu'une seule taille de donnée: 32 bits. Dessus, un char fait 32 bits. Et voilà.[...]

                                                            Dommage... Bon je cherche la petite bête, avec un peu de mauvaise foi, c'est sûr. Mais du coup, tu peux ptet remplacer tes uchar par des uint8_t ^^ .

                                                            Sur un processeur comme https://en.wikipedia.org/wiki/Super_Harvard_Architecture_Single-Chip_Computer il n'y a manifestement aucune possibilité d'adresser plus finement qu'un byte de 32 bits. Les char font donc 32 bits. Le type uint8_t n'existe probablement pas. Mais ce n'est pas un processeur destiné à faire fonctionner des programmes généralistes. Je ne vois pas de raison pratique d'essayer d'écrire un logiciel portable pour ce genre de processeur.
                                                            Il y a en revanche de bonnes raisons d'écrire un programme capable de gérer little et big endian, des long de 32 ou 64 bits, des char signés ou pas.
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              20 septembre 2013 à 12:47:11

                                                              Re,

                                                              @Marc: Effectivement... Ceci étant, ma remarque était plus une boutade qu'une véritable volonté de démonter les arguments de Taurre, à qui je demande de m'excuser s'il ne l'a pas perçu comme tel, probablement une maladresse de ma part.

                                                              cependant avec :

                                                              void
                                                              write_u32le(unsigned long *p, FILE *fp)
                                                              {
                                                                      unsigned char b[4];
                                                              
                                                                      b[0] = *p & 0xFF;
                                                                      b[1] = (*p >> 8) & 0xFF;
                                                                      b[2] = (*p >> 16) & 0xFF;
                                                                      b[3] = (*p >> 24) & 0xFF;
                                                                      fwrite(b, 1, sizeof b, fp);
                                                                      if (ferror(fp)) {
                                                                              ++read_write_err;
                                                                      }
                                                              }

                                                              p pointera sur un 64 bits chez Marc...Du coup la version big endian n’extraira que les 4 octets de poids les plus forts et écrira 0x00000000 dans le fichier si la valeur est inférieure à 0xFFFFFFFF...

                                                              Vous remarquerez quand-même que je me suis empressé d'implémenter les fonctions qui devraient résoudre ce problème en suivant vos conseils... Plus ou moins avec style, c'est certain, mais déjà...

                                                              Permettre le bon fonctionnement de mes programmes sur PC indépendamment de l'OS me suffit bien, comme ça les linuxiens pourront aussi profiter de mon super shoot'em up et changer leurs vies :p .

                                                              Sinon, je pense avoir eu les réponses et les explications que j'attendais, merci à vous tous.

                                                              Bonne continuation.

                                                              -
                                                              Edité par drx 20 septembre 2013 à 14:43:23

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              Bonhomme !! | Jeu de plateforme : Prototype.

                                                              Faire confiance à un type

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown