web-dev-qa-db-fra.com

Est-ce que sizeof (enum) == sizeof (int), toujours?

Est-ce que sizeof (enum) == sizeof (int), toujours?

  • Ou est-il dépendant du compilateur?
  • Est-il faux de dire que le compilateur est optimisé pour la longueur des mots (alignement de la mémoire), c'est-à-dire que y int est la taille du mot sur un compilateur particulier? Cela signifie-t-il qu'il n'y a pas de pénalité de traitement si j'utilise des énumérations, car elles seraient alignées sur Word?
  • N'est-il pas préférable de mettre tous les codes de retour dans une énumération, car je ne m'inquiète clairement pas des valeurs qu'il obtient, uniquement des noms lors de la vérification des types de retour. Si c'est le cas, #DEFINE ne sera pas meilleur car cela économiserait de la mémoire.

Quelle est la pratique habituelle? Si je dois transporter ces types de retour sur un réseau et qu'un traitement doit être effectué à l'autre extrémité, que préféreriez-vous enums/# define/const ints.

EDIT - Il suffit de vérifier sur le net, car complier ne lie pas symboliquement les macros, comment les gens déboguent-ils ensuite, comparent-ils la valeur entière avec le fichier d'en-tête?

D'après Answers —J'ajoute cette ligne ci-dessous, car j'ai besoin de clarifications—

"Il est donc défini par l'implémentation, et sizeof (enum) peut être égal à sizeof (char), c'est-à-dire 1."

  • Cela ne signifie-t-il pas que le compilateur vérifie la plage de valeurs dans les énumérations, puis affecte la mémoire. Je ne pense pas, bien sûr que je ne sais pas. Quelqu'un peut-il m'expliquer ce qui est "pourrait être"?.
58
Vivek Sharma

Il dépend du compilateur et peut différer entre les énumérations. Voici la sémantique

enum X { A, B };

// A has type int
assert(sizeof(A) == sizeof(int));

// some integer type. Maybe even int. This is
// implementation defined. 
assert(sizeof(enum X) == sizeof(some_integer_type));

Notez que "certains types entiers" dans C99 peuvent également inclure des types entiers étendus (que l'implémentation doit cependant documenter, si elle les fournit). Le type de l'énumération est un type qui peut stocker la valeur de n'importe quel énumérateur (A et B dans ce cas).

Je ne pense pas qu'il y ait de pénalités dans l'utilisation des énumérations. Les énumérateurs sont également des expressions constantes intégrales (vous pouvez donc les utiliser pour initialiser des variables d'étendue statiques ou de fichiers, par exemple), et je les préfère aux macros chaque fois que possible.

Les énumérateurs n'ont besoin d'aucune mémoire d'exécution. Ce n'est que lorsque vous créez une variable du type énumération, que vous pouvez utiliser la mémoire d'exécution. Considérez simplement les énumérateurs comme des constantes de temps de compilation.

J'utiliserais simplement un type qui peut stocker les valeurs de l'énumérateur (je devrais connaître la plage approximative des valeurs à l'avance), le transtyper et l'envoyer sur le réseau. De préférence, le type doit être à largeur fixe, comme int32_t, donc il n'y a pas de conflits lorsque différentes machines sont impliquées. Ou j'imprime le numéro et le numérise de l'autre côté, ce qui élimine certains de ces problèmes.


Réponse à l'édition

Eh bien, le compilateur n'est pas obligé d'utiliser n'importe quelle taille. Une chose facile à voir est que le signe des valeurs importe - les types non signés peuvent avoir une augmentation significative des performances dans certains calculs. Voici le comportement de GCC 4.4.0 sur ma boîte

int main(void) {
  enum X { A = 0 };
  enum X a; // X compatible with "unsigned int"
  unsigned int *p = &a;
}

Mais si vous attribuez un -1, puis GCC choisit d'utiliser int comme type compatible avec X

int main(void) {
  enum X { A = -1 };
  enum X a; // X compatible with "int"
  int *p = &a;
}

Utilisation de l'option --short-enums de GCC, ce qui lui fait utiliser le plus petit type correspondant toujours à toutes les valeurs.

int main() {
  enum X { A = 0 };
  enum X a; // X compatible with "unsigned char"
  unsigned char *p = &a;
}
34

C99, 6.7.2.2p4 dit

Chaque type énuméré doit être compatible avec char, un type entier signé ou un type entier non signé. Le choix du type est défini par l'implémentation, 108) mais doit être capable de représenter les valeurs de tous les membres de l'énumération. [...]

La note de bas de page 108 ajoute

Une implémentation peut retarder le choix du type d'entier jusqu'à ce que toutes les constantes d'énumération aient été vues.

Il est donc défini par l'implémentation, et sizeof (enum) peut être égal à sizeof (char), c'est-à-dire 1.

En choisissant la taille d'une petite plage d'entiers, il y a toujours une pénalité. Si vous le réduisez en mémoire, il y a probablement une pénalité de traitement; si vous l'agrandissez, il y a une pénalité d'espace. C'est un compromis espace-temps.

Les codes d'erreur sont généralement #defines, car ils doivent être extensibles: différentes bibliothèques peuvent ajouter de nouveaux codes d'erreur. Vous ne pouvez pas faire cela avec des énumérations.

19
Martin v. Löwis

Est-ce que sizeof (enum) == sizeof (int), toujours

La norme ANSI C dit:

Chaque type énuméré doit être compatible avec char, un type entier signé ou un type entier non signé. Le choix du type est défini par l'implémentation. (6.7.2.2 Spécificateurs d'énumération)

Je suppose donc que cela signifie non.

Si c'est le cas, #DEFINE ne sera pas meilleur car cela économiserait de la mémoire.

De quelle manière l'utilisation de définir définit-elle la sauvegarde de la mémoire par rapport à l'utilisation d'une énumération? Une énumération est juste un type qui vous permet de fournir plus d'informations au compilateur. Dans l'exécutable résultant, il est simplement converti en un entier, tout comme le préprocesseur convertit une macro créée avec # define in en sa valeur.

Quelle est la pratique habituelle. Si je dois transporter ces types de retour sur un réseau et qu'un traitement doit être effectué à l'autre extrémité

Si vous prévoyez de transporter des valeurs sur un réseau et de les traiter à l'autre extrémité, vous devez définir un protocole. Décidez de la taille en bits de chaque type, de l'endianisme (dans quel ordre les octets sont) et assurez-vous de respecter cela dans le code client et serveur. Ne vous contentez pas non plus de supposer que parce que cela fonctionne, vous avez raison. Il se peut que l'endianisme, par exemple, sur les plates-formes client et serveur choisies corresponde, mais ce n'est pas toujours le cas.

13
IRBMe

Non.

Exemple: Le compilateur CodeSourcery

Lorsque vous définissez une énumération comme celle-ci:

enum MyEnum1 {
A=1,
B=2,
C=3
};
// will have the sizeof 1 (fits in a char)

enum MyEnum1 {
A=1,
B=2,
C=3,
D=400
};
// will have the sizeof 2 (doesn't fit in a char)

Détails de leur liste de diffusion

4
INS

Sur certains compilateurs, la taille d'une énumération dépend du nombre d'entrées dans l'énumération. (moins de 255 entrées => octet, plus de 255 entrées int) Mais cela dépend du compilateur et des paramètres du compilateur.

2
nuriaion