char[] ou byte[] ?

char[] ou byte[] ? - C - Programmation

Marsh Posté le 11-04-2009 à 11:32:28    

Salut,

 

Pour un projet C embarqué, j'ai développé une petite lib bien utile que nous utilisons de façon extensive.

 

Parmi les choses développées, il y a une structure String (chaîne de caractères extensible et sûre), et petite structure destinée à servir de buffer d'octets extensible, nommée Arrchar définie ainsi:

Code :
  1. typedef struct Arrchar{
  2.    size_t len;
  3.    size_t sz;
  4.    char *buf;
  5. };


(Incidemment, les structures Arrchar et String sont structurellement identiques, mais les fonctions qui agissent dessus diffèrent)
Sur cette structure, j'ai défini un certains nombre de "méthodes", de mémoire:

Code :
  1. size_t arrcharcpy(Arrchar *dst, const Arrchar *src);
  2. size_t arrcharcat(Arrchar *dst, const Arrchar *src);
  3. size_t arrcharfill(Arrchar *ac, const char c);
  4. String * arrchartostring(const Arrchar *ac, const FORMAT f, const char separator); // FORMAT peut être HEXA, OCTO, DEC ou ASCII
  5. void arrcharleft(Arrchar *ac, const char c); // déplace le contenu à gauche
  6. void arrcharright(Arrchar *ac, const char c); // déplace le contenu à droite
  7. char arrcharpop (Arrchar *ac);
  8. void arrcharpush (Arrchar *ac, const char c);
  9. ...
  10. etc
 

Le problème est que je me rends compte que j'ai bien loosé en choisissant le type char pour buf, qui est signé.
Ma question, en pratique les buffers d'octets utilisés la plupart du temps dans les applis embarquées, ce sont des signés ou des non signés ?
(heureusement, je n'ai pas encore écrit des trucs du style arrchartoBCD)
Y'aurait-il une ruse pour pouvoir utiliser les deux types avec la même structure ou dois-je écrire une "classe" Arrbyte ?


Message édité par el muchacho le 11-04-2009 à 11:37:43

---------------
Les aéroports où il fait bon attendre, voila un topic qu'il est bien
Reply

Marsh Posté le 11-04-2009 à 11:32:28   

Reply

Marsh Posté le 11-04-2009 à 12:31:03    

char n'est pas toujours signé, ça dépend de l'implémentation. Certains compilateurs laissent le programmeur choisir, avec un paramètre de compilation.
 
Tu peux, et surtout en C, copier un char, un signed char (ça existe !) et un unsigned char de l'un vers l'autre, ça sera une copie bit-à-bit sans aucune perte d'information.
 

Code :
  1. char* c = malloc( 42 );
  2. signed char* sc = malloc( 42 );
  3. unsigned char* uc = malloc( 42 );
  4. memcpy( c, uc, 42 );
  5. memcpy( sc, c, 42 );
  6. memcpy( uc, sc, 42 );
  7. /*ou plus simplement :*/
  8. char x = 'a';
  9. unsigned char y;
  10. y = (unsigned char)x;
  11. x = (char)y;
  12. printf( "%c", x ); // a
  13. /* toutes ces copies de qlqchose-char sont indolores et ne modifient pas les valeurs selon le signe */


 
Donc pour répondre à ta question, tu peux sans problème stocker des char dans des unsigned char et vice-versa, pas de problème.
Note tout de même que ces 3 types sont différents et requierent, si on veut faire une syntaxe propre, un cast.
Pour l'annectode, en C++ passer de char à unsigned char demande un reinterpret_cast et non pas un static_cast, même si les compilateurs sont assez tolérants là dessus.

Reply

Marsh Posté le 11-04-2009 à 13:41:10    

jesus_christ a écrit :

Donc pour répondre à ta question, tu peux sans problème stocker des char dans des unsigned char et vice-versa, pas de problème.


 
En théorie il y a possibilité de cas problèmatique (signed char peut avoir une représentation "trap", un -0 en complément à un; je parie que si une implémentation doit utiliser cette possibilité, elle va avoir char qui est non signé).
 

Citation :

Note tout de même que ces 3 types sont différents et requierent, si on veut faire une syntaxe propre, un cast.


 
Par "syntaxe propre" entends-tu quelque chose d'autre que "le style que je préfère"?
 

Citation :

Pour l'annectode, en C++ passer de char à unsigned char demande un reinterpret_cast et non pas un static_cast, même si les compilateurs sont assez tolérants là dessus.


 
J'aimerais bien avoir ton raisonnement (reinterpret_cast n'est autorisé qu'avec des pointeurs et des références, le cas des références étant défini a partir du cas des pointeurs).  Donc d'après moi, un compilateur C++ doit donner une erreur sur

Code :
  1. unsigned char f(char c)
  2. {
  3.    return reinterpret_cast<unsigned char>(c);
  4. }


(petite vérification, c'est le cas de tous ceux que j'ai sous la main: g++, sun CC, Intel icpc, Como).

Reply

Marsh Posté le 11-04-2009 à 15:06:05    

Un Programmeur a écrit :


 
En théorie il y a possibilité de cas problèmatique (signed char peut avoir une représentation "trap", un -0 en complément à un; je parie que si une implémentation doit utiliser cette possibilité, elle va avoir char qui est non signé).
 
Bonne remarque, mon explication impose que la machine utilise le complément à 2, ce qui est le cas d'à peu près toutes les machines à peu près actuelles, même si dans l'embarqué, ce qui est le cas ici, on peut tomber sur des archi exotiques.
 

Citation :

Note tout de même que ces 3 types sont différents et requierent, si on veut faire une syntaxe propre, un cast.


 
Par "syntaxe propre" entends-tu quelque chose d'autre que "le style que je préfère"?
 
Une syntaxe qui ne sort pas de warning même à un niveau de warning élevé on va dire. Et keep cool, t'es pas obligé de poser la question sur un ton si hautin.
 

Citation :

Pour l'annectode, en C++ passer de char à unsigned char demande un reinterpret_cast et non pas un static_cast, même si les compilateurs sont assez tolérants là dessus.


 
J'aimerais bien avoir ton raisonnement (reinterpret_cast n'est autorisé qu'avec des pointeurs et des références, le cas des références étant défini a partir du cas des pointeurs).  [...]


 
Oui oui c'est ça, dans mon exemple avec le memcpy, qui serait devenu un std::copy, c'est le pointeur aurait du être reinterpret_casté.

Reply

Marsh Posté le 11-04-2009 à 16:47:16    

jesus_christ a écrit :


Donc pour répondre à ta question, tu peux sans problème stocker des char dans des unsigned char et vice-versa, pas de problème.
Note tout de même que ces 3 types sont différents et requierent, si on veut faire une syntaxe propre, un cast.
Pour l'annectode, en C++ passer de char à unsigned char demande un reinterpret_cast et non pas un static_cast, même si les compilateurs sont assez tolérants là dessus.


Ok donc ça devrait aller. Par contre, pour les opérations de manipulation de bits, ça sera une autre histoire.

Reply

Marsh Posté le 11-04-2009 à 16:55:31    

oui, typiquement les shifts se comportent différement selon le signness.
Pour un masque ^, | ou & (xor, or ou and) normalement il n'y a pas de différence, sauf si ton architecture est un peu spéciale.
Par contre pour du stockage, à plus forte raison avec des memcpy, tu ne devrais pas avoir de pb.

Reply

Marsh Posté le 11-04-2009 à 21:36:46    

jesus_christ a écrit :

Pour l'annectode, en C++ passer de char à unsigned char demande un reinterpret_cast et non pas un static_cast, même si les compilateurs sont assez tolérants là dessus.


 

jesus_christ a écrit :

Oui oui c'est ça, dans mon exemple avec le memcpy, qui serait devenu un std::copy, c'est le pointeur aurait du être reinterpret_casté.


 
C'est lesquels tes compilateurs qui acceptent un static_cast pour passer d'un char* à un unsigned char*?  Tous ceux que j'ai sous la main génèrent une erreur dans leur mode par défaut .

Reply

Marsh Posté le 14-04-2009 à 10:43:37    

Emmanuel Delahaye a écrit :


Non. Il n'y a pas de trap-representation sur un char (signé ou non). Ca commence à partir de short...


 
Quel est ton raisonnement?  Je viens de relire 6.2.6.2 et rien ne m'y fait changer d'avis:
 
- unsigned char n'a pas de trap-representation ni de bits de padding
- signed char n'a pas de bits de padding et peut avoir une trap representation (la seule est celle qui correspondrait a un -0 en grandeur et signe ou en complement a un; l'absence de bits de padding previent l'existance d'autres)
- les autres types entiers peuvent avoir les deux qu'ils soient signes ou non.
 
Il y a peut-etre une contrainte ailleurs qui empeche -0 d'etre une trap value, mais je ne l'ai pas vue.

Reply

Marsh Posté le 14-04-2009 à 10:52:54    

Un Programmeur a écrit :


 
Quel est ton raisonnement?  Je viens de relire 6.2.6.2 et rien ne m'y fait changer d'avis:
 
- unsigned char n'a pas de trap-representation ni de bits de padding
- signed char n'a pas de bits de padding et peut avoir une trap representation (la seule est celle qui correspondrait a un -0 en grandeur et signe ou en complement a un; l'absence de bits de padding previent l'existance d'autres)


OK, j'avais oublié ça.
 


---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 14-04-2009 à 11:24:15    

concretement, ca peut induire quelle genre d'erreur ces histoires ?

Reply

Marsh Posté le 14-04-2009 à 11:24:15   

Reply

Marsh Posté le 14-04-2009 à 11:59:56    

Joel F a écrit :

concretement, ca peut induire quelle genre d'erreur ces histoires ?


 
Concretement?  Je doute que tu vas trouver une machine en complement a un ou en grandeur et signe (c'est pour cela
que j'ai commence par "en theorie"; je ne passerais pas un quart de seconde a m'occuper du cas dans un programme),
meme dans l'embarque.
 
OK, il y a les descendants des Univac 1100 qui etaient toujours vendu par Unisys la derniere fois que j'ai regarde --
mais ca m'etonnerait qu'ils soient programmes en C et ca ne m'etonnerait pas qu'ils aient fait faillite depuis.  Une autre
possibilite, c'est l'utilisation d'un format commun flottant/entier -- voir les descendants des B6500 chez l'autre ancetre
d'Unisys -- mais cette architecture cadre tres mal avec toutes les contraintes explicites du C, sans compter
les us et coutumes, je serais curieux de voir un compilateur C conforme pour une quelconque de ces deux architectures
et comment il s'integre avec le reste du systeme, meme s'il est toujours possible de se les procurer chez Unisys.  Et a part
les necessites de compatibilite avec le passe, je ne vois pas de bonnes raisons de ne pas faire du complement a 2 pour
les entiers, et je vois un tas de bonnes raisons pour le faire.

Reply

Marsh Posté le 14-04-2009 à 13:22:25    

Je pose la question car j'utilise ~0 et autres trucvs du meme style dans ma bibliothqèue de calcul numerique et je me demandait si ca cachait aps des loups. Comme je supporte que les archi/compilos post-modernes, ca devrait aller.

Reply

Marsh Posté le 14-04-2009 à 14:55:41    

Joel F a écrit :

Je pose la question car j'utilise ~0 et autres trucvs du meme style dans ma bibliothqèue de calcul numerique et je me demandait si ca cachait aps des loups. Comme je supporte que les archi/compilos post-modernes, ca devrait aller.


 
~0 ?  J'utiliserais -1 ou ~0U suivant le contexte, mais en general je prefere reserver les manipulations de bits aux unsigned; les utiliser avec des signed a des possibilites de problemes un peu plus nombreuses que simplement celles dues aux representations etranges (en particulier avec les decalages).

Reply

Marsh Posté le 14-04-2009 à 15:22:52    

en fait j'utilise ~0 comme manière géénrique de renvoyait un entier de taille qqconque avec tt les bits à un au lieu d'enumérer 0xFF, 0xFFFF etc

Reply

Marsh Posté le 14-04-2009 à 15:37:51    

Joel F a écrit :

en fait j'utilise ~0 comme manière géénrique de renvoyait un entier de taille qqconque avec tt les bits à un au lieu d'enumérer 0xFF, 0xFFFF etc


La méthode officielle est ((T)-1)

 

T étant le type.

Message cité 1 fois
Message édité par Emmanuel Delahaye le 14-04-2009 à 15:38:09

---------------
Des infos sur la programmation et le langage C: http://www.bien-programmer.fr Pas de Wi-Fi à la maison : http://www.cpl-france.org/
Reply

Marsh Posté le 14-04-2009 à 15:42:27    

je note

Reply

Marsh Posté le 14-04-2009 à 15:52:10    

Si je m'interesse a la representation (ce que "tous les bits a un" me laisse croire), j'utilise des unsigned.  Donc ~0U, ~0UL, ~0ULL.

Reply

Marsh Posté le 14-04-2009 à 15:56:53    

Emmanuel Delahaye a écrit :


La méthode officielle est ((T)-1)
 
T étant le type.


 
Pour un type non signe, ~0U a le meme resultat et je vais utiliser l'un ou l'autre suivant le contexte (c'est pour faire en sorte que le +1 amene a 0, j'utilise -1U, si je pense aux bits, j'utilise ~0U).  Et pour un type signe, ta methode ne va pas avoir le bon motif de bits dans les cas rares de representations grandeur et signe (ou il faudrait utiliser -MAX_INT) et complement a un (ou il faudrait utiliser -0) tandis que ~0 va l'avoir.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed