J’ai beaucoup de code qui effectue des opérations au niveau des bits sur des entiers non signés. J’ai écrit mon code en supposant que ces opérations étaient effectuées sur des entiers de largeur fixe sans aucun bit de remplissage. Par exemple, un tableau d’entiers non signés de 32 bits dont les 32 bits sont disponibles pour chaque entier.
Je cherche à rendre mon code plus portable et à veiller à ce que je sois conforme à C89 (dans ce cas). L’un des problèmes que j’ai rencontré concerne les entiers matelassés possibles. Prenez cet exemple extrême, tiré du manuel GMP :
Cependant, sur les systèmes de vecteurs Cray, il est à noter que short et int sont toujours stockés sur 8 octets (et avec sizeof l’indiquant) mais n’utilisent que 32 ou 46 bits. La fonction clous peut en tenir compte en passant par exemple 8 * sizeof (int) -INT_BIT.
J’ai aussi entendu parler de ce type de rembourrage ailleurs. En fait, j’ai lu un article sur SO hier soir (pardonnez-moi, je n’ai pas le lien et je vais citer quelque chose de similaire par mémoire) où, si vous avez, disons, un double avec 60 bits utilisables, les 4 autres pourraient être utilisé pour le remplissage et ces bits de remplissage pourraient avoir une fonction interne, de sorte qu’ils ne puissent pas être modifiés.
Supposons par exemple que mon code soit compilé sur une plate-forme où un type d’entier non signé est dimensionné à 4 octets, chaque octet étant de 8 bits, mais les 2 bits les plus significatifs sont des bits de remplissage. Dans ce cas, UINT_MAX serait-il 0x3FFFFFFF (1073741823)?
#include #include /* padding bits represented by underscores */ int main( int argc, char **argv ) { unsigned int a = 0x2AAAAAAA; /* __101010101010101010101010101010 */ unsigned int b = 0x15555555; /* __010101010101010101010101010101 */ unsigned int c = a ^ b; /* ?? __111111111111111111111111111111 */ unsigned int d = c <> 5; /* ?? __000001111111111111111111111111 */ printf( "a: %X\nb: %X\nc: %X\nd: %X\ne: %X\n", a, b, c, d, e ); return 0; }
Est-il sûr de XOR deux entiers avec des bits de remplissage?
Est-ce que je ne ferais pas XOR quels que soient les bits de remplissage?
Je ne trouve pas ce comportement dans C89.
de plus, est-il garanti que la variable c soit 0x3FFFFFFF ou si, par exemple, les deux bits de remplissage étaient tous les deux activés dans a ou b, c serait-il 0xFFFFFFFF?
même question avec d et e. Est-ce que je manipule les bits de remplissage en décalant? Je m’attendrais à voir ceci ci-dessous, en supposant que 32 bits avec les 2 bits les plus significatifs utilisés pour le remplissage, mais je veux savoir si quelque chose comme ceci est garanti:
a: 2AAAAAAA b: 15555555 c: 3FFFFFFF d: 3FFFFFE0 e: 01FFFFFF
Les bits de remplissage sont-ils toujours les bits les plus significatifs ou pourraient-ils être les bits les moins significatifs?
Merci les gars
EDIT 19/12/2010 17h00 HNE : Christoph a répondu à ma question. Merci!
J’ai également demandé (ci-dessus) si les bits de remplissage sont toujours les bits les plus significatifs. Ceci est cité dans la justification de la norme C99 et la réponse est non. Je joue la sécurité et assume la même chose pour C89. Voici précisément ce que dit la justification C99 pour le § 6.2.6.2 (Représentation des types d’entiers):
Les bits de remplissage sont accessibles à l’utilisateur dans un type entier non signé. Par exemple, supposons qu’une machine utilise une paire de courts-circuits de 16 bits (chacun avec son propre bit de signe) pour constituer un int de 32 bits et que le bit de signe du plus petit court est ignoré lorsqu’il est utilisé dans cet int de 32 bits. Ensuite, en tant qu’int signé 32 bits, il existe un bit de remplissage (au milieu des 32 bits) qui est ignoré lors de la détermination de la valeur de l’int signé 32 bits. Toutefois, si cet élément 32 bits est traité comme un entier non signé 32 bits, alors ce bit de remplissage est visible par le programme de l’utilisateur. On a dit au comité C qu’il existe une machine qui fonctionne de cette façon, et c’est l’une des raisons pour lesquelles des bits de remplissage ont été ajoutés à C99.
Les notes de bas de page 44 et 45 indiquent que les bits de parité peuvent être des bits de remplissage. Le comité ne connaît aucune machine avec des bits de parité accessibles à l’utilisateur dans un entier. Par conséquent, le comité n’a pas connaissance de machines traitant les bits de parité comme des bits de remplissage.
EDIT 28/12/2010 15h00 HNE : J’ai trouvé une discussion intéressante sur comp.lang.c datant d’il y a quelques mois.
Effets des opérateurs sur les bits de rembourrage (lecteur VelocityReviews)
Effets d’opérateur binary sur les bits de remplissage (lien alternatif de Google Groupes)
Un point soulevé par Dietmar que j’ai trouvé intéressant:
Notons que les bits de remplissage ne sont pas nécessaires à l’existence de représentations de pièges; des combinaisons de bits de valeur qui ne représentent pas une valeur du type d’object feraient également l’affaire.
Les opérations au niveau des bits (comme les opérations arithmétiques) agissent sur les valeurs et ignorent le remplissage. L’implémentation peut modifier ou non les bits de remplissage (ou les utiliser en interne, par exemple comme bits de parité), mais le code C portable ne pourra jamais le détecter. Toute valeur (y compris UINT_MAX
) UINT_MAX
pas le remplissage.
Si vous utilisez des éléments tels que sizeof (int) * CHAR_BIT
, puis que vous essayez d’utiliser des décalages pour accéder à tous ces bits, le remplissage de nombres entiers risque de poser des problèmes. Si vous voulez être portable, utilisez uniquement un caractère ( unsigned
), des entiers de taille fixe (un ajout de C99) ou déterminez le nombre de bits de valeur par programmation. Cela peut être fait au moment de la compilation avec le préprocesseur en comparant UINT_MAX
avec des puissances de 2 ou au moment de l’exécution en utilisant des opérations en bits.
modifier:
C90 ne mentionne pas du tout le remplissage en entier, mais autant que je sache, les bits de remplissage en entier «invisibles» précédents ou suivants ne doivent pas violer la norme (je n’ai pas parcouru toutes les sections pertinentes pour être sûr que c’est bien le cas , bien que); il y a probablement des problèmes avec les bits de remplissage et de valeur mélangés, comme indiqué dans la justification du C99, car sinon, la norme n’aurait pas besoin d’être modifiée.
Quant à la signification de accessible par l’utilisateur: Les bits de remplissage sont accessibles dans la mesure où vous pouvez toujours accéder à n’importe quel bit de foo
(y compris le remplissage) en utilisant des opérations de bits sur ((unsigned char *)&foo)[…]
. Cependant, soyez prudent lorsque vous modifiez les bits de remplissage: le résultat ne changera pas la valeur de l’entier, mais pourrait néanmoins créer une représentation d’interruption. Dans le cas de C90, ceci est implicitement non spécifié (comme dans non mentionné du tout), dans le cas de C99, il est défini par l’implémentation.
Ce n’était cependant pas l’object de la citation de justification: l’architecture citée représente des entiers 32 bits via deux entiers 16 bits. Dans le cas des types non signés, l’entier résultant a 32 bits de valeur et une précision de 32; dans le cas d’entiers signés, il n’a que 31 bits de valeur et une précision de 30: l’un des bits de signe des nombres entiers de 16 bits est utilisé comme bit de signe de l’entier de 32 bits, l’autre est ignoré, ce qui crée un bit de remplissage entouré de bits de valeur. Désormais, si vous accédez à un entier signé 32 bits sous la forme d’un entier non signé (ce qui est explicitement autorisé et ne viole pas les règles de création d’alias C99), le bit de remplissage devient un bit de valeur (accessible à l’utilisateur).