64 votes

Types de données à longueur fixe en C/C++

J'ai entendu dire que la taille de types de données tels que int peut varier selon les plateformes.

Ma première question est la suivante : quelqu'un peut-il donner un exemple de ce qui ne va pas lorsque le programme suppose un int est de 4 octets, mais sur une autre plate-forme, il est de 2 octets ?

Une autre question que j'avais est liée. Je sais que des gens résolvent ce problème avec des typedefs , comme vous avez des variables comme u8 , u16 , u32 - dont la taille est garantie à 8, 16 ou 32 bits, quelle que soit la plate-forme. Ma question est la suivante : comment cela est-il généralement réalisé ? stdint bibliothèque - je suis curieux manuellement, comment peut-on imposer qu'un type soit toujours disons 32 bits quelle que soit la plateforme ??)

37voto

BЈовић Points 28674

Je sais que les gens résolvent ce problème avec certains typedefs, comme vous avez des variables comme u8,u16,u32 - qui sont garantis d'être 8bits, 16bits, 32bits, indépendamment de la plate-forme.

Il y a des plateformes qui n'ont pas de types d'une certaine taille (comme par exemple les 28xxx de TI, où la taille des caractères est de 16 bits). Dans de tels cas, il n'est pas possible d'avoir un type de 8 bits (à moins que vous ne le vouliez vraiment, mais cela peut introduire des pertes de performance).

comment cela se fait-il habituellement ?

Habituellement avec des typedefs. c99 (et c++11) ont ces typedefs dans un en-tête . Alors, utilisez-les.

Quelqu'un peut-il donner un exemple de ce qui ne va pas, lorsque le programme suppose qu'un int est de 4 octets, mais que sur une autre plate-forme, il est de 2 octets ?

Le meilleur exemple est une communication entre des systèmes ayant des tailles de caractères différentes. L'envoi d'un tableau d'entiers d'une plate-forme à une autre, où sizeof(int) est différent sur les deux, doit être effectué avec une extrême prudence.

De même, la sauvegarde d'un tableau d'ints dans un fichier binaire sur une plateforme 32 bits, et sa réinterprétation sur une plateforme 64 bits.

21voto

paxdiablo Points 341644

Dans les versions antérieures du standard C, vous faisiez généralement vos propres typedef pour s'assurer que vous obtenez (par exemple) un type de 16 bits, sur la base de #define passées dans le compilateur par exemple :

gcc -DINT16_IS_LONG ...

De nos jours (C99 et plus), il existe des types spécifiques tels que uint16_t le nombre entier non signé de 16 bits exactement.

A condition que vous incluiez stdint.h vous obtenez des types à largeur de bit exacte, des types à largeur minimale, des types les plus rapides avec une largeur minimale donnée, etc. C99 7.18 Integer types <stdint.h> . Si une implémentation dispose de types compatibles, elle est tenue de les fournir.

Très utile également inttypes.h qui ajoute d'autres fonctionnalités intéressantes pour la conversion du format de ces nouveaux types ( printf y scanf chaînes de format).

14voto

Yu Hao Points 40603

Pour la première question : Débordement d'un nombre entier .

Pour la deuxième question : par exemple, pour typedef un entier non signé de 32 octets, sur une plate-forme qui int est de 4 octets, utilisez :

 typedef unsigned int u32;

Sur une plateforme où int est de 2 octets tandis que long est de 4 octets :

typedef unsigned long u32;

De cette façon, il suffit de modifier un seul fichier d'en-tête pour rendre les types multiplateformes.

S'il existe des macros spécifiques à une plate-forme, cela peut être réalisé sans modification manuelle :

#if defined(PLAT1)
typedef unsigned int u32;
#elif defined(PLAT2)
typedef unsigned long u32;
#endif

Si C99 stdint.h est supporté, il est préféré.

7voto

stefan Points 4987

Tout d'abord : N'écrivez jamais de programmes qui dépendent de la largeur de types comme short , int , unsigned int ,....

En gros : "ne vous fiez jamais à la largeur, si elle n'est pas garantie par la norme".

Si vous souhaitez être réellement indépendant de la plate-forme et stocker, par exemple, la valeur 33000 sous la forme d'un nombre entier signé, il vous faut ne peut pas supposez simplement qu'un int le tiendra. Un site int a au moins l'intervalle -32767 a 32767 o -32768 a 32767 (selon le complément à un/trois). Cela n'est pas suffisant, même si cela généralement est de 32 bits et est donc capable de stocker 33000. Pour cette valeur, vous avez définitivement besoin d'un >16bit donc vous choisissez simplement int32_t o int64_t . Si ce type n'existe pas, le compilateur vous indiquera l'erreur, mais ce ne sera pas une erreur silencieuse.

Deuxièmement : C++11 fournit un en-tête standard pour les types d'entiers à largeur fixe. Il n'est pas garanti que ces types existent sur votre plateforme, mais lorsqu'ils existent, ils sont garantis d'être de la largeur exacte. Voir cet article sur cppreference.com pour une référence. Les types sont nommés selon le format int[n]_t y uint[n]_t donde n es 8 , 16 , 32 o 64 . Vous devrez inclure l'en-tête <cstdint> . Le site C L'en-tête est bien sûr <stdint.h> .

6voto

thang Points 2183

En général, le problème survient lorsque vous maximisez le nombre d'unités ou lorsque vous effectuez une sérialisation. Un scénario moins courant se produit lorsque quelqu'un fait une hypothèse de taille explicite.

Dans le premier scénario :

int x = 32000;
int y = 32000;
int z = x+y;        // can cause overflow for 2 bytes, but not 4

Dans le second scénario,

struct header {
int magic;
int w;
int h;
};

puis on passe à fwrite :

header h;
// fill in h
fwrite(&h, sizeof(h), 1, fp);

// this is all fine and good until one freads from an architecture with a different int size

Dans le troisième scénario :

int* x = new int[100];
char* buff = (char*)x;

// now try to change the 3rd element of x via buff assuming int size of 2
*((int*)(buff+2*2)) = 100;

// (of course, it's easy to fix this with sizeof(int))

Si vous utilisez un compilateur relativement récent, j'utiliserais uint8_t, int8_t, etc. afin d'être sûr de la taille du type.

Dans les compilateurs plus anciens, le typedef est généralement défini sur une base par plateforme. Par exemple, on peut faire :

 #ifdef _WIN32
      typedef unsigned char uint8_t;
      typedef unsigned short uint16_t;
      // and so on...
 #endif

De cette façon, il y aurait un en-tête par plateforme qui définirait les spécificités de cette plateforme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X