web-dev-qa-db-fra.com

types de données de longueur fixe en C/C++

J'ai entendu dire que la taille des types de données tels que int peut varier d'une plate-forme à l'autre.

Ma première question est la suivante: quelqu'un peut-il apporter un exemple, ce qui ne va pas, quand programme Suppose qu'une int est de 4 octets, mais que sur une plate-forme différente, il est dit 2 octets?

Une autre question que j'ai eue est liée. Je sais que les gens résolvent ce problème avec un typedefs, .__, comme vous avez des variables comme u8, u16, u32 - qui sont garantis être 8bits, 16bits, 32bits, quelle que soit la plate-forme - ma question est de savoir comment cela est réalisé ? (Je ne fais pas référence aux types de la bibliothèque stdint - Je suis curieux manuellement, comment peut-on exiger qu'un type soit toujours dit 32 bits quelle que soit la plate-forme?)

70
user2793162

Je sais que les gens résolvent ce problème avec certains types de caractères, comme vous avez des variables telles que u8, u16, u32, garanties 8bits, 16bits, 32bits, quelle que soit la plate-forme. 

Il existe certaines plates-formes, qui n'ont pas de types d'une certaine taille (comme par exemple le 28xxx de TI, où la taille du caractère est de 16 bits). Dans de tels cas, il n'est pas possible d'avoir un type 8 bits (à moins que vous ne le souhaitiez vraiment, mais cela peut nuire aux performances).

comment cela se fait-il habituellement?

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

quelqu'un peut-il apporter un exemple, ce qui ne va pas, quand le programme suppose qu'un int est de 4 octets, mais sur une plate-forme différente, il est dit 2 octets?

Le meilleur exemple est une communication entre des systèmes de taille différente. Pour envoyer un tableau d’entités d’une plate-forme à une autre, où sizeof (int) est différent sur deux, il faut faire très attention.

Vous pouvez également enregistrer un tableau d’entités dans un fichier binaire sur une plate-forme 32 bits et le réinterpréter sur une plate-forme 64 bits.

41
BЈовић

Dans les précédentes itérations de la norme C, vous avez généralement créé vos propres instructions typedef pour vous assurer d'obtenir un type (par exemple) 16 bits, basé sur les chaînes #define passées au 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.

Si vous incluez stdint.h, vous obtenez des types de largeur de bits exacts, au moins des types de largeur, les types les plus rapides avec une largeur minimale donnée, etc., comme décrit dans C99 7.18 Integer types <stdint.h>. Si une implémentation a des types compatibles, ils doivent les fournir.

Également très utile, inttypes.h ajoute des fonctionnalités intéressantes pour la conversion de format de ces nouveaux types (chaînes de format printf et scanf).

22
paxdiablo

Pour la première question: Débordement d’entier .

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

 typedef unsigned int u32;

Sur une plate-forme où int est 2 octets tandis que long est 4 octets:

typedef unsigned long u32;

De cette manière, il vous suffit de modifier un seul fichier d'en-tête pour rendre les types multi-plateformes. 

S'il existe des macros spécifiques à la plate-forme, vous pouvez le faire sans modifier manuellement:

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

Si C99 stdint.h est pris en charge, c'est préférable.

16
Yu Hao

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

Fondamentalement: "ne comptez jamais sur la largeur, si elle n'est pas garantie par la norme".

Si vous voulez être vraiment indépendant de la plateforme et stocker, par exemple, la valeur 33000 sous forme d’entier signé, vous ne pouvez pas présumer qu’une int le conservera. Une int a au moins la plage -32767 à 32767 ou -32768 à 32767 (selon le complément un/deux). Cela ne suffit tout simplement pas, même si habituellement a 32 bits et est donc capable de stocker 33 000. Pour cette valeur, vous avez définitivement besoin d'un type >16bit, vous devez donc simplement choisir int32_t ou int64_t. Si ce type n'existe pas, le compilateur vous dira l'erreur, mais ce ne sera pas une erreur silencieuse.

Deuxièmement: C++ 11 fournit un en-tête standard pour les types entiers de largeur fixe. Aucune de celles-ci n'est garantie sur votre plate-forme, mais lorsqu'elles existent, leur largeur exacte est garantie. Voir cet article sur cppreference.com pour une référence. Les types sont nommés au format int[n]_t et uint[n]_t, où n est 8, 16, 32 ou 64. Vous devrez inclure l'en-tête <cstdint>. L'en-tête C est bien sûr <stdint.h>.

7
stefan

en général, le problème se produit lorsque vous utilisez le nombre maximum ou lorsque vous effectuez une sérialisation. Un scénario moins courant se produit lorsque quelqu'un pose 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 deuxième scénario,

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

alors on va écrire:

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 nouveau, j’utiliserais uint8_t, int8_t, etc. 

Dans les anciens compilateurs, typedef est généralement défini par plate-forme. Par exemple, on peut faire:

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

De cette manière, il y aurait un en-tête par plate-forme qui définit les spécificités de cette plate-forme.

6
thang

Je suis curieux manuellement, comment peut-on exiger que certains types soient toujours 32 bits, quelle que soit la plate-forme?

Si vous voulez que la compilation de votre programme C++ (moderne) échoue si un type donné ne correspond pas à la largeur que vous attendez, ajoutez un static_assert quelque part. J'ajouterais ceci autour des hypothèses sur la largeur du type.

static_assert(sizeof(int) == 4, "Expected int to be four chars wide but it was not.");

chars sur les plates-formes les plus couramment utilisées ont une taille de 8 bits, mais toutes les plates-formes ne fonctionnent pas de cette façon. 

5
chwarr

Bien, premier exemple - quelque chose comme ça:

int a = 45000; // both a and b 
int b = 40000; // does not fit in 2 bytes.
int c = a + b; // overflows on 16bits, but not on 32bits

Si vous examinez l'en-tête cstdint, vous découvrirez comment sont définis tous les types de taille fixe (int8_t, uint8_t, etc.) - et le fichier d'en-tête est la seule différence. Donc, sur une architecture, int16_t pourrait être:

 typedef int int16_t;

et sur un autre:

 typedef short int16_t;

En outre, il existe d'autres types, qui peuvent être utiles, tels que: int_least16_t

3
Nemanja Boric

quelqu'un peut-il apporter un exemple, ce qui ne va pas, quand le programme suppose qu'un int est de 4 octets, mais sur une plate-forme différente, il est dit 2 octets?

Supposons que vous avez conçu votre programme pour lire 100 000 entrées et que vous le comptez à l'aide d'un unsigned int en supposant une taille de 32 bits (les entiers non signés en 32 bits peuvent compter jusqu'à 4 294 967 295). Si vous compilez le code sur une plate-forme (ou un compilateur) avec des nombres entiers de 16 bits (les entrées non signées de 16 bits ne peuvent compter que jusqu'à 65 535), la valeur dépassera les 65 535 en raison de la capacité et indique un nombre incorrect.

2
legends2k
  1. Si un type est plus petit que vous le pensez, il risque de ne pas pouvoir stocker une valeur que vous devez y stocker.
  2. Pour créer des types de taille fixe, lisez la documentation relative aux plates-formes à prendre en charge, puis définissez typedefs en fonction de #ifdef pour les plates-formes spécifiques.
2
Juraj Blaho

Les compilateurs sont responsables d'obéir à la norme. Lorsque vous incluez <cstdint> ou <stdint.h>, ils doivent fournir les types en fonction de la taille standard.

Les compilateurs savent qu'ils compilent le code de quelle plate-forme. Ils peuvent ensuite générer des macros internes ou de la magie pour créer le type approprié. Par exemple, un compilateur sur une machine 32 bits génère la macro __32BIT__. Auparavant, il contient ces lignes dans le fichier d'en-tête stdint:

#ifdef __32BIT__
typedef __int32_internal__ int32_t;
typedef __int64_internal__ int64_t;
...
#endif

et vous pouvez l'utiliser.

1
deepmax

les drapeaux sont l'exemple trivial. 0x10000 vous causera des problèmes, vous ne pouvez pas masquer ou vérifier si un bit est défini dans cette 17ème position si tout est tronqué ou écrasé pour tenir sur 16 bits.

0
jheriko