web-dev-qa-db-fra.com

Pourquoi un short doit-il être converti en int avant les opérations arithmétiques en C et C ++?

D'après les réponses que j'ai obtenues de cette question , il semble que C++ a hérité de cette exigence pour la conversion de short en int lors de l'exécution d'opérations arithmétiques de C. Puis-je choisir votre cerveaux quant à pourquoi cela a été introduit en C en premier lieu? Pourquoi ne pas simplement faire ces opérations en tant que short?

Par exemple ( tiré de la suggestion de dyp dans les commentaires):

short s = 1, t = 2 ;
auto  x = s + t ;

x aura le type int.

68
dayuloli

Si nous regardons le Justification de la norme internationale — Langages de programmation — C dans la section 6.3.1.8 Conversions arithmétiques habituelles il est dit ( le mien est à souligner):

Les règles de la norme pour ces conversions sont de légères modifications de celles de K&R: les modifications tiennent compte des types ajoutés et des règles de préservation de la valeur. Une licence explicite a été ajoutée pour effectuer des calculs dans un type "plus large" qu'il n'est absolument nécessaire, car cela peut parfois produire un code plus petit et plus rapide, sans parler de la bonne réponse plus souvent . Les calculs peuvent également être effectués dans un type "plus étroit" par la règle comme si tant que le même résultat final est obtenu. La conversion explicite peut toujours être utilisée pour obtenir une valeur dans le type souhaité

Section 6.3.1.8 du projet de norme C99 couvre le Conversions arithmétiques habituelles qui est appliqué aux opérandes d'expressions arithmétiques par exemple la section - 6.5.6 Opérateurs additifs dit:

Si les deux opérandes ont un type arithmétique, les conversions arithmétiques habituelles sont effectuées sur eux.

Nous trouvons également un texte similaire dans la section 6.5.5 Opérateurs multiplicatifs . Dans le cas d'un opérande court, les promotions entières sont d'abord appliquées à partir de la section 6.3.1.1 Booléen, caractères et entiers qui dit:

Si un int peut représenter toutes les valeurs du type d'origine, la valeur est convertie en int; sinon, il est converti en un entier non signé. Celles-ci sont appelées promotions entières .48) Tous les autres types sont inchangés par les promotions entières.

La discussion de la section 6.3.1.1 du Justification ou Norme internationale — Langages de programmation — C sur promotions entières est en fait plus intéressant, je vais citer sélectivement b/c c'est trop long pour citer complètement:

Les implémentations sont tombées dans deux camps majeurs qui peuvent être caractérisés comme préservation non signée et préservation de la valeur .

[...]

L'approche de conservation non signée appelle à promouvoir les deux types non signés plus petits en entier non signé. Il s'agit d'une règle simple qui donne un type indépendant de l'environnement d'exécution.

L'approche de conservation de la valeur appelle à la promotion de ces types en entier signé si ce type peut représenter correctement toutes les valeurs du type d'origine, et sinon à la promotion de ces types à int. non signé Ainsi, si l'environnement d'exécution représente short comme quelque chose de plus petit que int, short non signé devient int; sinon, il devient non signé int.

Cela peut avoir des résultats plutôt inattendus dans certains cas, comme Comportement incohérent de conversion implicite entre les types signés non signés et les plus grands le montre, il existe de nombreux autres exemples comme celui-ci. Bien que dans la plupart des cas, les opérations fonctionnent comme prévu.

39
Shafik Yaghmour

Ce n'est pas une caractéristique du langage autant que c'est une limitation des architectures de processeur physique sur lesquelles le code s'exécute. Le typer int en C est généralement la taille de votre registre CPU standard. Plus de silicium prend plus d'espace et plus de puissance, de sorte que dans de nombreux cas, l'arithmétique ne peut être effectuée que sur les types de données de "taille naturelle". Ce n'est pas universellement vrai, mais la plupart des architectures ont toujours cette limitation. En d'autres termes, lors de l'ajout de deux nombres à 8 bits, ce qui se passe réellement dans le processeur est un type d'arithmétique à 32 bits suivi soit d'un simple masque de bits, soit d'une autre conversion de type appropriée.

21
Phonon

Les types short et char sont considérés par le type standard de "types de stockage", c'est-à-dire des sous-plages que vous pouvez utiliser pour économiser de l'espace mais qui ne vous permettront pas de gagner de la vitesse car leur taille est "contre nature" pour le CPU.

Sur certains processeurs, ce n'est pas vrai, mais les bons compilateurs sont suffisamment intelligents pour remarquer que si vous par exemple ajouter une constante à un caractère non signé et stocker le résultat dans un caractère non signé, il n'est pas nécessaire de passer par le unsigned char -> int conversion. Par exemple, avec g ++, le code généré pour la boucle interne de

void incbuf(unsigned char *buf, int size) {
    for (int i=0; i<size; i++) {
        buf[i] = buf[i] + 1;
    }
}

est juste

.L3:
    addb    $1, (%rdi,%rax)
    addq    $1, %rax
    cmpl    %eax, %esi
    jg  .L3
.L1:

où vous pouvez voir qu'une instruction d'ajout de caractère non signé (addb) est utilisée.

La même chose se produit si vous effectuez vos calculs entre des entrées courtes et stockez le résultat dans des entrées courtes.

17
6502

La question liée semble le couvrir assez bien: le CPU ne le fait tout simplement pas. Un processeur 32 bits a ses opérations arithmétiques natives configurées pour les registres 32 bits. Le processeur préfère travailler dans sa taille préférée, et pour des opérations comme celle-ci, la copie d'une petite valeur dans un registre de taille native est bon marché. (Pour l'architecture x86, les registres 32 bits sont nommés comme s'il s'agissait de versions étendues des registres 16 bits (eax à ax, ebx à bx, etc.); voir instructions entières x86 ).

Pour certaines opérations extrêmement courantes, en particulier l'arithmétique vectorielle/flottante, il peut y avoir des instructions spécialisées qui fonctionnent sur un type ou une taille de registre différent. Pour quelque chose comme un court, le remplissage avec (jusqu'à) 16 bits de zéros a très peu de coût de performance et l'ajout d'instructions spécialisées ne vaut probablement pas le temps ou l'espace sur le dé (si vous voulez vraiment comprendre pourquoi; je suis je ne sais pas s'ils prendraient de la place, mais cela devient beaucoup plus complexe).

7
ssube