82 votes

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 reçues de cette question il semble que le C++ ait hérité de cette exigence de conversion de l'expression short en int lors de l'exécution d'opérations arithmétiques à partir du langage C. Puis-je vous demander ce que vous savez à ce sujet ? pourquoi ce qui a été introduit dans le C en premier lieu ? Pourquoi ne pas simplement effectuer 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 de int .

7 votes

@Jefffrey La promotion intégrale fait partie des conversions arithmétiques habituelles. short s=1, t=2; auto x = s+t; puis x est un int .

0 votes

0 votes

IMO : Il y avait très peu à gagner en obligeant un compilateur conforme à effectuer des calculs sur des nombres entiers sous la forme d'un nombre court (ou d'un caractère). En l'absence de demande, pourquoi l'exiger ?

46voto

Shafik Yaghmour Points 42198

Si l'on considère le Raison d'être de la norme internationale sur les langages de programmation-C en section 6.3.1.8 Conversions arithmétiques habituelles il est dit ( l'accent est mis sur la mienne pour l'avenir ) :

Les règles de la norme pour ces conversions de celles de K&R : les modifications tiennent compte de l'ajout des et les règles de préservation de la valeur. La licence explicite était d'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 plus rapide, sans parler du fait que la bonne réponse est plus souvent obtenue. . Les calculs peuvent également être peuvent également être effectués dans un type "plus étroit" par la règle du "comme si" tant que le même résultat final est obtenu. résultat final est obtenu. La distribution explicite peut toujours être utilisée pour obtenir une valeur dans un type désiré

Section 6.3.1.8 de la projet de norme C99 couvre les Conversions arithmétiques habituelles qui s'applique aux opérandes des expressions arithmétiques, par exemple la section 6.5.6 Opérateurs additifs dit :

Si les deux opérandes sont de type arithmétique, la fonction u conversions sont effectués sur eux.

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

Si un int peut représenter toutes les valeurs du type original, l convertie en int ; sinon, elle est co C'est ce qu'on appelle les promotions d'entiers . 48) Tous les autres types sont inchangés par les promotions d'entiers.

La discussion de la section 6.3.1.1 de la Raison d'être de la norme internationale sur les langages de programmation-C sur promotions sur les nombres entiers est en fait plus intéressante, je vais la citer de manière sélective car elle est trop longue pour être citée entièrement :

Les mises en œuvre se répartissent comme suit deux camps principaux qui peuvent être des caractères comme préservation des valeurs non signées et préservation des valeurs .

[...]

En approche de préservation non signée appelle à la promotion de la t en unsigned int. Il s'agit d'une règle simple, qui permet d'obtenir un indépendant de l'environnement d'exécution.

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

Cela peut avoir des résultats plutôt inattendus dans certains cas, par exemple Comportement incohérent de la conversion implicite entre les types non signés et les types signés plus grands démontre qu'il existe de nombreux autres exemples de ce type. Dans la plupart des cas, les opérations fonctionnent comme prévu.

2 votes

Oui, il est parfois plus petit et plus rapide parce qu'il n'est pas nécessaire d'ajouter des instructions supplémentaires pour signer ou mettre à zéro les valeurs en int ou pour masquer les bits de poids fort. En x86, vous n'avez pas non plus besoin de préfixes d'instructions supplémentaires pour modifier la taille des arguments.

0 votes

Il est dommage que le raisonnement n'ait pas ajouté une règle secondaire selon laquelle si le résultat d'un opérateur additif, multiplicatif ou bit à bit est contraint à un type non signé inférieur à int l'expression se comportera comme si ses opérandes étaient également coercitifs et que l'opération était effectuée sur le type le plus petit. Il n'y a pas de cas défini qui contredirait une telle règle, mais certains compilateurs peuvent utiliser la promotion comme une excuse pour déduire qu'une déclaration telle que x*=y; (avec les deux variables unsigned short ) promet que x ne peut excéder 2147483648/an.

0 votes

Si j'ai quelque chose comme ceci int x = 1234 et char *y = &x . Représentation binaire de 1234 es 00000000 00000000 00000100 11010010 . Ma machine est little endian donc elle l'inverse et la stocke en mémoire. 11010010 00000100 00000000 00000000 Le LSB vient en premier. Maintenant la partie principale . si j'utilise printf("%d" , *p) . printf lit le premier octet 11010010 seule la sortie est -46 mais 11010010 es 210 alors pourquoi s'imprime-t-il -46 . Je suis vraiment confus, je suppose qu'une promotion char to integer fait quelque chose, mais je ne sais pas.

21voto

Phonon Points 6751

Il ne s'agit pas d'une caractéristique du langage, mais plutôt d'une limitation des architectures de processeurs physiques sur lesquels le code s'exécute. Les int en C correspond généralement à la taille d'un registre standard de l'unité centrale. 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 encore cette limitation. En d'autres termes, lorsque l'on additionne deux nombres de 8 bits, ce qui se passe en réalité dans le processeur est un type d'arithmétique de 32 bits suivi soit d'un simple masque de bits, soit d'une autre conversion de type appropriée.

4 votes

Je ne suis pas sûr qu'il y ait nécessairement un masque de bits. Le processeur effectue l'arithmétique dans sa taille de mot native, puis ne stocke que les bits inférieurs dans la mémoire. (Par ailleurs, si vous avez raison de dire que la plupart des architectures ne font que de l'arithmétique de mots, la seule exception notable, Intel, est assez largement répandue).

0 votes

@JamesKanze Vous avez raison. J'ai édité la réponse. Et oui, Intel est très en avance en ce qui concerne l'arithmétique optimisée, en particulier avec ses bibliothèques IPP.

12 votes

Je ne suis pas d'accord avec l'expression "ce n'est pas une caractéristique de la langue" ; c'est es une caractéristique de la langue. Il est défini ainsi parce que ... mais il est défini par le langage, pas par le processeur.

18voto

6502 Points 42700

short et char sont considérés par la norme comme des "types de stockage", c'est-à-dire des sous-gammes que vous pouvez utiliser pour gagner de l'espace mais qui ne vont pas vous faire gagner de la vitesse parce que leur taille n'est pas "naturelle" pour le CPU.

Sur certains processeurs, ce n'est pas vrai, mais les bons compilateurs sont suffisamment intelligents pour remarquer que si, par exemple, vous ajoutez une constante à un caractère non signé et que vous stockez le résultat dans un caractère non signé, il n'est pas nécessaire de passer par l'étape de l'analyse. unsigned char -> int conversion. Par exemple, avec g++, le code généré pour la boucle intérieure 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ù l'on voit qu'une instruction d'addition de caractères non signés ( addb ) est utilisé.

Il en va de même si vous effectuez vos calculs entre des ints courts et que vous stockez le résultat dans des ints courts.

8voto

ssube Points 8838

La question posée semble couvrir assez bien le problème : l'unité centrale ne le fait tout simplement pas. Les opérations arithmétiques natives d'un processeur 32 bits sont configurées pour des registres 32 bits. Le processeur préfère travailler dans sa taille préférée, et pour des opérations comme celle-ci, copier une petite valeur dans un registre de taille native est peu coûteux. (Pour l'architecture x86, les registres 32 bits sont nommés comme s'ils étaient des versions étendues des registres 16 bits ( eax a ax , ebx a bx ) ; voir Instructions sur les nombres entiers x86 ).

Pour certaines opérations extrêmement courantes, en particulier l'arithmétique vectorielle/float, il peut exister des instructions spécialisées qui opèrent 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 en termes de performances et l'ajout d'instructions spécialisées ne vaut probablement pas la peine de consacrer du temps ou de l'espace à la puce (si vous voulez vraiment comprendre pourquoi ; je ne suis pas sûr qu'elles prendraient de l'espace réel, mais cela devient beaucoup plus complexe).

2 votes

Il ne s'agit pas d'un problème purement matériel, il y a eu un choix conscient lors de la rédaction de la norme C99 pour que les promotions d'entiers fonctionnent d'une manière spécifique.

5 votes

"Notez que les registres 32 bits sont également nommés comme s'ils étaient des versions étendues des registres 16 bits (eax à ax, ebx à bx, etc.)". pas correct pour la plupart des autres architectures. Les registres MIPS portent le même nom, qu'ils soient en mode 32 ou 64 bits, et ils fonctionnent toujours dans la taille native, de sorte que vous ne pouvez de toute façon pas faire d'arithmétique en 8 ou 16 bits.

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