Dans un devoir récent, on m'a dit d'utiliser long
variable pour stocker un résultat, car il peut s'agir d'un grand nombre.
J'ai décidé de vérifier si cela importait vraiment pour moi sur mon système (compilateur intel core i5/64-bit Windows 7/gnu gcc) et j'ai découvert que le code suivant:
printf("sizeof(char) => %d\n", sizeof(char));
printf("sizeof(short) => %d\n", sizeof(short));
printf("sizeof(short int) => %d\n", sizeof(short int));
printf("sizeof(int) => %d\n", sizeof(int));
printf("sizeof(long) => %d\n", sizeof(long));
printf("sizeof(long int) => %d\n", sizeof(long int));
printf("sizeof(long long) => %d\n", sizeof(long long));
printf("sizeof(long long int) => %d\n", sizeof(long long int));
produit la sortie suivante:
sizeof(char) => 1
sizeof(short) => 2
sizeof(short int) => 2
sizeof(int) => 4
sizeof(long) => 4
sizeof(long int) => 4
sizeof(long long) => 8
sizeof(long long int) => 8
En d’autres termes, sur mon système, int
et long
sont identiques, et tout ce qui sera trop gros pour que int
puisse tenir, sera trop gros pour long
à tenir aussi.
Le devoir en tant que tel n’est pas le problème ici. Je me demande comment, sur un système où int < long
, devrais-je assigner un int
à long?
Je suis conscient du fait que làsontnombreuxétroitementliés questions sur sur ce sujet, mais j’ai le sentiment que les réponses à ces questions ne m’offrent pas une compréhension complète de ce qui se passera ou pourrait se produire au cours du processus.
Fondamentalement, j'essaie de comprendre ce qui suit:
long
en int
avant l'affectation, ou long
n'est pas un type de données différent, mais simplement un modificateur,long > int
? Le résultat sera-t-il indéfini (ou imprévisible) ou les parties supplémentaires de la variable seront-elles omises?long
à int
fonctionne-t-il en C?long
à int
fonctionne-t-elle en C lorsque je n'utilise pas de transtypage?Le langage garantit que int
est au moins 16 bits, long
est au moins 32 bits et long
peut représenter au moins toutes les valeurs que int
peut représenter.
Si vous attribuez une valeur long
à un objet int
, elle sera convertie implicitement. Il n'y a pas besoin de casting explicite; cela indiquerait simplement la même conversion qui va se produire de toute façon.
Sur votre système, où int
et long
ont la même taille et la même plage, la conversion est triviale; il copie simplement la valeur.
Sur un système où long
est plus large que int
, si la valeur ne tient pas dans un int
, le résultat de la conversion est défini par l'implémentation. (Ou, à partir de C99, cela peut générer un signal défini par l'implémentation, mais je ne connais aucun compilateur qui le fasse réellement.) Quoi en général est que les bits de poids fort sont rejetés, mais vous ne devriez pas en dépendre. (Les règles sont différentes pour les types non signés; le résultat de la conversion d'un entier signé ou non signé en un type non signé est bien défini.)
Si vous devez en toute sécurité attribuer une valeur long
à un objet int
, vous pouvez vérifier qu'il tiendra avant faire la mission:
#include <limits.h> /* for INT_MIN, INT_MAX */
/* ... */
int i;
long li = /* whatever */
if (li >= INT_MIN && li <= INT_MAX) {
i = li;
}
else {
/* do something else? */
}
Les détails de "quelque chose d'autre" vont dépendre de ce que vous voulez faire.
Une correction: int
et long
sont toujours des types distincts, même s’ils ont la même taille et la même représentation. . Les types arithmétiques sont librement convertibles, donc souvent cela ne fait aucune différence, mais par exemple int*
et long*
sont des types distincts et incompatibles; vous ne pouvez pas affecter un long*
à un int*
, ou vice versa, sans conversion explicite (et potentiellement dangereuse).
Et si vous avez besoin de convertir une valeur long
en int
, la première chose à faire est de réexaminer la conception de votre code. Parfois, de telles conversions sont nécessaires, mais elles indiquent le plus souvent que le int
auquel vous affectez aurait dû être défini en tant que long
.
Un long
peut toujours représenter toutes les valeurs de int
. Si la valeur disponible peut être représentée par le type de la variable que vous affectez, la valeur est conservée.
S'il ne peut pas être représenté, le résultat est formellement non spécifié pour le type de destination signé, tandis que pour le type de destination non signé, il est spécifié en tant que valeur d'origine modulo 2n, où n est le nombre de bits dans la représentation de la valeur (qui ne correspond pas nécessairement à tous les bits de la destination).
En pratique, sur les machines modernes, vous obtenez également un emballage pour les types signés.
En effet, les machines modernes utilisent complément à deux pour représenter des entiers signés, sans aucun bit utilisé pour désigner une "valeur non valide" ou autre, c'est-à-dire, tous les bits utilisés pour la représentation des valeurs.
Avec n représentation de la valeur des bits toute valeur entière est x est mappé sur x+ K * 2n avec la constante entière K choisie telle que le résultat se situe dans la plage où la moitié des valeurs possibles sont négatives.
Ainsi, par exemple, avec int
32 bits, la valeur -7 est représentée par le nombre de caractères binaires -7 + 2.32 = 232-7, de sorte que si vous affichez le nombre que bitpattern représente sous la forme d'un entier non signé, vous obtenez un nombre assez grand.
La raison pour laquelle cela s'appelle complément à deux est parce que cela a du sens pour le système à numération binaire, le système à deux chiffres de base. Pour le système de numération binaire, il y a aussi un complément de uns (notez le placement de l'apostrophe). De même, pour le système décimal décimal, il existe un complément de dix et un complément de neuf. Avec une représentation du complément à quatre chiffres à dix, vous représenteriez -7 par 10000-7 = 9993. C'est tout, vraiment.