Avantages / inconvénients d’utiliser char pour de petits entiers en C

Y at-il un inconvénient à utiliser char pour les petits entiers en C? Existe-t-il d’autres avantages que l’avantage d’occupation / de mémoire?

En particulier, le processeur est-il susceptible de gérer l’arithmétique entière sur un caractère mieux ou moins bon que sur un entier ( long / short )?

Je sais que ce sera spécifique au processeur / système / compilateur, mais j’espère une réponse dans le cas général, ou du moins dans le cas général de Windows 32 bits et de Solaris, étant les systèmes sur lesquels je travaille actuellement. . Je suppose également que des problèmes tels que les problèmes de débordement / enveloppement ont déjà été traités.

Mise à jour: Visual Studio 6.0 n’a pas réellement stdint.h comme suggéré par Christoph. Un peu d’parsing comparative sur Windows (VS 6.0, version de débogage, 32 bits) avec une poignée de boucles empilées donne des performances int et long offrant des performances similaires, soit environ deux fois plus vite que char . Exécuter le même test sous Linux avec gcc de la même manière pegs int et long similar, et les deux plus rapidement que char , bien que la différence soit moins marquée.

En passant, je n’ai pas passé beaucoup de temps à chercher, mais la première implémentation de stdint.h pour VS 6.0 que j’ai trouvée (via Wikipedia ) définit uint_fast8_t comme étant un caractère unsigned char , bien que cela semble au moins être plus lent dans mes tests. Ainsi, comme l’a suggéré à juste titre Christoph, la morale de l’histoire: toujours une référence!

C99 a ajouté les types d’entiers de largeur minimale dits “les plus rapides” pour résoudre ce problème. Pour la plage qui vous intéresse, les types seraient int_fast8_t et uint_fast8_t , qui peuvent être trouvés dans stdint.h .

N’oubliez pas qu’il peut ne pas y avoir de gain de performances (l’augmentation de la consommation de mémoire peut même ralentir les choses); comme toujours, sharepoint repère! Ne optimisez pas prématurément ou uniquement sur des hypothèses potentiellement erronées de ce qui devrait fonctionner.

Eh bien, le premier problème est que la norme C ne définit pas si le caractère brut est signé ou non signé – de sorte que la seule plage sur laquelle vous pouvez compter de manière portable est comprise entre 0 et 127.

En dehors de cela, int est généralement supposé être du type correspondant à la taille de mot native de l’architecture (mais bien sûr, cela n’est pas imposé par rien). C’est le type qui offre la meilleure performance arithmétique, mais c’est à peu près tout ce que vous pouvez dire.

Notez que les opérandes plus étroits int sont élargis à int ou unsigned int lors de l’évaluation de l’expression.

Un autre inconvénient auquel je peux penser est que (autant que je sache), les processeurs “modernes” effectuent toutes leurs calculs en entiers “complets”, généralement 32 bits. Ainsi, traiter avec un caractère signifie généralement extraire un seul octet de la mémoire, remplir des zéros pour le transférer dans un registre, en faire quelque chose et ensuite ne réinsérer que les bits les moins significatifs du résultat dans la mémoire. Surtout si le caractère n’est pas aligné sur une limite pratique, cet access à la mémoire demande beaucoup plus de travail.

L’utilisation de char pour int n’est vraiment utile que lorsque vous avez beaucoup de nombres (c’est-à-dire un grand tableau) et que vous devez économiser de l’espace.

L’arithmétique sur les caractères sera probablement exécutée en utilisant les mêmes registres que l’arithmétique sur les caractères. Par exemple:

 char c1 = 1; char c2 = c1 + 2; 

L’addition est compilée comme suit avec VC ++:

 00401030 movsx eax,byte ptr [ebp-4] 00401034 add eax,2 00401037 mov byte ptr [ebp-0Ch],al 

où eax est un registre 32 bits.

Il n’ya donc pas d’avantage à utiliser des caractères sur des performances en termes de performances arithmétiques.

En interne, les processeurs effectuent généralement des opérations arithmétiques sur des mots machine. Cela signifie que lorsque des calculs sur d’autres types sont effectués, bien que le calcul lui-même prenne la même durée, en fonction du jeu d’instructions disponible, un travail supplémentaire peut être effectué pour lire les entrées et pour contraindre les résultats des calculs dans le type cible (par exemple: remplissage / mise à zéro des signes, décalage / masquage pour éviter les access mémoire non alignés, etc.).

C est pour cette raison que C définit les types et les opérations comme il le fait – la taille de int n’est pas prescrite par la norme, ce qui permet aux auteurs du compilateur de le faire correspondre à un mot machine. le nombre de points auxquels les résultats doivent être forcés à un type de cible.

Les raisons valables d’utiliser char pour stocker des valeurs entières sont lorsque l’espace importe vraiment (pas aussi souvent qu’on pourrait le penser) et lorsque vous décrivez un format / protocole de données externe pour lequel vous organisez des données. Attendez-vous à ce que les utilisations de char entraînent une légère perte de performances, en particulier sur du matériel tel que Cell SPU où seuls les access mémoire en taille de mot machine sont disponibles. L’access à un caractère en mémoire nécessite donc plusieurs décalages et masques.

Le principal inconvénient que je constaterais, c’est que votre code utilise un type qui signifie une chose pour des valeurs qui signifient autre chose – par exemple, il y a un problème sémantique qui pourrait être un problème de maintenance. Si vous le faisiez, je vous conseillerais probablement de taper:

 typedef char REALLYSHORT; 

De cette façon, A), ce que vous faites est plus clair, et B) Vous pouvez le changer facilement (par exemple, un seul endroit) si vous rencontrez des difficultés.

Avez-vous une très bonne raison de ne pas utiliser int ?